Kamis, 11 Desember 2025

WARUNG GORENGAN - SENDI

🍽️ Warung Gorengan

Aplikasi Jastip Gorengan Kantin - Solusi Modern untuk Pesan Makanan Favorit Anda

Flutter Mobile App

Deskripsi Aplikasi

Warung Gorengan adalah aplikasi mobile inovatif berbasis Flutter yang dirancang khusus untuk memudahkan siswa, guru, dan staf dalam memesan gorengan favorit dari kantin sekolah. Aplikasi ini menghadirkan pengalaman pemesanan yang praktis, cepat, dan efisien dengan integrasi langsung ke WhatsApp untuk komunikasi real-time dengan penjual.

Dengan antarmuka yang modern dan user-friendly, aplikasi ini memungkinkan pengguna untuk memilih menu, menambahkan catatan khusus, dan melakukan pemesanan hanya dalam beberapa tap. Tidak perlu lagi mengantri panjang atau khawatir kehabisan gorengan favorit!

Tujuan Pembuatan

Hemat Waktu
Praktis & Mudah
Jastip Efisien
Meningkatkan Layanan

Aplikasi ini dibuat dengan tujuan untuk mendigitalisasi proses pemesanan makanan di lingkungan sekolah, mengurangi antrian di kantin, dan memberikan kemudahan bagi siswa yang ingin fokus pada kegiatan belajar tanpa harus menghabiskan waktu lama untuk membeli makanan.

Fitur-Fitur Unggulan

Menu Lengkap

Pilihan menu gorengan lengkap dengan gambar, nama, dan harga yang jelas. Termasuk Tempe Goreng, Gehu, Bala-bala, Kroket, Lontong, dan Cilok.

Keranjang Belanja

Sistem keranjang yang interaktif dengan kontrol jumlah pesanan, penambahan catatan khusus, dan kalkulasi harga otomatis.

Integrasi WhatsApp

Kirim pesanan langsung ke WhatsApp penjual dengan format yang rapi dan profesional, lengkap dengan detail pemesan.

Informasi Pemesan

Form lengkap untuk identitas pemesan (Status, Nama, Ruangan) agar penjual mudah mengidentifikasi dan mengantar pesanan.

Catatan Pesanan

Tambahkan catatan khusus untuk setiap item (misal: pedas, dipisah, tanpa cabe) sesuai preferensi Anda.

UI Modern

Desain antarmuka yang modern, clean, dan user-friendly dengan gradient orange yang eye-catching dan animasi smooth.

Dokumentasi Tampilan

Menu Screen
Halaman Menu
Shopping Cart
Keranjang Belanja
Order Form
Form Pemesanan
WhatsApp
Integrasi WhatsApp

Note: Ganti URL placeholder di atas dengan screenshot aplikasi Anda yang sebenarnya

Potongan Kode Penting

Berikut adalah beberapa kode penting yang menjadi inti dari aplikasi Warung Gorengan:

πŸ“„ MenuItem Model Class
// Model class untuk item menu
class MenuItem {
  final String id;
  final String name;
  final int price;
  final String imagePath;
  int quantity;
  String note;

  MenuItem({
    required this.id,
    required this.name,
    required this.price,
    required this.imagePath,
    this.quantity = 0,
    this.note = '',
  });

  // Getter untuk menghitung subtotal
  int get subtotal => price * quantity;
}
πŸ“± Integrasi WhatsApp
// Fungsi untuk membuka WhatsApp dengan pesan pesanan
void _openWhatsApp({
  required String role,
  required String name,
  required String room,
}) {
  final message = _createWhatsAppMessage(
    role: role, 
    name: name, 
    room: room
  );
  final phoneNumber = '6282130919861';
  final whatsappUrl = 
    'https://wa.me/$phoneNumber?text=${Uri.encodeComponent(message)}';

  try {
    html.window.open(whatsappUrl, '_blank');
  } catch (e) {
    // Handle error
  }
}
🎨 Gradient Button Widget
// Floating Action Button dengan gradient
Container(
  padding: const EdgeInsets.symmetric(
    horizontal: 24, 
    vertical: 16
  ),
  decoration: BoxDecoration(
    gradient: const LinearGradient(
      colors: [Color(0xFFFF6B35), Color(0xFFF7931E)],
    ),
    borderRadius: BorderRadius.circular(20),
  ),
  child: Text('Lihat Keranjang'),
)

Kesimpulan

Warung Gorengan merupakan solusi digital yang tepat untuk modernisasi sistem pemesanan makanan di lingkungan sekolah. Dengan menggabungkan teknologi Flutter yang powerful dan integrasi WhatsApp yang praktis, aplikasi ini berhasil menciptakan ekosistem jastip yang efisien dan mudah digunakan.

Aplikasi ini tidak hanya menghemat waktu siswa, guru, dan staf, tetapi juga membantu penjual dalam mengelola pesanan dengan lebih terorganisir. Desain UI/UX yang modern dengan gradient orange yang khas memberikan pengalaman visual yang menyenangkan dan profesional.

Fitur-fitur unggulan seperti keranjang belanja interaktif, form pemesan yang lengkap, dan integrasi WhatsApp langsung membuat proses pemesanan menjadi seamless dari awal hingga akhir. Dengan aplikasi ini, sistem jastip gorengan kantin menjadi lebih tertata, cepat, dan praktis.

Siap Mencoba?

Coba langsung aplikasi Warung Gorengan sekarang! Akses via web atau scan QR code.

QR Code Warung Gorengan

Scan untuk Akses Cepat

Aplikasi berjalan di browser, tidak perlu install!

Selasa, 11 November 2025

SOAL FOR LOOP

Quiz For Loop JavaScript

Programming Style - Level Expert
Waktu: Tidak Terbatas
SOAL 1 LEVEL: HARD

Perhatikan kode berikut dengan teliti. Apa output yang dihasilkan?

let output = ""; for (let i = 1; i <= 4; i++) { for (let j = 1; j <= i; j++) { output += (i + j) + " "; } output += "| "; } console.log(output);

Output yang benar adalah:

Penjelasan Detail:

Trace Execution:

  • i=1: j=1, (1+1)=2, output: "2 | "
  • i=2: j=1, (2+1)=3; j=2, (2+2)=4, output: "3 4 | "
  • i=3: j=1, (3+1)=4; j=2, (3+2)=5; j=3, (3+3)=6, output: "4 5 6 | "
  • i=4: j=1, (4+1)=5; j=2, (4+2)=6; j=3, (4+3)=7; j=4, (4+4)=8, output: "5 6 7 8 | "

Hasil Final: "2 | 3 4 | 4 5 6 | 5 6 7 8 | "

SOAL 2 LEVEL: EXPERT

Kode berikut bertujuan menghitung faktorial dari 5. Namun ada error yang menyebabkan hasilnya salah. Identifikasi masalahnya!

let result = 0; for (let i = 1; i <= 5; i++) { result *= i; } console.log(result); // Expected: 120

Apa yang salah dan bagaimana memperbaikinya?

Penjelasan Detail:

Analisis Bug:

  • Masalah: Inisialisasi result = 0
  • Dampak: 0 x 1 = 0, 0 x 2 = 0, hasilnya selalu 0
  • Solusi: Ubah menjadi let result = 1
  • Proses Benar: 1 x 1 = 1, 1 x 2 = 2, 2 x 3 = 6, 6 x 4 = 24, 24 x 5 = 120

Konsep: Untuk perkalian beruntun (faktorial), nilai awal HARUS 1, bukan 0!

Skor Anda

0 / 2

Soal Latihan For Loop

Soal Latihan For Loop

Materi: Traditional For Loop dengan Initializer, Condition, dan Increment

Pengantar

For loop adalah salah satu struktur perulangan paling fundamental dalam pemrograman JavaScript. Loop ini terdiri dari tiga bagian utama: initializer (nilai awal), condition (kondisi), dan increment (perubahan nilai).

SOAL 1

Perhatikan kode berikut:

for (let i = 1; i <= 5; i++) { console.log(i); }

Apa output yang dihasilkan dari kode di atas?

  • A. 1 2 3 4
  • B. 1 2 3 4 5
  • C. 0 1 2 3 4
  • D. 2 3 4 5 6
  • E. 1 2 3 4 5 6

✓Jawaban: B

Penjelasan:
  • Initializer: let i = 1 - Loop dimulai dari nilai 1
  • Condition: i <= 5 - Loop berjalan selama i kurang dari atau sama dengan 5
  • Increment: i++ - Nilai i bertambah 1 setiap iterasi
  • Output: 1, 2, 3, 4, 5
SOAL 2

Perhatikan kode berikut:

let total = 0; for (let i = 2; i <= 6; i += 2) { total += i; } console.log(total);

Berapakah nilai total yang akan ditampilkan?

  • A. 6
  • B. 8
  • C. 10
  • D. 12
  • E. 14

✓Jawaban: D

Penjelasan:

Loop ini menambahkan bilangan genap dari 2 sampai 6:

  • Iterasi 1: i = 2, total = 0 + 2 = 2
  • Iterasi 2: i = 4, total = 2 + 4 = 6
  • Iterasi 3: i = 6, total = 6 + 6 = 12
  • Hasil akhir: total = 12

Selasa, 09 September 2025

ZenBeforeExam: Dari Stres Jadi Solusi

🧘‍♂️ ZenBeforeExam: Dari Stres Jadi Solusi

“Gue gak bisa tidur. Besok ujian Matematika. Udah belajar seminggu, tapi tetep aja ngerasa ‘gak siap’. Jantung deg-degan. Kepala berat. HP gue buka-buka terus — cuma buat scroll TikTok biar lupa. Tapi malah makin panik.”

Kalimat di atas… pernah kamu rasain?
Aku pernah. Dan mungkin kamu juga.
Dan jutaan pelajar lain di luar sana.

Itulah kenapa aku mulai ngerancang ZenBeforeExam — aplikasi meditasi & relaksasi khusus buat pelajar yang lagi stres-stresnya jelang ujian. Masih prototype. Belum jadi. Tapi prosesnya udah bikin aku belajar banyak — dan mungkin, bisa jadi inspirasi buat kamu juga.

πŸ’‘ Ide Gila yang Muncul di Tengah Kepanikan

Aku lagi ngerjain tugas Flutter di Zaprun (platform coding Flutter yang lumayan enak buat pemula kayak aku). Terus mikir:
“Flutter bisa bikin apa aja… kenapa gak bikin sesuatu yang beneran gue butuhin?”

Dan voila — muncullah ide: aplikasi meditasi 5 menit buat reset otak sebelum ujian.

“Ujian itu penting — tapi kesehatan mental kita, jauh lebih penting.”

🎯 Fitur yang Kubayangin — Simple Tapi Powerful

  • 🧘‍♀️ Meditasi Kilat 5 Menit — suara lembut, panduan napas, tanpa gangguan
  • 🌬️ Animasi Napas Visual — ikutin lingkaran mengembang & menciut, biar detak jantung pelan
  • 🎧 Mix & Match Suara — hujan + lo-fi? Bisa! Biar belajar makin fokus
  • πŸ’¬ Quote Penyemangat Harian — swipe, dapetin energi baru
  • ⏰ Pengingat “Istirahat Dulu” — karena otakmu butuh jeda, bukan dipaksa terus

πŸ› ️ Teknologi? Flutter + Zaprun — Why Not?

Aku pake Flutter karena cross-platform dan UI-nya fleksibel. Dan aku pilih Zaprun karena ringan, cepat, cocok buat prototyping.

Package yang rencana aku pake: audioplayers, flutter_local_notifications, provider.

πŸ“ˆ Roadmap: Dari Ide ke Nyata (Perlahan Tapi Pasti)

  • Minggu ini: Selesaiin desain UI → implementasi dasar di Zaprun
  • Minggu depan: Bikin fitur meditasi audio & animasi napas
  • Bulan depan: Uji coba ke 5 temen sekelas — minta feedback
  • Q3 2025: Tambah fitur “Study Session Timer + Break Meditasi”

🎯 Lihat Pitch Deck Lengkap “ZenBeforeExam”

Penasaran gimana tampilan rancangan aplikasi ini dalam bentuk presentasi?
Aku udah bikin pitch deck lengkap — 10 slide, desain calming, modern, dan jujur banget soal proses & roadmap.

πŸ‘‰ Lihat Pitch Deck “ZenBeforeExam” di Canva.app

(Klik link di atas — akan terbuka di tab baru. Desainnya aku buat pakai warna pastel biar beneran feel-nya “zen” 😌)

Cover ZenBeforeExam Pitch Deck

Pitch deck ini masih versi rancangan — tapi semoga bisa kasih gambaran utuh soal visi, fitur, dan rencana ke depannya.

❤️ Kenapa Ini Penting Buatku — dan Mungkin Buat Kamu Juga

Ini bukan cuma soal nilai tugas atau portofolio.
Ini soal: “Bagaimana caranya bikin teknologi yang beneran ngerti perasaan manusia?”

🀝 Yuk Kolaborasi!

Aku buka banget buat kolaborasi:

  • Kamu jago desain? Bantu aku bikin UI makin calming
  • Kamu jago Flutter? Yuk ngoding bareng!
  • Kamu sering stres ujian? Coba kasih masukan — fitur apa yang kamu butuhin?

🌱 Ini Baru Awal

ZenBeforeExam belum sempurna. Belum jadi. Belum ada di Play Store.
Tapi… ini awal dari sesuatu yang (semoga) bermakna.

Tarik napas… Kamu gak sendiri. Kamu pasti bisa.

#Flutter #Zaprun #MentalHealth #StudentLife #ZenBeforeExam

Baso Soteng Kantin, Kini Bisa Jastip! πŸŽ‰

 



Siapa sih yang nggak kenal **Baso Soteng kantin sekolah**? Setiap jam istirahat, aroma gurih kuahnya langsung bikin semua siswa ngiler. Tapi ada satu masalah klasik: **antreannya panjang banget**. Kadang sebelum sampai giliran, jam istirahat sudah habis duluan.


Nah, dari keresahan itu, kami akhirnya punya ide untuk bikin layanan **Jasa Titip (Jastip) Baso Soteng**. Tujuannya simpel: biar siswa, guru, dan staf sekolah bisa tetap menikmati baso soteng tanpa ribet antre.


---


## Ceritanya Berawal dari Kantin


Bayangin deh, bel istirahat berbunyi. Semua langsung lari ke kantin, suasananya rame banget. Ada yang buru-buru pesen, ada yang nunggu lama, bahkan ada juga yang akhirnya batal makan karena waktunya nggak cukup.


Dari situ, kami kepikiran: *“Kalau pesanannya bisa lewat HP, pasti lebih gampang.”* Akhirnya lahirlah ide **Jastip Baso Soteng** ini.


---


## Apa Itu Jastip Baso Soteng?


Jastip ini bukan sekadar titip makanan biasa. Kami sudah buat sistem sederhana berbasis WhatsApp. Jadi, ketika kamu klik tombol “Pesan Sekarang”, langsung muncul chat otomatis dengan format pesan pesanan.


Contohnya:

*"Halo, saya ingin memesan Baso Soteng. Terima kasih."*


Praktis kan? Tinggal tunggu konfirmasi, dan pesananmu siap diambil di kantin.


---


## Kenapa Kamu Harus Coba?


* Nggak perlu lagi rebutan antre.

* Hemat waktu, bisa tetap istirahat atau ngobrol sama teman.

* Bisa dipakai oleh siswa, guru, maupun staf.

* Baso Soteng tetap hangat, tetap nikmat.


---


## Mau Coba? Ini Caranya


1. Klik tombol **Pesan Sekarang**.

2. Kamu akan langsung diarahkan ke WhatsApp.

3. Kirim pesan otomatis yang sudah disiapkan.

4. Tunggu konfirmasi, lalu ambil pesananmu.


---


## Demo Aplikasi Jastip


Biar lebih jelas, kami juga sudah menyiapkan **demo aplikasi Jastip Baso Soteng**. Kamu bisa langsung coba lewat link ini:


πŸ”— [Klik untuk melihat demo](https://zva2061xva30.zapp.page/#/)


Atau scan barcode berikut:





---


Penutup


Dengan adanya Jastip Baso Soteng ini, kami berharap semua warga sekolah bisa lebih nyaman menikmati makanan kantin tanpa harus kelelahan antre.


Layanan ini dibuat oleh kami, siswa SMK:

Ragil, Wisnu, Ken, dan Sendi ✨


Terima kasih sudah mendukung ide kecil kami. Semoga Jastip ini bisa bermanfaat untuk semua! πŸ™


Selasa, 26 Agustus 2025

Aplikasi Jastip Warung Teh Ulpa (Flutter Web)


Aplikasi Jastip Warung Teh Ulpa (Flutter Web)

Demo: https://zwbm067uwbn0.zapp.page/#/

Proyek ini membantu siswa/siswi SMKN 1 Banjar memesan makanan di kantin Warung Teh Ulpa via aplikasi web. Fitur utama: katalog menu, keranjang, catatan per item, checkout (wajib isi Nama/Kelas dan No. Telepon), serta struk belanja untuk ditunjukkan ke jastip/tukang.

Stack singkat: Flutter Web • Provider • Intl (locale id_ID) • Aset gambar lokal (statis) • Pembayaran COD (tip dibayar langsung, tidak dihitung aplikasi).

Daftar Isi

  1. Tampilan Awal (Katalog)
  2. Menu Checkout
  3. Struk Belanja
  4. Potongan Kode Penting
  5. Penutup

1) Tampilan Awal (Katalog)

Pada halaman awal, pengguna melihat daftar produk dengan gambar, judul, subjudul, harga, serta kontrol tambah/kurang. Produk tertentu bergabung sebagai varian (misalnya Bala-bala / Karoket dan Gurandil / Rencang) dengan tombol pilihan varian.


Gambar 1. Katalog produk Warung Teh Ulpa (klik gambar produk untuk preview besar).

2) Menu Checkout

Setelah memilih produk, pengguna masuk ke halaman checkout. Di sini tampil ringkasan pesanan, tombol edit catatan per item, dan form wajib Nama/Kelas serta Nomor Telepon. Metode pembayaran hanya COD.


Gambar 2. Checkout dengan ringkasan belanja & form data pembeli.

3) Struk Belanja

Setelah menekan tombol Buat Struk, aplikasi menampilkan struk bergaya minimarket yang memuat Order ID, Tanggal, Nama/Kelas, No. Telepon, serta daftar item dan total. Struk ini cukup ditunjukkan ke jastip/tukang sebagai daftar belanja.


Gambar 3. Struk pesanan siap ditunjukkan ke jastip.

4) Potongan Kode Penting

A. Inisialisasi Locale (hindari LocaleDataException)

// main.dart (bagian atas)
import 'package:intl/intl.dart';
import 'package:intl/date_symbol_data_local.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await initializeDateFormatting('id_ID', null); // penting untuk Flutter Web
  Intl.defaultLocale = 'id_ID';
  runApp(const JastipApp());
}

B. Data Produk (termasuk varian gabungan)

// CatalogProvider: contoh produk + varian
final List<Product> _items = const [
  Product(
    id: 'gehu',
    name: 'Gehu',
    price: 1000,
    assetPath: 'assets/images/gehu.jpg',
    subtitle: 'Tahu isi, cocok pakai cabe',
  ),
  // Bala-bala / Karoket (1 gambar, 2 varian harga sama)
  Product(
    id: 'balakaroket',
    name: 'Bala-bala / Karoket',
    assetPath: 'assets/images/karoket.jpg',
    subtitle: 'Pilih salah satu',
    variants: [
      ProductVariant(key: 'balabala', name: 'Bala-bala', price: 1000),
      ProductVariant(key: 'karoket', name: 'Karoket', price: 1000),
    ],
  ),
  // Gurandil / Rencang (harga berbeda)
  Product(
    id: 'guranren',
    name: 'Gurandil / Rencang',
    assetPath: 'assets/images/rencang_gurandil.jpg',
    subtitle: 'Pilih salah satu',
    variants: [
      ProductVariant(key: 'gurandil', name: 'Gurandil', price: 3000),
      ProductVariant(key: 'rencang', name: 'Rencang', price: 3500),
    ],
  ),
];

C. Form Checkout (wajib Nama/Kelas & Nomor Telepon)

// Di CheckoutPage: form validasi data pembeli
final _formKey = GlobalKey<FormState>();
final _buyerClassCtrl = TextEditingController();
final _buyerPhoneCtrl = TextEditingController();

Form(
  key: _formKey,
  child: Card(
    child: Padding(
      padding: const EdgeInsets.all(12),
      child: Column(
        children: [
          TextFormField(
            controller: _buyerClassCtrl,
            decoration: const InputDecoration(
              labelText: 'Nama/Kelas',
              hintText: 'Contoh: Andi • XI RPL 2',
            ),
            validator: (v) {
              if (v == null || v.trim().isEmpty) return 'Nama/Kelas wajib diisi';
              if (v.length < 3) return 'Terlalu pendek';
              return null;
            },
          ),
          SizedBox(height: 12),
          TextFormField(
            controller: _buyerPhoneCtrl,
            keyboardType: TextInputType.phone,
            decoration: const InputDecoration(
              labelText: 'Nomor Telepon',
              hintText: 'Contoh: 08xxxxxxxxxx',
            ),
            validator: (v) {
              if (v == null || v.trim().isEmpty) return 'Nomor telepon wajib diisi';
              final d = v.replaceAll(RegExp(r'[^0-9]'), '');
              if (d.length < 10 || d.length > 15) return 'Nomor telepon tidak valid';
              return null;
            },
          ),
        ],
      ),
    ),
  ),
);

D. Membuat Struk & Navigasi

// Di tombol "Buat Struk" (CheckoutPage)
onPressed: () {
  if (!_formKey.currentState!.validate()) {
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('Lengkapi data pembeli dulu ya.')),
    );
    return;
  }

  final orderId = generateOrderId();
  final data = OrderTicketData(
    orderId: orderId,
    total: cart.subtotal,
    createdAt: DateTime.now(),
    buyerClass: _buyerClassCtrl.text.trim(),
    buyerPhone: _buyerPhoneCtrl.text.trim(),
    items: cart.items.values.map((e) => TicketItem(
      name: e.displayName, qty: e.qty, price: e.unitPrice, note: e.note,
    )).toList(),
  );

  cart.clear();
  Navigator.pushReplacement(
    context,
    MaterialPageRoute(builder: (_) => OrderReceiptPage(data: data)),
  );
},

E. Tampilan Struk (ringkas)

// OrderReceiptPage: menampilkan detail struk
Column(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [
    Text('Warung Teh Ulpa', style: TextStyle(fontWeight: FontWeight.w800)),
    Text('Order ID: ${data.orderId}'),
    Text('Tanggal: ${formatDateTime(data.createdAt)}'),
    Text('Nama/Kelas: ${data.buyerClass}'),
    Text('No. Telepon: ${data.buyerPhone}'),
    Divider(),
    ...data.items.map((e) => Row(
      children: [
        Expanded(child: Text('${e.name}  x${e.qty}')),
        Text(rupiah(e.price * e.qty)),
      ],
    )),
    Divider(),
    Row(
      children: [
        Expanded(child: Text('Total Bayar (COD)', style: TextStyle(fontWeight: FontWeight.w800))),
        Text(rupiah(data.total), style: TextStyle(fontWeight: FontWeight.w900)),
      ],
    ),
  ],
)

5) Penutup

Dengan pendekatan data statis + varian per kartu, catatan per item, dan struk bergaya minimarket, alur jastip menjadi jelas: pembeli memilih menu, menambahkan catatan, mengisi data, lalu menunjukkan struk ke jastip/tukang. Pembayaran COD memudahkan di lingkungan sekolah, sementara tip dibayar langsung di tempat.

Tips: Untuk menambah gambar baru, letakkan di assets/images/ dan pastikan path sama dengan di assetPath produk.

Belajar CRUD Flutter + MockAPI.io

 

---


# πŸ“± Belajar CRUD Flutter + MockAPI.io


Halo semua πŸ‘‹

Saya baru saja membuat aplikasi sederhana menggunakan **Flutter** yang terhubung dengan **MockAPI.io**. Aplikasi ini bisa melakukan **CRUD (Create, Read, Delete, dan nanti bisa ditambah Update)** dengan database online.


## πŸ”— Link Project Demo


πŸ‘‰ [Coba aplikasinya di sini](https://zf48062dg490.zapp.page/#/)


## πŸ’» Source Code Lengkap


Kalau mau lihat langsung kode Flutter-nya, bisa cek di Pastebin berikut:

πŸ‘‰ [Lihat source code](https://pastebin.com/Sn1ATvZ4)


## πŸ› ️ Fitur Aplikasi


✨ Input data (Nama, Email, Tempat Lahir, Tanggal Lahir)

✨ Menampilkan data dari API

✨ Hapus data dari daftar

✨ Format tanggal rapi tanpa `T00:00:00.000`


## 🎯 Tujuan Project


Project ini dibuat untuk **belajar koneksi Flutter ke API** dengan cara yang mudah, cepat, dan gratis menggunakan MockAPI.io.


---


Jadi, buat teman-teman yang lagi belajar Flutter πŸš€ silakan coba juga, semoga bermanfaat!

Kalau ada yang mau tanya atau butuh file project, langsung aja komentar ✨


---


Selasa, 12 Agustus 2025

Membuat login mobile menggunakan flutter

πŸš€ My Flutter Login Page — Simple UI, Big Vibes ✨ + Smooth Animation πŸ’»πŸ”₯



Yo fam! πŸ‘‹
Hari ini gue mau flex hasil ngulik di Flutter — yes, a clean & modern mobile login page yang sekarang udah pake smooth animation. πŸ’»✨

Bayangin, pas app dibuka, form login nggak langsung nongol gitu aja, tapi muncul dari bawah dengan animasi halus ala aplikasi premium. 🫢πŸ”₯

Highlight-nya:
πŸ‘€ Big profile icon on top
πŸ”’ Smooth username & password input fields
πŸ‘ Show/hide password button — so you don’t mess up your password
🟠 Bold “Sign in” button
🎬 Slide-up animation pas masuk halaman login


Nah, buat lo yang pengen nyoba bikin login page ini, gue kasih langsung kode lengkapnya. Tinggal copy → paste → run di Flutter. πŸš€

import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(
    debugShowCheckedModeBanner: false,
    home: LoginPage(),
  ));
}

class LoginPage extends StatefulWidget {
  const LoginPage({Key? key}) : super(key: key);

  @override
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage>
    with SingleTickerProviderStateMixin {
  bool _obscurePassword = true;
  late AnimationController _controller;
  late Animation<Offset> _slideAnimation;

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 700),
    );

    _slideAnimation = Tween<Offset>(
      begin: const Offset(0, 1), // mulai dari bawah
      end: Offset.zero, // posisi normal
    ).animate(CurvedAnimation(
      parent: _controller,
      curve: Curves.easeOutCubic,
    ));

    // mulai animasi saat halaman muncul
    _controller.forward();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFF009688),
      body: Center(
        child: SingleChildScrollView(
          child: Column(
            children: [
              const Icon(Icons.account_circle, size: 100, color: Colors.white),
              const SizedBox(height: 20),
              SlideTransition(
                position: _slideAnimation,
                child: Container(
                  margin: const EdgeInsets.symmetric(horizontal: 20),
                  padding: const EdgeInsets.all(20),
                  decoration: BoxDecoration(
                    color: Colors.white,
                    borderRadius: BorderRadius.circular(12),
                    boxShadow: [
                      BoxShadow(
                        color: Colors.black26,
                        blurRadius: 6,
                        offset: const Offset(0, 2),
                      ),
                    ],
                  ),
                  child: Column(
                    children: [
                      TextField(
                        decoration: InputDecoration(
                          prefixIcon: const Icon(Icons.person),
                          labelText: 'User Name',
                          border: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(8),
                          ),
                        ),
                      ),
                      const SizedBox(height: 15),
                      TextField(
                        obscureText: _obscurePassword,
                        decoration: InputDecoration(
                          prefixIcon: const Icon(Icons.lock),
                          suffixIcon: IconButton(
                            icon: Icon(
                              _obscurePassword
                                  ? Icons.visibility_off
                                  : Icons.visibility,
                            ),
                            onPressed: () {
                              setState(() {
                                _obscurePassword = !_obscurePassword;
                              });
                            },
                          ),
                          labelText: 'Password',
                          border: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(8),
                          ),
                        ),
                      ),
                      const SizedBox(height: 10),
                      Align(
                        alignment: Alignment.centerRight,
                        child: TextButton(
                          onPressed: () {},
                          child: const Text('Forgot Password?'),
                        ),
                      ),
                      const SizedBox(height: 10),
                      SizedBox(
                        width: double.infinity,
                        height: 45,
                        child: ElevatedButton(
                          style: ElevatedButton.styleFrom(
                            backgroundColor: Colors.orange,
                            shape: RoundedRectangleBorder(
                              borderRadius: BorderRadius.circular(8),
                            ),
                          ),
                          onPressed: () {},
                          child: const Text(
                            'Sign in',
                            style: TextStyle(fontSize: 16),
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Penutup:

That’s it gengs! 🎯
Sekarang lo udah punya login page Flutter yang bukan cuma simple & modern, tapi juga punya smooth animation biar keliatan premium. πŸš€✨

Kalau lo baru belajar Flutter, project ini cocok banget buat latihan karena:

  • Lo bakal ngerti dasar layout widget

  • Lo belajar handle input text & tombol

  • Lo bisa explore animasi biar UI lebih hidup

Next level? Tinggal sambungin ke Firebase Auth atau API backend biar login ini beneran berfungsi. πŸ”₯

So… gimana?
Apakah login page ini udah pass the vibe check menurut lo? 😏
Drop pendapat lo di kolom komentar, dan jangan lupa share ke temen coding lo biar makin rame!

Happy coding & keep building awesome stuff! πŸ’»πŸ’™

#FlutterDev #UIUX #GenZCoding #CodeLife #MobileDev



Selasa, 05 Agustus 2025

Bikin Fitur Maps di ZaapRun Flutter, Ada Tombol Rute & WhatsApp Gesss πŸ—Ί️πŸ’¬






Heyy sobat ngoding! 😎
Di project Flutter gue yang bernama ZaapRun, gue lagi nambahin fitur yang menurut gue wajib banget ada di aplikasi kekinian — yaitu Maps pake iframe, lengkap sama tombol rute dan tombol chat ke WA! πŸ”₯

🧠 Kenapa Nggak Pake Plugin Maps?

Gue tau sih di Flutter ada plugin kayak google_maps_flutter, tapi ribet bro, harus API key, setting sana-sini, kadang error juga. Jadi gue cari cara yang lebih cepet dan simpel: langsung sematkan Google Maps pake iframe di dalam WebView.
Lebih ringan, lebih clean, dan tetep works!


πŸ”§ Cara Gue Nerapin di Flutter

Gue pake package webview_flutter buat nampilin HTML-nya. Nah di HTML itu gue tempelin iframe maps + 2 tombol penting:

WebView(
  initialUrl: Uri.dataFromString(
    '''
    <html>
    <body style="margin:0;padding:0;text-align:center;font-family:sans-serif;">
      <iframe 
        src="https://www.google.com/maps/embed?pb=..." 
        width="100%" height="350" style="border:0;" allowfullscreen loading="lazy">
      </iframe>
      <br><br>
      <a href="https://www.google.com/maps/dir/?api=1&destination=-6.123456,106.123456" target="_blank">
        <button style="padding:10px 25px;border:none;background:#000;color:#fff;border-radius:8px;">πŸ“ Buka Rute</button>
      </a>
      <br><br>
      <a href="https://wa.me/6281234567890" target="_blank">
        <button style="padding:10px 25px;border:none;background:#25D366;color:#fff;border-radius:8px;">πŸ’¬ Chat WA</button>
      </a>
    </body>
    </html>
    ''',
    mimeType: 'text/html'
  ).toString(),
  javascriptMode: JavascriptMode.unrestricted,
)

πŸš€ Hasilnya Gimana?

  • Bisa liat lokasi langsung di aplikasi.

  • Bisa klik tombol "Buka Rute" buat langsung navigasi ke lokasi via Google Maps.

  • Bisa klik tombol "Chat WA" buat langsung ngobrol.

Semua langsung jalan, tanpa perlu install plugin ribet, tanpa loading berat. Simpel, clean, dan user-friendly banget.


πŸ’­ Buat Apa Sih Fitur Ini?

Cocok buat lo yang mau bikin aplikasi:

  • Lokasi bisnis πŸͺ

  • Titik jemput/antar πŸ›΅

  • Map sekolah, rumah, kampus, atau event πŸ“

  • Dan pokoknya biar user langsung tau: "Gue harus ke mana nih?"


Segitu dulu cerita gue kali ini.
Kalo lo pengen nyoba juga, tinggal copas aja konsep ini. Dijamin gampang dan cepet jadi. 😎
Mau tanya-tanya? Klik tombol WhatsApp aja gengs! πŸ’¬

Peace out, stay ngoding! ✌️



Kamis, 24 April 2025

aplikasi To-Do List dengan fitur CRUD menggunakan SQLite

 Berikut adalah contoh kode Flutter lengkap untuk aplikasi To-Do List dengan fitur CRUD menggunakan SQLite:


---


### **1. Tambahkan dependensi di `pubspec.yaml`:**


```yaml

dependencies:

  flutter:

    sdk: flutter

  sqflite: ^2.3.0

  path: ^1.9.0

```


---


### **2. Buat file model: `models/todo.dart`**


```dart

class Todo {

  int? id;

  String title;

  String description;


  Todo({this.id, required this.title, required this.description});


  Map<String, dynamic> toMap() {

    return {

      'id': id,

      'title': title,

      'description': description,

    };

  }


  factory Todo.fromMap(Map<String, dynamic> map) {

    return Todo(

      id: map['id'],

      title: map['title'],

      description: map['description'],

    );

  }

}

```


---


### **3. Buat file database helper: `helpers/database_helper.dart`**


```dart

import 'package:sqflite/sqflite.dart';

import 'package:path/path.dart';

import '../models/todo.dart';


class DatabaseHelper {

  static final DatabaseHelper _instance = DatabaseHelper._internal();

  factory DatabaseHelper() => _instance;

  DatabaseHelper._internal();


  static Database? _database;


  Future<Database> get database async {

    return _database ??= await _initDatabase();

  }


  Future<Database> _initDatabase() async {

    final dbPath = await getDatabasesPath();

    final path = join(dbPath, 'todo.db');


    return openDatabase(

      path,

      version: 1,

      onCreate: (db, version) async {

        await db.execute('''

          CREATE TABLE todos (

            id INTEGER PRIMARY KEY AUTOINCREMENT,

            title TEXT,

            description TEXT

          )

        ''');

      },

    );

  }


  Future<int> insertTodo(Todo todo) async {

    final db = await database;

    return await db.insert('todos', todo.toMap());

  }


  Future<List<Todo>> getTodos() async {

    final db = await database;

    final maps = await db.query('todos');

    return List.generate(maps.length, (i) {

      return Todo.fromMap(maps[i]);

    });

  }


  Future<int> updateTodo(Todo todo) async {

    final db = await database;

    return await db.update(

      'todos',

      todo.toMap(),

      where: 'id = ?',

      whereArgs: [todo.id],

    );

  }


  Future<int> deleteTodo(int id) async {

    final db = await database;

    return await db.delete('todos', where: 'id = ?', whereArgs: [id]);

  }

}

```


---


### **4. Buat file utama: `main.dart`**


```dart

import 'package:flutter/material.dart';

import 'helpers/database_helper.dart';

import 'models/todo.dart';


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


class MyApp extends StatelessWidget {

  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      title: 'To-Do List CRUD',

      theme: ThemeData(primarySwatch: Colors.blue),

      home: TodoListScreen(),

    );

  }

}


class TodoListScreen extends StatefulWidget {

  @override

  _TodoListScreenState createState() => _TodoListScreenState();

}


class _TodoListScreenState extends State<TodoListScreen> {

  final dbHelper = DatabaseHelper();

  List<Todo> todos = [];


  @override

  void initState() {

    super.initState();

    refreshTodos();

  }


  void refreshTodos() async {

    final data = await dbHelper.getTodos();

    setState(() {

      todos = data;

    });

  }


  void showTodoDialog({Todo? todo}) {

    final titleController = TextEditingController(text: todo?.title);

    final descController = TextEditingController(text: todo?.description);


    showDialog(

      context: context,

      builder: (_) => AlertDialog(

        title: Text(todo == null ? 'Tambah Todo' : 'Edit Todo'),

        content: Column(

          mainAxisSize: MainAxisSize.min,

          children: [

            TextField(controller: titleController, decoration: InputDecoration(labelText: 'Judul')),

            TextField(controller: descController, decoration: InputDecoration(labelText: 'Deskripsi')),

          ],

        ),

        actions: [

          TextButton(onPressed: () => Navigator.pop(context), child: Text('Batal')),

          ElevatedButton(

            onPressed: () async {

              final title = titleController.text;

              final desc = descController.text;

              if (title.isNotEmpty && desc.isNotEmpty) {

                if (todo == null) {

                  await dbHelper.insertTodo(Todo(title: title, description: desc));

                } else {

                  await dbHelper.updateTodo(Todo(id: todo.id, title: title, description: desc));

                }

                refreshTodos();

                Navigator.pop(context);

              }

            },

            child: Text('Simpan'),

          ),

        ],

      ),

    );

  }


  void deleteTodo(int id) async {

    await dbHelper.deleteTodo(id);

    refreshTodos();

  }


  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(title: Text('To-Do List')),

      body: ListView.builder(

        itemCount: todos.length,

        itemBuilder: (context, index) {

          final todo = todos[index];

          return ListTile(

            title: Text(todo.title),

            subtitle: Text(todo.description),

            trailing: Row(

              mainAxisSize: MainAxisSize.min,

              children: [

                IconButton(icon: Icon(Icons.edit), onPressed: () => showTodoDialog(todo: todo)),

                IconButton(icon: Icon(Icons.delete), onPressed: () => deleteTodo(todo.id!)),

              ],

            ),

          );

        },

      ),

      floatingActionButton: FloatingActionButton(

        child: Icon(Icons.add),

        onPressed: () => showTodoDialog(),

      ),

    );

  }

}

```


---


Kalau kamu mau aku bantu buatkan folder strukturnya atau ingin menambahkan fitur lain seperti pencarian, kategori, atau deadline, tinggal bilang aja!

WARUNG GORENGAN - SENDI

🍽️ Warung Gorengan Aplikasi Jastip Gorengan Kantin - Solusi Modern untuk Pesan...