Membuat aplikasi Jastip/Kantin

 


🛒 APLIKASI KANTIN/JASTIP BERBASIS FLUTTER


1. Identitas


Nama: Nabil Rega Mulyadi

Kelas: XI RPL

Mata Pelajaran: Pemrograman Mobile

Teknologi: Flutter & Dart


2. Judul Projek


KantinJastip — Aplikasi Pemesanan Makanan dan Jasa Titip


3. Pengertian Aplikasi


Aplikasi Kantin/Jastip adalah aplikasi yang dibuat untuk memudahkan pengguna dalam melakukan pemesanan makanan dan minuman dari kantin. Selain menyediakan daftar menu kantin, aplikasi ini juga memiliki fitur jasa titip (jastip) untuk membantu pengguna yang tidak dapat datang langsung ke kantin.


Pengguna dapat melihat menu, mengetahui harga, memilih makanan atau minuman, memasukkannya ke dalam keranjang, kemudian melakukan pemesanan. Pada fitur jastip, pengguna dapat menuliskan makanan yang ingin dibeli dan lokasi pengantaran.


Aplikasi ini dikembangkan menggunakan Flutter dan bahasa pemrograman Dart.


4. Latar Belakang


Kantin merupakan salah satu tempat yang banyak digunakan oleh siswa untuk membeli makanan dan minuman. Namun, pada saat jam istirahat, kantin biasanya ramai sehingga siswa harus mengantre dan membutuhkan waktu untuk mendapatkan makanan.


Oleh karena itu, dibuatlah aplikasi KantinJastip untuk membantu mempermudah proses pemesanan makanan. Dengan adanya aplikasi ini, pengguna dapat melihat menu dan melakukan pemesanan tanpa harus datang langsung ke kantin.


Selain itu, fitur jastip dapat membantu siswa yang sedang berada di kelas atau memiliki kegiatan lain sehingga tidak sempat pergi ke kantin.


5. Tujuan Aplikasi


Tujuan dibuatnya aplikasi KantinJastip adalah:


Mempermudah pengguna dalam memesan makanan dan minuman.


Mengurangi waktu yang digunakan untuk mengantre di kantin.


Menyediakan informasi menu dan harga.


Menyediakan layanan jasa titip makanan.


Menerapkan pembelajaran Flutter dan Dart dalam sebuah projek.


Membuat aplikasi sederhana yang dapat digunakan di lingkungan sekolah.


6. Fitur Aplikasi


🏠 Home


Halaman utama menampilkan informasi aplikasi dan daftar makanan serta minuman yang tersedia di kantin.


🍔 Menu Kantin


Pengguna dapat melihat berbagai menu seperti nasi goreng, ayam geprek, mie goreng, es teh, es jeruk, dan air mineral.


🛒 Keranjang


Menu yang dipilih akan masuk ke dalam keranjang. Pengguna dapat melihat jumlah pesanan dan total harga.


📦 Jastip


Fitur jastip digunakan ketika pengguna ingin menitipkan pembelian kepada orang lain. Pengguna dapat menuliskan pesanan dan lokasi pengantaran.


👤 Profil


Halaman profil menampilkan informasi pengguna.


7. Teknologi yang Digunakan


Aplikasi ini dibuat menggunakan:


Flutter

Framework untuk membuat aplikasi menggunakan bahasa Dart.


Dart

Bahasa pemrograman yang digunakan dalam pengembangan aplikasi Flutter.


DartPad / FlutLab / VS Code

Digunakan sebagai tempat untuk menulis dan menjalankan kode Flutter.


8. Alur Penggunaan Aplikasi


Alur penggunaan aplikasi KantinJastip:


Home → Pilih Menu → Tambahkan ke Keranjang → Periksa Pesanan → Pesan


Sedangkan untuk jastip:


Home → Jastip → Masukkan Pesanan → Masukkan Lokasi → Kirim Pesanan


9. Tampilan Aplikasi


Halaman Home


Pada halaman Home, pengguna dapat melihat banner aplikasi dan daftar makanan serta minuman yang tersedia.


Screenshot:

Masukkan screenshot halaman Home di sini.


Halaman Keranjang


Halaman keranjang menampilkan makanan yang telah dipilih, jumlah pesanan, dan total harga.


Screenshot:

Masukkan screenshot halaman Keranjang di sini.


Halaman Jastip


Halaman Jastip digunakan untuk memasukkan makanan yang ingin dititipkan dan lokasi pengantaran.


Screenshot:

Masukkan screenshot halaman Jastip di sini.


Halaman Profil


Halaman profil berisi informasi pengguna.


Screenshot:

Masukkan screenshot halaman Profil di sini.


10. Source Code


Kode aplikasi dibuat menggunakan Flutter dan Dart. Struktur utama aplikasi menggunakan MaterialApp, Scaffold, NavigationBar, Card, GridView, ListView, dan beberapa widget Flutter lainnya.


Screenshot kode:

Masukkan screenshot kode Flutter di sini.


11. Kelebihan Aplikasi


Beberapa kelebihan aplikasi KantinJastip adalah:


Tampilan sederhana dan mudah digunakan.


Pengguna dapat melihat menu dan harga.


Terdapat fitur keranjang.


Terdapat fitur jasa titip.


Dapat digunakan sebagai dasar pengembangan aplikasi kantin yang lebih lengkap.


Dibuat menggunakan Flutter sehingga dapat dikembangkan untuk berbagai platform.


12. Kekurangan Aplikasi


Aplikasi ini masih merupakan versi sederhana sehingga masih memiliki beberapa kekurangan, seperti:


Belum menggunakan database online.


Belum memiliki sistem login.


Pembayaran masih berupa simulasi.


Belum terhubung dengan sistem kasir kantin.


Belum memiliki fitur pelacakan pesanan secara real-time.


13. Pengembangan Selanjutnya


Aplikasi KantinJastip dapat dikembangkan dengan menambahkan:


🔐 Login dan registrasi pengguna.


💳 Pembayaran digital.


🗄️ Database Firebase.


🔔 Notifikasi pesanan.


📍 Pelacakan pengantaran.


⭐ Rating dan ulasan makanan.


👨‍🍳 Halaman khusus penjual/kantin.


📊 Dashboard admin.


14. Kesimpulan


Aplikasi KantinJastip merupakan aplikasi berbasis Flutter yang dibuat untuk membantu pengguna dalam melakukan pemesanan makanan dan minuman di kantin serta menggunakan layanan jasa titip.


Dengan adanya aplikasi ini, proses pemesanan diharapkan menjadi lebih praktis dan menghemat waktu. Projek ini juga menjadi sarana untuk menerapkan pembelajaran bahasa pemrograman Dart dan framework Flutter dalam pembuatan aplikasi mobile.


KantinJastip — Pesan mudah, tidak perlu antre! 🛒🍔📦


CODE


import 'package:flutter/material.dart';


void main() {

  runApp(const KantinJastipApp());

}


class KantinJastipApp extends StatelessWidget {

  const KantinJastipApp({super.key});


  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      debugShowCheckedModeBanner: false,

      title: 'KantinJastip',

      theme: ThemeData(

        primarySwatch: Colors.orange,

        scaffoldBackgroundColor: const Color(0xFFF7F7F7),

        useMaterial3: true,

      ),

      home: const MainPage(),

    );

  }

}


// ================= DATA MENU =================


class MenuItem {

  final String nama;

  final int harga;

  final String emoji;


  const MenuItem({

    required this.nama,

    required this.harga,

    required this.emoji,

  });

}


const List<MenuItem> daftarMenu = [

  MenuItem(

    nama: 'Nasi Goreng',

    harga: 10000,

    emoji: '🍳',

  ),

  MenuItem(

    nama: 'Ayam Geprek',

    harga: 12000,

    emoji: '🍗',

  ),

  MenuItem(

    nama: 'Mie Goreng',

    harga: 8000,

    emoji: '🍜',

  ),

  MenuItem(

    nama: 'Bakso',

    harga: 10000,

    emoji: '🍲',

  ),

  MenuItem(

    nama: 'Es Teh',

    harga: 4000,

    emoji: '🥤',

  ),

  MenuItem(

    nama: 'Es Jeruk',

    harga: 5000,

    emoji: '🍊',

  ),

  MenuItem(

    nama: 'Air Mineral',

    harga: 3000,

    emoji: '💧',

  ),

  MenuItem(

    nama: 'Kopi Susu',

    harga: 7000,

    emoji: '☕',

  ),

];


// ================= MAIN PAGE =================


class MainPage extends StatefulWidget {

  const MainPage({super.key});


  @override

  State<MainPage> createState() => _MainPageState();

}


class _MainPageState extends State<MainPage> {

  int selectedIndex = 0;


  final Map<MenuItem, int> keranjang = {};


  // ================= FORMAT RUPIAH =================


  String formatRupiah(int harga) {

    String angka = harga.toString();


    return 'Rp ${angka.replaceAllMapped(

      RegExp(r'(\d)(?=(\d{3})+(?!\d))'),

      (match) => '${match[1]}.',

    )}';

  }


  // ================= TAMBAH MENU =================


  void tambahMenu(MenuItem item) {

    setState(() {

      keranjang[item] = (keranjang[item] ?? 0) + 1;

    });


    ScaffoldMessenger.of(context).showSnackBar(

      SnackBar(

        content: Text('${item.nama} ditambahkan ke keranjang'),

        duration: const Duration(seconds: 1),

      ),

    );

  }


  // ================= KURANG MENU =================


  void kurangiMenu(MenuItem item) {

    setState(() {

      if (keranjang[item] != null) {

        if (keranjang[item]! > 1) {

          keranjang[item] = keranjang[item]! - 1;

        } else {

          keranjang.remove(item);

        }

      }

    });

  }


  // ================= TOTAL ITEM =================


  int get totalItem {

    int total = 0;


    for (final jumlah in keranjang.values) {

      total += jumlah;

    }


    return total;

  }


  // ================= TOTAL HARGA =================


  int get totalHarga {

    int total = 0;


    keranjang.forEach((item, jumlah) {

      total += item.harga * jumlah;

    });


    return total;

  }


  @override

  Widget build(BuildContext context) {

    final pages = [

      buildHome(),

      buildKeranjang(),

      buildJastip(),

      buildProfil(),

    ];


    return Scaffold(

      body: pages[selectedIndex],


      bottomNavigationBar: NavigationBar(

        selectedIndex: selectedIndex,

        onDestinationSelected: (index) {

          setState(() {

            selectedIndex = index;

          });

        },

        destinations: [

          const NavigationDestination(

            icon: Icon(Icons.home_outlined),

            selectedIcon: Icon(Icons.home),

            label: 'Home',

          ),


          NavigationDestination(

            icon: Badge(

              isLabelVisible: totalItem > 0,

              label: Text('$totalItem'),

              child: const Icon(Icons.shopping_cart_outlined),

            ),

            selectedIcon: Badge(

              isLabelVisible: totalItem > 0,

              label: Text('$totalItem'),

              child: const Icon(Icons.shopping_cart),

            ),

            label: 'Keranjang',

          ),


          const NavigationDestination(

            icon: Icon(Icons.delivery_dining_outlined),

            selectedIcon: Icon(Icons.delivery_dining),

            label: 'Jastip',

          ),


          const NavigationDestination(

            icon: Icon(Icons.person_outline),

            selectedIcon: Icon(Icons.person),

            label: 'Profil',

          ),

        ],

      ),

    );

  }


  // ==================================================

  // HOME

  // ==================================================


  Widget buildHome() {

    return SafeArea(

      child: SingleChildScrollView(

        padding: const EdgeInsets.all(18),

        child: Column(

          crossAxisAlignment: CrossAxisAlignment.start,

          children: [


            const SizedBox(height: 10),


            const Text(

              'Halo 👋',

              style: TextStyle(

                fontSize: 17,

                color: Colors.grey,

              ),

            ),


            const SizedBox(height: 5),


            const Text(

              'Mau makan apa hari ini?',

              style: TextStyle(

                fontSize: 25,

                fontWeight: FontWeight.bold,

              ),

            ),


            const SizedBox(height: 20),


            // ================= BANNER =================


            Container(

              width: double.infinity,

              padding: const EdgeInsets.all(20),

              decoration: BoxDecoration(

                gradient: const LinearGradient(

                  colors: [

                    Colors.orange,

                    Colors.deepOrange,

                  ],

                ),

                borderRadius: BorderRadius.circular(22),

              ),

              child: Row(

                children: [

                  const Expanded(

                    child: Column(

                      crossAxisAlignment: CrossAxisAlignment.start,

                      children: [

                        Text(

                          'KantinJastip',

                          style: TextStyle(

                            color: Colors.white,

                            fontSize: 25,

                            fontWeight: FontWeight.bold,

                          ),

                        ),


                        SizedBox(height: 8),


                        Text(

                          'Pesan makanan tanpa perlu antre!',

                          style: TextStyle(

                            color: Colors.white,

                            fontSize: 14,

                          ),

                        ),

                      ],

                    ),

                  ),


                  const Text(

                    '🍔',

                    style: TextStyle(fontSize: 55),

                  ),

                ],

              ),

            ),


            const SizedBox(height: 25),


            const Text(

              'Menu Kantin',

              style: TextStyle(

                fontSize: 21,

                fontWeight: FontWeight.bold,

              ),

            ),


            const SizedBox(height: 15),


            // ================= GRID MENU =================


            GridView.builder(

              shrinkWrap: true,

              physics: const NeverScrollableScrollPhysics(),

              itemCount: daftarMenu.length,

              gridDelegate:

                  const SliverGridDelegateWithFixedCrossAxisCount(

                crossAxisCount: 2,

                crossAxisSpacing: 12,

                mainAxisSpacing: 12,

                childAspectRatio: 0.78,

              ),

              itemBuilder: (context, index) {

                final item = daftarMenu[index];


                return Card(

                  elevation: 2,

                  shape: RoundedRectangleBorder(

                    borderRadius: BorderRadius.circular(18),

                  ),

                  child: Padding(

                    padding: const EdgeInsets.all(12),

                    child: Column(

                      mainAxisAlignment: MainAxisAlignment.center,

                      children: [


                        Text(

                          item.emoji,

                          style: const TextStyle(

                            fontSize: 48,

                          ),

                        ),


                        const SizedBox(height: 8),


                        Text(

                          item.nama,

                          textAlign: TextAlign.center,

                          style: const TextStyle(

                            fontSize: 16,

                            fontWeight: FontWeight.bold,

                          ),

                        ),


                        const SizedBox(height: 5),


                        Text(

                          formatRupiah(item.harga),

                          style: const TextStyle(

                            color: Colors.orange,

                            fontWeight: FontWeight.bold,

                          ),

                        ),


                        const SizedBox(height: 10),


                        SizedBox(

                          width: double.infinity,

                          child: ElevatedButton(

                            onPressed: () {

                              tambahMenu(item);

                            },

                            style: ElevatedButton.styleFrom(

                              backgroundColor: Colors.orange,

                              foregroundColor: Colors.white,

                            ),

                            child: const Text('Tambah'),

                          ),

                        ),

                      ],

                    ),

                  ),

                );

              },

            ),

          ],

        ),

      ),

    );

  }


  // ==================================================

  // KERANJANG

  // ==================================================


  Widget buildKeranjang() {

    return SafeArea(

      child: Padding(

        padding: const EdgeInsets.all(18),

        child: Column(

          crossAxisAlignment: CrossAxisAlignment.start,

          children: [


            const Text(

              '🛒 Keranjang',

              style: TextStyle(

                fontSize: 26,

                fontWeight: FontWeight.bold,

              ),

            ),


            const SizedBox(height: 20),


            if (keranjang.isEmpty)

              const Expanded(

                child: Center(

                  child: Column(

                    mainAxisAlignment: MainAxisAlignment.center,

                    children: [


                      Text(

                        '🛒',

                        style: TextStyle(fontSize: 70),

                      ),


                      SizedBox(height: 15),


                      Text(

                        'Keranjang masih kosong',

                        style: TextStyle(

                          fontSize: 18,

                          color: Colors.grey,

                        ),

                      ),

                    ],

                  ),

                ),

              )

            else

              Expanded(

                child: Column(

                  children: [


                    Expanded(

                      child: ListView(

                        children: keranjang.entries.map((entry) {


                          final item = entry.key;

                          final jumlah = entry.value;


                          return Card(

                            margin: const EdgeInsets.only(

                              bottom: 10,

                            ),

                            child: ListTile(


                              leading: Text(

                                item.emoji,

                                style: const TextStyle(

                                  fontSize: 35,

                                ),

                              ),


                              title: Text(

                                item.nama,

                                style: const TextStyle(

                                  fontWeight: FontWeight.bold,

                                ),

                              ),


                              subtitle: Text(

                                '${jumlah} x ${formatRupiah(item.harga)}',

                              ),


                              trailing: Row(

                                mainAxisSize: MainAxisSize.min,

                                children: [


                                  IconButton(

                                    onPressed: () {

                                      kurangiMenu(item);

                                    },

                                    icon: const Icon(

                                      Icons.remove_circle_outline,

                                    ),

                                  ),


                                  Text(

                                    '$jumlah',

                                    style: const TextStyle(

                                      fontWeight: FontWeight.bold,

                                    ),

                                  ),


                                  IconButton(

                                    onPressed: () {

                                      tambahMenu(item);

                                    },

                                    icon: const Icon(

                                      Icons.add_circle_outline,

                                      color: Colors.orange,

                                    ),

                                  ),

                                ],

                              ),

                            ),

                          );

                        }).toList(),

                      ),

                    ),


                    // ================= TOTAL =================


                    Container(

                      padding: const EdgeInsets.all(18),

                      decoration: BoxDecoration(

                        color: Colors.white,

                        borderRadius: BorderRadius.circular(18),

                      ),

                      child: Column(

                        children: [


                          Row(

                            mainAxisAlignment:

                                MainAxisAlignment.spaceBetween,

                            children: [


                              const Text(

                                'Total Pesanan',

                                style: TextStyle(

                                  fontSize: 17,

                                ),

                              ),


                              Text(

                                formatRupiah(totalHarga),

                                style: const TextStyle(

                                  fontSize: 20,

                                  fontWeight: FontWeight.bold,

                                  color: Colors.orange,

                                ),

                              ),

                            ],

                          ),


                          const SizedBox(height: 15),


                          SizedBox(

                            width: double.infinity,

                            child: ElevatedButton(

                              onPressed: pesanSekarang,

                              style: ElevatedButton.styleFrom(

                                backgroundColor: Colors.orange,

                                foregroundColor: Colors.white,

                                padding:

                                    const EdgeInsets.all(15),

                              ),

                              child: const Text(

                                'Pesan Sekarang',

                              ),

                            ),

                          ),

                        ],

                      ),

                    ),

                  ],

                ),

              ),

          ],

        ),

      ),

    );

  }


  // ==================================================

  // PESAN SEKARANG

  // ==================================================


  void pesanSekarang() {


    showDialog(

      context: context,

      builder: (context) {


        return AlertDialog(

          title: const Text(

            'Pesanan Berhasil 🎉',

          ),


          content: Text(

            'Total pesanan kamu '

            '${formatRupiah(totalHarga)}.\n\n'

            'Pesanan sedang diproses.',

          ),


          actions: [


            TextButton(

              onPressed: () {


                Navigator.pop(context);


                setState(() {

                  keranjang.clear();

                });

              },

              child: const Text('OK'),

            ),

          ],

        );

      },

    );

  }


  // ==================================================

  // JASTIP

  // ==================================================


  Widget buildJastip() {


    final pesananController = TextEditingController();

    final lokasiController = TextEditingController();


    return SafeArea(

      child: SingleChildScrollView(

        padding: const EdgeInsets.all(18),

        child: Column(

          crossAxisAlignment: CrossAxisAlignment.start,

          children: [


            const Text(

              '📦 Jasa Titip',

              style: TextStyle(

                fontSize: 26,

                fontWeight: FontWeight.bold,

              ),

            ),


            const SizedBox(height: 8),


            const Text(

              'Tidak sempat ke kantin? Titip saja!',

              style: TextStyle(

                color: Colors.grey,

              ),

            ),


            const SizedBox(height: 25),


            Card(

              elevation: 2,

              shape: RoundedRectangleBorder(

                borderRadius: BorderRadius.circular(20),

              ),

              child: Padding(

                padding: const EdgeInsets.all(20),

                child: Column(

                  children: [


                    const Text(

                      '🚶',

                      style: TextStyle(

                        fontSize: 65,

                      ),

                    ),


                    const SizedBox(height: 10),


                    const Text(

                      'Pesan Jastip',

                      style: TextStyle(

                        fontSize: 22,

                        fontWeight: FontWeight.bold,

                      ),

                    ),


                    const SizedBox(height: 8),


                    const Text(

                      'Tuliskan makanan yang ingin '

                      'kamu titipkan.',

                      textAlign: TextAlign.center,

                    ),


                    const SizedBox(height: 20),


                    TextField(

                      controller: pesananController,

                      decoration: InputDecoration(

                        labelText: 'Pesanan',

                        hintText: 'Contoh: Nasi goreng 1',

                        prefixIcon: const Icon(

                          Icons.fastfood,

                        ),

                        border: OutlineInputBorder(

                          borderRadius:

                              BorderRadius.circular(12),

                        ),

                      ),

                    ),


                    const SizedBox(height: 15),


                    TextField(

                      controller: lokasiController,

                      decoration: InputDecoration(

                        labelText: 'Lokasi Pengantaran',

                        hintText: 'Contoh: Kelas XI



Komentar