Aplikasi Kantin Digital Berbasis Flutter

Aplikasi Kantin Digital Berbasis Flutter



nama: akmal ghani

kelas :xii rpl 1

Deskripsi Aplikasi

Aplikasi Kantin Digital merupakan aplikasi mobile yang dikembangkan menggunakan Dart dan Flutter untuk memudahkan proses pemesanan makanan dan minuman di kantin sekolah. Aplikasi ini dirancang agar peserta didik dapat melihat daftar menu, melakukan pemesanan, serta melihat total harga secara digital tanpa harus mengantri secara langsung di kantin.

Dengan antarmuka yang sederhana dan responsif, aplikasi ini dapat dijalankan pada perangkat Android dan iOS, sehingga mendukung kemudahan akses bagi pengguna. 


Tujuan Pembuatan Aplikasi

Tujuan dari pembuatan aplikasi Kantin Digital ini adalah:

  1. Mempermudah siswa dalam memesan makanan dan minuman di kantin.

  2. Mengurangi antrian dan kerumunan di area kantin.

  3. Meningkatkan efisiensi pelayanan kantin.

  4. Menerapkan pembelajaran pemrograman Dart dan Flutter dalam proyek nyata.

  5. Melatih kemampuan pengembangan aplikasi mobile berbasis UI dan logika program.




Fitur-Fitur Aplikasi

Aplikasi Kantin Digital memiliki beberapa fitur utama, antara lain:

  • Halaman Daftar Menu
    Menampilkan daftar makanan dan minuman lengkap dengan harga.

  • Detail Menu
    Menampilkan informasi detail menu dan jumlah pesanan (quantity).

  • Keranjang Pesanan
    Menyimpan menu yang dipilih dan menampilkan total harga.

  • Checkout Pesanan
    Menampilkan ringkasan pesanan sebelum dikonfirmasi.

  • Antarmuka Responsif
    Tampilan aplikasi menyesuaikan ukuran layar perangkat.

  • Navigasi Antar Halaman
    Menggunakan sistem routing Flutter untuk berpindah halaman.





Dokumentasi Tampilan Aplikasi


                  https://zvfm06p8wfn0.zapp.page/#/


Potongan Kode Penting


import 'package:flutter/material.dart';
import 'dart:html' as html;

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Kantin Sekolah RASA NIKMAT",
      theme: ThemeData(
        primarySwatch: Colors.deepOrange,
        fontFamily: 'Poppins',
        appBarTheme: const AppBarTheme(
          backgroundColor: Color(0xFFE65100),
          elevation: 4,
          centerTitle: true,
        ),
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.deepOrange,
          brightness: Brightness.light,
        ),
      ),
      home: const HomePage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});
  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final List<Map<String, dynamic>> _allProducts = [
    {
      "id": 1,
      "name": "Mie Ayam Bakso",
      "price": 15000,
      "qty": 0,
      "image": "https://images.unsplash.com/photo-1586190848861-99aa4a171e90?q=80&w=400&auto=format&fit=crop",
      "category": "Makanan Berat",
      "rating": 4.8,
      "description": "Mie ayam dengan bakso sapi pilihan",
    },
    {
      "id": 2,
      "name": "Nasi Goreng Special",
      "price": 12000,
      "qty": 0,
      "image": "https://images.unsplash.com/photo-1633945274417-b8c0b5dada04?q=80&w=400&auto=format&fit=crop",
      "category": "Makanan Berat",
      "rating": 4.7,
      "description": "Nasi goreng dengan telur dan ayam",
    },
    {
      "id": 3,
      "name": "Batagor",
      "price": 8000,
      "qty": 0,
      "image": "https://images.unsplash.com/photo-1563379091339-03246963d9d6?q=80&w=400&auto=format&fit=crop",
      "category": "Snack",
      "rating": 4.5,
      "description": "Bakso tahu goreng dengan bumbu kacang",
    },
    {
      "id": 4,
      "name": "Es Teh Manis",
      "price": 3000,
      "qty": 0,
      "image": "https://images.unsplash.com/photo-1568649929103-28ffbefaca1e?q=80&w=400&auto=format&fit=crop",
      "category": "Minuman",
      "rating": 4.3,
      "description": "Es teh dengan gula pasir",
    },
    {
      "id": 5,
      "name": "Sate Ayam",
      "price": 10000,
      "qty": 0,
      "image": "https://images.unsplash.com/photo-1594488506255-a8c8b4d8dc5e?q=80&w=400&auto=format&fit=crop",
      "category": "Makanan Berat",
      "rating": 4.6,
      "description": "10 tusuk sate ayam dengan bumbu kacang",
    },
    {
      "id": 6,
      "name": "Es Jeruk",
      "price": 5000,
      "qty": 0,
      "image": "https://images.unsplash.com/photo-1621506289937-a8e4df240d0b?q=80&w=400&auto=format&fit=crop",
      "category": "Minuman",
      "rating": 4.4,
      "description": "Es jeruk segar",
    },
    {
      "id": 7,
      "name": "Roti Bakar",
      "price": 7000,
      "qty": 0,
      "image": "https://images.unsplash.com/photo-1488477181946-6428a0291777?q=80&w=400&auto=format&fit=crop",
      "category": "Snack",
      "rating": 4.5,
      "description": "Roti bakar coklat keju",
    },
    {
      "id": 8,
      "name": "Air Mineral",
      "price": 2000,
      "qty": 0,
      "image": "https://images.unsplash.com/photo-1523362628745-0c100150b504?q=80&w=400&auto=format&fit=crop",
      "category": "Minuman",
      "rating": 4.0,
      "description": "Air mineral kemasan 600ml",
    },
    {
      "id": 9,
      "name": "Ayam Geprek",
      "price": 14000,
      "qty": 0,
      "image": "https://images.unsplash.com/photo-1562967914-608f82629710?q=80&w=400&auto=format&fit=crop",
      "category": "Makanan Berat",
      "rating": 4.9,
      "description": "Ayam geprek sambal bawang",
    },
    {
      "id": 10,
      "name": "Es Campur",
      "price": 8000,
      "qty": 0,
      "image": "https://images.unsplash.com/photo-1572490122747-3968b75cc699?q=80&w=400&auto=format&fit=crop",
      "category": "Minuman",
      "rating": 4.6,
      "description": "Es campur dengan buah segar",
    },
  ];

  List<Map<String, dynamic>> _foundProducts = [];
  final TextEditingController _searchController = TextEditingController();
  int _selectedCategory = 0;

  @override
  void initState() {
    super.initState();
    _foundProducts = List.from(_allProducts);
  }

  void _filterProducts(String query) {
    List<Map<String, dynamic>> results = [];

    if (query.isEmpty) {
      results = List.from(_allProducts);
    } else {
      results = _allProducts
          .where((item) =>
              (item['name'] as String).toLowerCase().contains(query.toLowerCase()) ||
              (item['category'] as String).toLowerCase().contains(query.toLowerCase()))
          .toList();
    }

    if (_selectedCategory > 0) {
      String category = _getCategoryFromIndex(_selectedCategory);
      results = results.where((item) => item['category'] == category).toList();
    }

    setState(() {
      _foundProducts = results;
    });
  }

  void _filterByCategory(int index) {
    setState(() {
      _selectedCategory = index;
    });

    if (index == 0) {
      _filterProducts(_searchController.text);
    } else {
      String category = _getCategoryFromIndex(index);
      setState(() {
        _foundProducts = _allProducts
            .where((item) => item['category'] == category)
            .where((item) =>
                _searchController.text.isEmpty ||
                (item['name'] as String).toLowerCase().contains(_searchController.text.toLowerCase()))
            .toList();
      });
    }
  }

  String _getCategoryFromIndex(int index) {
    switch (index) {
      case 1:
        return 'Makanan Berat';
      case 2:
        return 'Snack';
      case 3:
        return 'Minuman';
      default:
        return '';
    }
  }

  @override
  Widget build(BuildContext context) {
    int totalItems = _allProducts.fold<int>(0, (int sum, item) => sum + (item['qty'] as int));
    int totalPrice = _allProducts.fold<int>(0, (int sum, item) => sum + (item['price'] as int) * (item['qty'] as int));

    return Scaffold(
      appBar: AppBar(
        title: Column(
          children: [
            Text(
              "KANTIN RASA NIKMAT",
              style: TextStyle(
                fontSize: 20,
                fontWeight: FontWeight.bold,
                color: Colors.white,
              ),
            ),
            Text(
              "Makanan Hangat & Lezat",
              style: TextStyle(
                fontSize: 12,
                color: Colors.white70,
              ),
            ),
          ],
        ),
        centerTitle: true,
        elevation: 0,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.vertical(
            bottom: Radius.circular(30),
          ),
        ),
      ),
      
      body: Column(
        children: [
          // Search Bar
          Padding(
            padding: EdgeInsets.all(16.0),
            child: Container(
              decoration: BoxDecoration(
                color: Colors.white,
                borderRadius: BorderRadius.circular(15),
                boxShadow: [
                  BoxShadow(
                    color: Colors.deepOrange.withOpacity(0.1),
                    blurRadius: 10,
                    spreadRadius: 1,
                  ),
                ],
              ),
              child: TextField(
                controller: _searchController,
                onChanged: _filterProducts,
                decoration: InputDecoration(
                  hintText: "Cari makanan atau minuman...",
                  prefixIcon: Icon(Icons.search, color: Colors.deepOrange),
                  suffixIcon: _searchController.text.isNotEmpty
                      ? IconButton(
                          icon: Icon(Icons.clear, color: Colors.deepOrange),
                          onPressed: () {
                            _searchController.clear();
                            _filterProducts('');
                          },
                        )
                      : null,
                  border: InputBorder.none,
                  contentPadding: EdgeInsets.symmetric(horizontal: 20, vertical: 15),
                ),
              ),
            ),
          ),

          // Category Filter
          SizedBox(
            height: 50,
            child: ListView(
              scrollDirection: Axis.horizontal,
              padding: EdgeInsets.symmetric(horizontal: 16),
              children: [
                _buildCategoryChip("Semua", 0),
                SizedBox(width: 8),
                _buildCategoryChip("Makanan Berat", 1),
                SizedBox(width: 8),
                _buildCategoryChip("Snack", 2),
                SizedBox(width: 8),
                _buildCategoryChip("Minuman", 3),
              ],
            ),
          ),

          SizedBox(height: 16),

          // Cart Summary
          Padding(
            padding: EdgeInsets.symmetric(horizontal: 16.0),
            child: Container(
              padding: EdgeInsets.all(16),
              decoration: BoxDecoration(
                gradient: LinearGradient(
                  colors: [Colors.deepOrange[400]!, Colors.deepOrange[600]!],
                  begin: Alignment.centerLeft,
                  end: Alignment.centerRight,
                ),
                borderRadius: BorderRadius.circular(15),
                boxShadow: [
                  BoxShadow(
                    color: Colors.deepOrange.withOpacity(0.3),
                    blurRadius: 10,
                    spreadRadius: 2,
                  ),
                ],
              ),
              child: Row(
                children: [
                  Container(
                    padding: EdgeInsets.all(10),
                    decoration: BoxDecoration(
                      color: Colors.white,
                      borderRadius: BorderRadius.circular(12),
                    ),
                    child: Icon(Icons.shopping_basket, color: Colors.deepOrange, size: 24),
                  ),
                  SizedBox(width: 16),
                  Expanded(
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(
                          "Pesanan Anda",
                          style: TextStyle(
                            color: Colors.white.withOpacity(0.9),
                            fontSize: 12,
                          ),
                        ),
                        Text(
                          "$totalItems item",
                          style: TextStyle(
                            color: Colors.white,
                            fontSize: 18,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                      ],
                    ),
                  ),
                  Column(
                    crossAxisAlignment: CrossAxisAlignment.end,
                    children: [
                      Text(
                        "Total",
                        style: TextStyle(
                          color: Colors.white.withOpacity(0.9),
                          fontSize: 12,
                        ),
                      ),
                      Text(
                        "Rp ${totalPrice.toString().replaceAllMapped(
                          RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
                          (Match m) => '${m[1]}.',
                        )}",
                        style: TextStyle(
                          color: Colors.white,
                          fontSize: 16,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ),

          SizedBox(height: 16),

          // Products Grid
          Expanded(
            child: Padding(
              padding: EdgeInsets.symmetric(horizontal: 12.0),
              child: GridView.builder(
                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 2,
                  crossAxisSpacing: 12,
                  mainAxisSpacing: 12,
                  childAspectRatio: 0.75,
                ),
                itemCount: _foundProducts.length,
                itemBuilder: (context, index) {
                  var item = _foundProducts[index];
                  return _buildProductCard(item);
                },
              ),
            ),
          ),
        ],
      ),

      floatingActionButton: FloatingActionButton.extended(
        onPressed: () {
          List<Map<String, dynamic>> cart = _allProducts.where((e) => (e['qty'] as int) > 0).toList();
          if (cart.isEmpty) {
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(
                content: Text("Belum ada makanan dipilih!"),
                backgroundColor: Colors.deepOrange[800],
                behavior: SnackBarBehavior.floating,
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(10),
                ),
              ),
            );
            return;
          }

          Navigator.push(
            context,
            MaterialPageRoute(builder: (_) => CheckoutPage(cart)),
          );
        },
        label: Text(
          "Pesan Sekarang",
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
        icon: Icon(Icons.restaurant_menu),
        backgroundColor: Color(0xFFE65100),
        elevation: 4,
      ),
    );
  }

  Widget _buildCategoryChip(String text, int index) {
    bool isSelected = _selectedCategory == index;
    return GestureDetector(
      onTap: () => _filterByCategory(index),
      child: Container(
        padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
        decoration: BoxDecoration(
          color: isSelected ? Colors.deepOrange : Colors.grey[200],
          borderRadius: BorderRadius.circular(25),
          border: Border.all(
            color: isSelected ? Colors.deepOrange : Colors.transparent,
            width: 1.5,
          ),
        ),
        child: Text(
          text,
          style: TextStyle(
            color: isSelected ? Colors.white : Colors.grey[700],
            fontWeight: FontWeight.w600,
            fontSize: 13,
          ),
        ),
      ),
    );
  }

  Widget _buildProductCard(Map<String, dynamic> item) {
    return Container(
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(20),
        boxShadow: [
          BoxShadow(
            color: Colors.grey.withOpacity(0.2),
            blurRadius: 10,
            spreadRadius: 2,
            offset: Offset(0, 4),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          // Product Image
          ClipRRect(
            borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
            child: Container(
              height: 110,
              decoration: BoxDecoration(
                image: DecorationImage(
                  image: NetworkImage(item['image'] as String),
                  fit: BoxFit.cover,
                ),
              ),
              child: Stack(
                children: [
                  // Category Badge
                  Positioned(
                    top: 8,
                    left: 8,
                    child: Container(
                      padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                      decoration: BoxDecoration(
                        color: _getCategoryColor(item['category'] as String).withOpacity(0.9),
                        borderRadius: BorderRadius.circular(10),
                      ),
                      child: Text(
                        item['category'] as String,
                        style: TextStyle(
                          fontSize: 10,
                          fontWeight: FontWeight.bold,
                          color: _getCategoryTextColor(item['category'] as String),
                        ),
                      ),
                    ),
                  ),
                  // Rating Badge
                  Positioned(
                    top: 8,
                    right: 8,
                    child: Container(
                      padding: EdgeInsets.symmetric(horizontal: 6, vertical: 3),
                      decoration: BoxDecoration(
                        color: Colors.black.withOpacity(0.5),
                        borderRadius: BorderRadius.circular(10),
                      ),
                      child: Row(
                        mainAxisSize: MainAxisSize.min,
                        children: [
                          Icon(Icons.star, color: Colors.amber, size: 12),
                          SizedBox(width: 2),
                          Text(
                            "${item['rating']}",
                            style: TextStyle(
                              color: Colors.white,
                              fontSize: 10,
                              fontWeight: FontWeight.bold,
                            ),
                          ),
                        ],
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),

          // Product Info
          Padding(
            padding: EdgeInsets.all(12),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  item['name'] as String,
                  style: TextStyle(
                    fontSize: 14,
                    fontWeight: FontWeight.bold,
                    color: Color(0xFF333333),
                  ),
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                ),
                SizedBox(height: 4),
                Text(
                  item['description'] as String,
                  style: TextStyle(
                    fontSize: 11,
                    color: Colors.grey[600],
                  ),
                  maxLines: 2,
                  overflow: TextOverflow.ellipsis,
                ),
                SizedBox(height: 8),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Text(
                      "Rp ${(item['price'] as int).toString().replaceAllMapped(
                        RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
                        (Match m) => '${m[1]}.',
                      )}",
                      style: TextStyle(
                        fontSize: 14,
                        fontWeight: FontWeight.bold,
                        color: Color(0xFFE65100),
                      ),
                    ),
                    if ((item['qty'] as int) > 0)
                      Container(
                        padding: EdgeInsets.symmetric(horizontal: 6),
                        decoration: BoxDecoration(
                          color: Colors.deepOrange[100],
                          borderRadius: BorderRadius.circular(10),
                        ),
                        child: Text(
                          "${item['qty']}",
                          style: TextStyle(
                            color: Colors.deepOrange,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                      ),
                  ],
                ),
                SizedBox(height: 8),

                // Quantity Controls
                Container(
                  decoration: BoxDecoration(
                    color: Colors.grey[50],
                    borderRadius: BorderRadius.circular(15),
                  ),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Expanded(
                        child: TextButton(
                          onPressed: () {
                            setState(() {
                              if ((item['qty'] as int) > 0) {
                                item['qty'] = (item['qty'] as int) - 1;
                              }
                            });
                          },
                          style: TextButton.styleFrom(
                            padding: EdgeInsets.symmetric(vertical: 8),
                            shape: RoundedRectangleBorder(
                              borderRadius: BorderRadius.horizontal(
                                left: Radius.circular(15),
                              ),
                            ),
                          ),
                          child: Icon(
                            Icons.remove,
                            color: Colors.grey,
                            size: 18,
                          ),
                        ),
                      ),
                      Padding(
                        padding: EdgeInsets.symmetric(horizontal: 8),
                        child: Text(
                          "${item['qty']}",
                          style: TextStyle(
                            fontWeight: FontWeight.bold,
                            color: Colors.deepOrange,
                          ),
                        ),
                      ),
                      Expanded(
                        child: TextButton(
                          onPressed: () {
                            setState(() {
                              item['qty'] = (item['qty'] as int) + 1;
                            });
                          },
                          style: TextButton.styleFrom(
                            padding: EdgeInsets.symmetric(vertical: 8),
                            shape: RoundedRectangleBorder(
                              borderRadius: BorderRadius.horizontal(
                                right: Radius.circular(15),
                              ),
                            ),
                          ),
                          child: Icon(
                            Icons.add,
                            color: Colors.deepOrange,
                            size: 18,
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  Color _getCategoryColor(String category) {
    switch (category) {
      case 'Makanan Berat':
        return Color(0xFFFFF3E0);
      case 'Snack':
        return Color(0xFFE8F5E9);
      case 'Minuman':
        return Color(0xFFE3F2FD);
      default:
        return Color(0xFFF5F5F5);
    }
  }

  Color _getCategoryTextColor(String category) {
    switch (category) {
      case 'Makanan Berat':
        return Color(0xFFE65100);
      case 'Snack':
        return Color(0xFF2E7D32);
      case 'Minuman':
        return Color(0xFF1565C0);
      default:
        return Colors.grey;
    }
  }
}

class CheckoutPage extends StatefulWidget {
  final List<Map<String, dynamic>> cart;
  const CheckoutPage(this.cart, {super.key});

  @override
  State<CheckoutPage> createState() => _CheckoutPageState();
}

class _CheckoutPageState extends State<CheckoutPage> {
  final TextEditingController _namaController = TextEditingController();
  final TextEditingController _kelasController = TextEditingController();
  final TextEditingController _nomorController = TextEditingController();
  
  @override
  void initState() {
    super.initState();
    _nomorController.text = "6282263088566";
  }
  
  @override
  void dispose() {
    _namaController.dispose();
    _kelasController.dispose();
    _nomorController.dispose();
    super.dispose();
  }
  
  int get total {
    return widget.cart.fold<int>(0, (int sum, item) {
      return sum + (item['price'] as int) * (item['qty'] as int);
    });
  }
  
  int get totalItems {
    return widget.cart.fold<int>(0, (int sum, item) => sum + (item['qty'] as int));
  }
  
  void _resetForm() {
    setState(() {
      _namaController.clear();
      _kelasController.clear();
      _nomorController.text = "6282263088566";
    });
  }
  
  void _kirimWhatsApp() {
    if (_namaController.text.isEmpty) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text("Harap masukkan nama pemesan!"),
          backgroundColor: Colors.red,
        ),
      );
      return;
    }
    
    if (_kelasController.text.isEmpty) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text("Harap masukkan kelas!"),
          backgroundColor: Colors.red,
        ),
      );
      return;
    }
    
    if (_nomorController.text.isEmpty) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text("Harap masukkan nomor WhatsApp!"),
          backgroundColor: Colors.red,
        ),
      );
      return;
    }
    
    // Format pesan WhatsApp
    String message = """
*PESANAN KANTIN SEKOLAH RASA NIKMAT*

Nama: ${_namaController.text}
Kelas: ${_kelasController.text}
Waktu: ${DateTime.now().hour}:${DateTime.now().minute.toString().padLeft(2, '0')}

────────────────────
*DETAIL PESANAN:*
""";

    for (var item in widget.cart) {
      message += """
• ${item['name']}
  ${item['qty']} x Rp ${(item['price'] as int).toString().replaceAllMapped(
        RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
        (Match m) => '${m[1]}.',
      )} = Rp ${((item['price'] as int) * (item['qty'] as int)).toString().replaceAllMapped(
        RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
        (Match m) => '${m[1]}.',
      )}
""";
    }

    message += """
────────────────────
*TOTAL: Rp ${total.toString().replaceAllMapped(
      RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
      (Match m) => '${m[1]}.',
    )}*

_Catatan: Pesanan akan diantar ke kelas ${_kelasController.text} pada jam istirahat berikutnya._

Terima kasih!
Kantin Rasa Nikmat 🍽️
""";

    String phone = _nomorController.text;
    String url = "https://wa.me/$phone?text=${Uri.encodeComponent(message)}";

    html.window.open(url, "_blank");

    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          mainAxisSize: MainAxisSize.min,
          children: [
            Text(
              "✅ Pesanan Dikirim ke WhatsApp",
              style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
            ),
            SizedBox(height: 8),
            Text(
              "Nama: ${_namaController.text}",
              style: TextStyle(fontSize: 14),
            ),
            Text(
              "Kelas: ${_kelasController.text}",
              style: TextStyle(fontSize: 14),
            ),
            Text(
              "Total: Rp ${total.toString().replaceAllMapped(
                RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
                (Match m) => '${m[1]}.',
              )}",
              style: TextStyle(
                fontSize: 14,
                fontWeight: FontWeight.bold,
                color: Colors.green,
              ),
            ),
          ],
        ),
        backgroundColor: Colors.green[700],
        behavior: SnackBarBehavior.floating,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(10),
        ),
        duration: Duration(seconds: 5),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Checkout - Kantin Rasa Nikmat"),
        backgroundColor: Color(0xFFE65100),
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.vertical(
            bottom: Radius.circular(30),
          ),
        ),
      ),

      body: SingleChildScrollView(
        padding: EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // Header
            Center(
              child: Column(
                children: [
                  Container(
                    width: 100,
                    height: 100,
                    decoration: BoxDecoration(
                      color: Colors.deepOrange[100],
                      shape: BoxShape.circle,
                      border: Border.all(color: Colors.deepOrange, width: 3),
                      boxShadow: [
                        BoxShadow(
                          color: Colors.deepOrange.withOpacity(0.3),
                          blurRadius: 10,
                          spreadRadius: 2,
                        ),
                      ],
                    ),
                    child: Icon(
                      Icons.restaurant,
                      size: 50,
                      color: Color(0xFFE65100),
                    ),
                  ),
                  SizedBox(height: 16),
                  Text(
                    "Selesaikan Pesanan Anda",
                    style: TextStyle(
                      fontSize: 24,
                      fontWeight: FontWeight.bold,
                      color: Color(0xFFE65100),
                    ),
                  ),
                  SizedBox(height: 8),
                  Text(
                    "Isi data diri untuk pengiriman pesanan",
                    textAlign: TextAlign.center,
                    style: TextStyle(
                      fontSize: 14,
                      color: Colors.grey[700],
                    ),
                  ),
                ],
              ),
            ),

            SizedBox(height: 24),

            // Form Input
            Column(
              children: [
                _buildInputField(
                  controller: _namaController,
                  label: "Nama Lengkap",
                  icon: Icons.person,
                  hint: "Masukkan nama lengkap",
                ),
                SizedBox(height: 16),
                _buildInputField(
                  controller: _kelasController,
                  label: "Kelas",
                  icon: Icons.school,
                  hint: "Contoh: XII IPA 1",
                ),
                SizedBox(height: 16),
                _buildInputField(
                  controller: _nomorController,
                  label: "Nomor WhatsApp",
                  icon: Icons.phone,
                  hint: "6282263088566",
                  isPhone: true,
                ),
              ],
            ),

            SizedBox(height: 24),

            // Order Summary
            Container(
              padding: EdgeInsets.all(20),
              decoration: BoxDecoration(
                color: Colors.deepOrange[50],
                borderRadius: BorderRadius.circular(20),
                border: Border.all(color: Colors.deepOrange[100]!),
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Row(
                    children: [
                      Icon(Icons.receipt, color: Colors.deepOrange),
                      SizedBox(width: 8),
                      Text(
                        "Ringkasan Pesanan",
                        style: TextStyle(
                          fontSize: 18,
                          fontWeight: FontWeight.bold,
                          color: Colors.deepOrange,
                        ),
                      ),
                    ],
                  ),
                  SizedBox(height: 16),
                  ...widget.cart.map((item) => Padding(
                        padding: EdgeInsets.only(bottom: 12),
                        child: Row(
                          children: [
                            Container(
                              width: 40,
                              height: 40,
                              decoration: BoxDecoration(
                                borderRadius: BorderRadius.circular(8),
                                image: DecorationImage(
                                  image: NetworkImage(item['image'] as String),
                                  fit: BoxFit.cover,
                                ),
                              ),
                            ),
                            SizedBox(width: 12),
                            Expanded(
                              child: Column(
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: [
                                  Text(
                                    item['name'] as String,
                                    style: TextStyle(
                                      fontWeight: FontWeight.w600,
                                    ),
                                  ),
                                  Text(
                                    "${item['qty']} x Rp ${(item['price'] as int).toString().replaceAllMapped(
                                      RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
                                      (Match m) => '${m[1]}.',
                                    )}",
                                    style: TextStyle(
                                      fontSize: 12,
                                      color: Colors.grey[600],
                                    ),
                                  ),
                                ],
                              ),
                            ),
                            Text(
                              "Rp ${((item['price'] as int) * (item['qty'] as int)).toString().replaceAllMapped(
                                RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
                                (Match m) => '${m[1]}.',
                              )}",
                              style: TextStyle(
                                fontWeight: FontWeight.bold,
                                color: Colors.deepOrange,
                              ),
                            ),
                          ],
                        ),
                      )),
                  Divider(height: 30),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      Text(
                        "Total Item:",
                        style: TextStyle(fontSize: 16, color: Colors.grey),
                      ),
                      Text(
                        "$totalItems item",
                        style: TextStyle(
                          fontSize: 16,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ],
                  ),
                  SizedBox(height: 8),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      Text(
                        "Total Pembayaran:",
                        style: TextStyle(
                          fontSize: 18,
                          fontWeight: FontWeight.bold,
                          color: Colors.deepOrange,
                        ),
                      ),
                      Text(
                        "Rp ${total.toString().replaceAllMapped(
                          RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
                          (Match m) => '${m[1]}.',
                        )}",
                        style: TextStyle(
                          fontSize: 20,
                          fontWeight: FontWeight.bold,
                          color: Colors.deepOrange,
                        ),
                      ),
                    ],
                  ),
                ],
              ),
            ),

            SizedBox(height: 24),

            // Information Card
            Container(
              padding: EdgeInsets.all(16),
              decoration: BoxDecoration(
                color: Colors.amber[50],
                borderRadius: BorderRadius.circular(15),
                border: Border.all(color: Colors.amber[100]!),
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Row(
                    children: [
                      Icon(Icons.info_outline, color: Colors.amber, size: 20),
                      SizedBox(width: 8),
                      Text(
                        "Informasi Penting",
                        style: TextStyle(
                          fontWeight: FontWeight.bold,
                          color: Colors.amber,
                        ),
                      ),
                    ],
                  ),
                  SizedBox(height: 8),
                  Text(
                    "• Pesanan diantar saat jam istirahat\n"
                    "• Pembayaran saat barang diterima\n"
                    "• Hubungi kantin jika ada perubahan\n"
                    "• Jam operasional: 07.00 - 15.00 WIB",
                    style: TextStyle(fontSize: 13, height: 1.5),
                  ),
                ],
              ),
            ),

            SizedBox(height: 24),

            // Action Buttons
            Row(
              children: [
                Expanded(
                  child: OutlinedButton(
                    style: OutlinedButton.styleFrom(
                      padding: EdgeInsets.symmetric(vertical: 16),
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(12),
                      ),
                      side: BorderSide(color: Colors.grey),
                    ),
                    onPressed: () {
                      Navigator.pop(context);
                    },
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Icon(Icons.arrow_back, size: 18),
                        SizedBox(width: 8),
                        Text("Kembali"),
                      ],
                    ),
                  ),
                ),
                SizedBox(width: 12),
                Expanded(
                  child: ElevatedButton(
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Color(0xFF25D366),
                      foregroundColor: Colors.white,
                      padding: EdgeInsets.symmetric(vertical: 16),
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(12),
                      ),
                      elevation: 3,
                    ),
                    onPressed: _kirimWhatsApp,
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Icon(Icons.phone, size: 20),
                        SizedBox(width: 8),
                        Text(
                          "Kirim via WhatsApp",
                          style: TextStyle(fontWeight: FontWeight.bold),
                        ),
                      ],
                    ),
                  ),
                ),
              ],
            ),

            SizedBox(height: 40),
          ],
        ),
      ),
    );
  }

  Widget _buildInputField({
    required TextEditingController controller,
    required String label,
    required IconData icon,
    required String hint,
    bool isPhone = false,
  }) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          label,
          style: TextStyle(
            fontWeight: FontWeight.w600,
            color: Colors.deepOrange,
          ),
        ),
        SizedBox(height: 8),
        Container(
          decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.circular(12),
            boxShadow: [
              BoxShadow(
                color: Colors.grey.withOpacity(0.1),
                blurRadius: 5,
                spreadRadius: 1,
              ),
            ],
          ),
          child: TextField(
            controller: controller,
            keyboardType: isPhone ? TextInputType.phone : TextInputType.text,
            decoration: InputDecoration(
              hintText: hint,
              border: InputBorder.none,
              prefixIcon: Icon(icon, color: Colors.deepOrange),
              contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 16),
            ),
          ),
        ),
      ],
    );
  }
}







Komentar

Postingan populer dari blog ini

Membuat Aplikasi Jastip di Lingkungan Sekolah Menggunakan Flutter dan Integrasi WhatsApp API