• PEMROGRAMAN WEB DINAMIS

    Pengertian web dinamis adalah suatu web yang konten atau isinya dapat berubah-ubah setiap saat. Sebab dalam teknologi pembuatan web dinamis sudah dirancang semudah mungkin bagi pemakai atau user yang menggunakan web tersebut..

  • SIMULASI DAN KOMUNIKASI DIGITAL

    Suatu proses peniruan dalam bentuk visual yang dideskripsikan menyerupai kata, gambar dan grafis..

  • SISTEM KOMPUTER

    Sistem komputer adalah suatu jaringan elektronik yang terdiri dari perangkat lunak dan perangkat keras yang melakukan tugas tertentu (menerima input, memproses input, menyimpan perintah-perintah, dan menyediakan output dalam bentuk informasi). Selain itu dapat pula diartikan sebagai elemen-elemen yang terkait untuk menjalankan suatu aktivitas dengan menggunakan komputer..

  • DASAR DESAIN GRAPIS

    Banyak yang berpikiran kalau desain yang baik adalah yang membutuhkan jam kerja yang banyak, membutuhkan skill tinggi dan aplikasi yang mahal. Ya, memang, tapi sebenarnya desain yang baik adalah desain yang sederhana, yang membuat setiap orang yang melihatnya mudah menangkap maksud dari sebuah bentuk visual tersebut..

  • BASIS DATA

    Pangkalan data atau basis data (bahasa Inggris: database) adalah kumpulan informasi yang disimpan di dalam komputer secara sistematik sehingga dapat diperiksa menggunakan suatu program komputer untuk memperoleh informasi dari basis data tersebut. Perangkat lunak yang digunakan untuk mengelola dan memanggil kueri (query) basis data disebut sistem manajemen basis data (database management system, DBMS). Sistem basis data dipelajari dalam ilmu informasi.

Cara Agar Link Blogspot Muncul Gambar Saat Dibagikan ke WhatsApp

Cara Agar Link Blogspot Muncul Gambar Saat Dibagikan ke WhatsApp

Pernahkah Anda membagikan link artikel blog ke WhatsApp, lalu yang muncul hanya judul, deskripsi, dan screenshot bawaan blog? Padahal akan lebih menarik kalau muncul gambar khusus (thumbnail/cover) sesuai yang kita inginkan.

Nah, hal itu bisa diatur dengan menambahkan meta tag Open Graph (og:image) pada template blog. Dengan cara ini, setiap kali artikel dibagikan ke WhatsApp, Facebook, atau media sosial lain, akan muncul preview dengan gambar, judul, dan deskripsi.


Solusi untuk Blogger (Blogspot)

Karena Anda menggunakan platform Blogger (alamat blog berakhiran *.blogspot.com), maka langkahnya cukup sederhana:

  1. Masuk ke Dashboard Blogger

  2. Pilih menu TemaEdit HTML

  3. Cari bagian <head> ... </head>

  4. Tambahkan kode berikut di dalamnya:

<b:if cond='data:blog.pageType == &quot;item&quot;'>
  <meta expr:content='data:blog.pageName' property='og:title'/>
  <meta expr:content='data:blog.pageTitle' property='og:description'/>
  <meta expr:content='data:blog.url' property='og:url'/>
  <meta expr:content='data:blog.postImageUrl' property='og:image'/>
</b:if>

Apa Fungsinya?

  • og:title → otomatis mengambil judul postingan

  • og:description → otomatis mengambil ringkasan/deskripsi postingan

  • og:image → otomatis menggunakan gambar pertama dalam postingan atau featured image

Sehingga Anda tidak perlu repot menulis meta tag di setiap artikel.


Menentukan Gambar Khusus untuk Semua Artikel

Kalau Anda ingin semua artikel punya thumbnail tetap (misalnya logo atau cover khusus), bisa gunakan kode:

<meta property="og:image" content="https://i.ibb.co/xyz123/thumbnail-flutter.png"/>

👉 Ganti URL dengan alamat gambar Anda. Bisa diupload di ImgBB, Google Drive (public link), atau folder Blogger.


Tips Penting

  • Gunakan ukuran gambar minimal 1200 x 630 px agar tidak pecah atau terpotong.

  • Setelah edit template, cek hasilnya dengan Facebook Sharing Debugger agar cache preview diperbarui.

  • Jika di WhatsApp belum muncul, biasanya butuh waktu beberapa menit sampai perubahan terbaca.


Kesimpulan

Saat ini, link blog Anda memang sudah bisa muncul preview di WhatsApp, tetapi masih mengambil screenshot otomatis.
Dengan menambahkan meta tag og:image, Anda bisa menentukan gambar thumbnail sesuai keinginan.

Hasilnya, ketika artikel dibagikan ke grup WA atau media sosial lain, tampilannya akan jauh lebih menarik dan profesional.


👉 Sudah siap coba edit template Blogger Anda? Dijamin setelah itu, link blog Anda akan terlihat lebih keren saat dibagikan!


Salam : Redaksi

Share:

Belajar Membuat Aplikasi Data Kelompok dengan Dart Flutter di Zapp.run

Belajar Membuat Aplikasi Data Kelompok dengan Dart Flutter di Zapp.run

Saat belajar pemrograman mobile dengan Dart Flutter, salah satu latihan menarik adalah membuat tampilan aplikasi sederhana yang menampilkan data sebuah kelompok belajar. Dengan memanfaatkan platform zapp.run, kita bisa langsung mencoba kode Flutter tanpa perlu instalasi IDE yang rumit.

Tujuan Latihan

Dalam latihan ini, saya ingin membuat sebuah aplikasi satu halaman (one page app) dengan fitur:

  1. Bagian atas menampilkan judul "Nama Kelompok".

  2. Bagian tengah menampilkan daftar anggota kelompok berupa foto, nama, nomor absen, dan alamat.

  3. Bagian bawah terdapat tombol, dan ketika ditekan akan muncul popup berisi pesan "Terima Kasih".

Latihan ini sederhana, tetapi melatih pemahaman dasar tentang layout, list, image, card, button, dan dialog popup di Flutter.

Kode Lengkap main.dart

Berikut kode lengkap yang saya tuliskan di main.dart:

import 'package:flutter/material.dart';

void main() {
  runApp(const KelompokApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Data Kelompok Belajar',
      debugShowCheckedModeBanner: false,
      home: const KelompokPage(),
    );
  }
}

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

  // Contoh data anggota
  final List<Map<String, String>> anggota = const [
    {
      "nama": "Andi",
      "absen": "01",
      "alamat": "Jl. Merdeka No. 10",
      "foto": "assets/andi.png",
    },
    {
      "nama": "Budi",
      "absen": "02",
      "alamat": "Jl. Mawar No. 5",
      "foto": "assets/budi.png",
    },
    {
      "nama": "Citra",
      "absen": "03",
      "alamat": "Jl. Melati No. 7",
      "foto": "assets/citra.png",
    },
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Nama Kelompok"),
        centerTitle: true,
        backgroundColor: Colors.teal,
      ),
      body: Column(
        children: [
          const SizedBox(height: 10),
          Expanded(
            child: ListView.builder(
              itemCount: anggota.length,
              itemBuilder: (context, index) {
                final item = anggota[index];
                return Card(
                  margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(12),
                  ),
                  elevation: 3,
                  child: ListTile(
                    leading: CircleAvatar(
                      radius: 28,
                      backgroundImage: AssetImage(item["foto"]!),
                    ),
                    title: Text(
                      "${item['nama']} (Absen: ${item['absen']})",
                      style: const TextStyle(fontWeight: FontWeight.bold),
                    ),
                    subtitle: Text(item["alamat"]!),
                  ),
                );
              },
            ),
          ),
          const SizedBox(height: 10),
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: ElevatedButton(
              style: ElevatedButton.styleFrom(
                minimumSize: const Size(double.infinity, 50),
                backgroundColor: Colors.teal,
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(12),
                ),
              ),
              onPressed: () {
                showDialog(
                  context: context,
                  builder: (context) => AlertDialog(
                    title: const Text("Pesan"),
                    content: const Text("Terima Kasih!"),
                    actions: [
                      TextButton(
                        onPressed: () => Navigator.pop(context),
                        child: const Text("OK"),
                      ),
                    ],
                  ),
                );
              },
              child: const Text(
                "Tampilkan Pesan",
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
              ),
            ),
          )
        ],
      ),
    );
  }
}

Menambahkan Gambar ke Aplikasi

Agar foto anggota kelompok bisa tampil, langkahnya:

  1. Buat folder assets/ di project Flutter.

  2. Simpan foto anggota (misalnya andi.png, budi.png, citra.png) di dalam folder tersebut.

  3. Edit file pubspec.yaml, lalu tambahkan:

    flutter:
      assets:
        - assets/andi.png
        - assets/budi.png
        - assets/citra.png
    

Dengan begitu, Flutter akan mengenali file gambar yang kita simpan.

Hasil Akhir

  • Tampilan bagian atas menampilkan judul kelompok.

  • Bagian tengah berupa list anggota dengan foto, nama, absen, dan alamat.

  • Bagian bawah ada tombol yang ketika ditekan akan memunculkan popup pesan terimakasih.

Latihan sederhana ini membantu saya memahami dasar-dasar UI di Flutter, serta bagaimana menghubungkan data dengan tampilan. Ke depannya, latihan bisa dikembangkan menjadi aplikasi yang lebih kompleks, seperti menambahkan database, fitur pencarian anggota, atau bahkan menampilkan data dari server.


👉 Latihan ini bisa langsung dicoba di zapp.run, sehingga pemula tidak perlu repot menginstal Android Studio. Ikuti link ini untuk melihat demo yg sudah saya buat : https://ztpi0651tpj0.zapp.page/#/


Salam : Redaksi

Share:

Membuat Tampilan Login Modern dengan Flutter (Penjelasan untuk Pemula)

Membuat Tampilan Login Modern dengan Flutter (Penjelasan untuk Pemula)

Link Demo Aplikasi : https://zbjy06vobjz0.zapp.page/#/

Hari ini saya belajar bagaimana membuat tampilan Login Page modern menggunakan Flutter. Tujuannya adalah agar saya paham bagaimana memanfaatkan komponen dasar Flutter seperti Text, Image, Button, Input, Card, List, Media, dan Navigation.

Saya mencoba meniru desain login yang umum: ada ilustrasi gambar di atas, form input username dan password, tombol login, tombol sosial media (Google dan Facebook), serta link “Forget password” dan “Sign up”.


1. Kode Flutter (main.dart)

Berikut contoh kode sederhana untuk membuat tampilan login di Flutter (cocok dijalankan di zapp.run):

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Login UI',
      debugShowCheckedModeBanner: false,
      home: const LoginPage(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 60),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              // Gambar ilustrasi
              SizedBox(
                height: 180,
                child: Image.network(
                  "https://cdn-icons-png.flaticon.com/512/5234/5234843.png",
                  fit: BoxFit.contain,
                ),
              ),
              const SizedBox(height: 20),

              // Judul
              const Text(
                "Sign In",
                style: TextStyle(
                  fontSize: 28,
                  fontWeight: FontWeight.bold,
                ),
              ),
              const SizedBox(height: 8),
              const Text(
                "Enter valid user name & password to continue",
                textAlign: TextAlign.center,
                style: TextStyle(color: Colors.grey),
              ),
              const SizedBox(height: 30),

              // Input Username
              TextField(
                decoration: InputDecoration(
                  prefixIcon: const Icon(Icons.person_outline),
                  hintText: "User name",
                  border: OutlineInputBorder(
                    borderRadius: BorderRadius.circular(12),
                  ),
                ),
              ),
              const SizedBox(height: 15),

              // Input Password
              TextField(
                obscureText: true,
                decoration: InputDecoration(
                  prefixIcon: const Icon(Icons.lock_outline),
                  hintText: "Password",
                  border: OutlineInputBorder(
                    borderRadius: BorderRadius.circular(12),
                  ),
                ),
              ),

              // Forget Password
              Align(
                alignment: Alignment.centerRight,
                child: TextButton(
                  onPressed: () {},
                  child: const Text(
                    "Forget password",
                    style: TextStyle(color: Colors.blue),
                  ),
                ),
              ),
              const SizedBox(height: 10),

              // Tombol Login
              SizedBox(
                width: double.infinity,
                height: 50,
                child: ElevatedButton(
                  style: ElevatedButton.styleFrom(
                    backgroundColor: Colors.blue,
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(12),
                    ),
                  ),
                  onPressed: () {},
                  child: const Text(
                    "Login",
                    style: TextStyle(fontSize: 18, color: Colors.white),
                  ),
                ),
              ),
              const SizedBox(height: 20),

              const Text("Or Continue with"),
              const SizedBox(height: 20),

              // Tombol Sosial Media
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  _socialButton("Google", "https://cdn-icons-png.flaticon.com/512/281/281764.png"),
                  const SizedBox(width: 20),
                  _socialButton("Facebook", "https://cdn-icons-png.flaticon.com/512/5968/5968764.png"),
                ],
              ),
              const SizedBox(height: 30),

              // Link Sign up
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  const Text("Haven't any account? "),
                  GestureDetector(
                    onTap: () {},
                    child: const Text(
                      "Sign up",
                      style: TextStyle(color: Colors.blue, fontWeight: FontWeight.bold),
                    ),
                  ),
                ],
              )
            ],
          ),
        ),
      ),
    );
  }

  // Widget tombol sosial media
  Widget _socialButton(String text, String imageUrl) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
      decoration: BoxDecoration(
        border: Border.all(color: Colors.grey.shade300),
        borderRadius: BorderRadius.circular(12),
      ),
      child: Row(
        children: [
          Image.network(imageUrl, height: 20),
          const SizedBox(width: 8),
          Text(text),
        ],
      ),
    );
  }
}

2. Penjelasan Komponen UI

Agar tidak bingung, saya pelajari bahwa komponen UI di Flutter bisa dikelompokkan seperti ini:

  1. Text (Tulisan)

    • "Sign In", "Enter valid user name...", "Forget password", "Sign up".

    • Flutter: Text()

  2. Image (Gambar)

    • Ilustrasi orang di atas.

    • Logo Google & Facebook di tombol bawah.

    • Flutter: Image.network() atau Image.asset()

  3. Button (Tombol)

    • Tombol Login (biru).

    • Tombol Google & Facebook.

    • Forget password (link).

    • Flutter: ElevatedButton, TextButton, OutlinedButton.

  4. Input (Form)

    • User name (TextField dengan ikon orang).

    • Password (TextField dengan ikon gembok).

    • Flutter: TextField() atau TextFormField().

  5. Card

    • Tidak ada card khusus, tapi tombol sosial media bisa dibuat mirip card dengan Container.

  6. List

    • Tidak ada daftar data pada halaman login.

  7. Media

    • Hanya gambar (ilustrasi dan logo sosial media).

  8. Navigation

    • "Sign up" untuk ke halaman registrasi.

    • "Forget password" untuk ke halaman reset password.

    • Flutter: Navigator.push()


3. Kesimpulan

Dengan membuat halaman login sederhana ini, saya jadi lebih paham bahwa:

  • Text menampilkan informasi.

  • Image memberikan ilustrasi/ikon.

  • Button adalah interaksi utama (login, sign up, dll).

  • Input menerima data dari user.

  • Navigation memungkinkan pindah halaman.

Halaman login sederhana ini bisa menjadi latihan dasar Flutter sebelum membuat aplikasi yang lebih kompleks seperti CRUD, Absensi, atau E-Commerce.


👉 Jadi, buat kamu yang pemula juga, cobalah jalankan kode ini di zapp.run atau Android Studio, lalu perhatikan setiap komponen yang digunakan. Dengan latihan ini, kamu akan semakin familiar dengan bahasa Dart & framework Flutter.


Postingan saya selanjutnya coba bikin juga gambar mockup dengan tanda panah (highlight bagian Text, Image, Button, Input, dll).

Share:

Dokumentasi dan Presentasi Aplikasi (Konteks: Proyek Mobile Apps dengan Flutter/Android)

Alur Pembelajaran & Materi Lengkap

Tema: Dokumentasi dan Presentasi Aplikasi

(Konteks: Proyek Mobile Apps dengan Flutter/Android)


1. Pendahuluan

  • Tujuan Pembelajaran:

    1. Peserta didik mampu membuat dokumentasi teknis aplikasi (ReadMe, diagram alur, skema basis data).

    2. Peserta didik memahami struktur folder proyek Flutter/Android.

    3. Peserta didik dapat membuat presentasi aplikasi (PowerPoint, Canva, atau Pitch Deck).

    4. Peserta didik mampu melakukan simulasi presentasi aplikasi kepada guru/tim.

  • Kegiatan Awal:

    • Apersepsi: "Mengapa dokumentasi penting dalam pengembangan aplikasi?"

    • Diskusi singkat: contoh dokumentasi aplikasi populer (misalnya dokumentasi di GitHub).

    • Menyampaikan peta pembelajaran hari ini.


2. Materi & Tahapan Pembelajaran

A. Penulisan Dokumentasi Teknis

  1. ReadMe Project

    • Isi yang umum ada di README.md:

      • Judul aplikasi

      • Deskripsi singkat

      • Fitur utama

      • Cara instalasi & menjalankan

      • Teknologi yang digunakan

      • Screenshot aplikasi

      • Kontak/kontributor

    Contoh format README sederhana:

    # Aplikasi Absensi Siswa
    Aplikasi mobile untuk absensi siswa berbasis Flutter & SQLite.
    
    ## Fitur
    - Input data siswa
    - Absensi harian (Hadir, Izin, Sakit, Alpha)
    - Rekap bulanan
    - Export ke Excel/PDF
    
    ## Instalasi
    1. Clone repositori
    2. Jalankan `flutter pub get`
    3. Run dengan `flutter run`
    
    ## Teknologi
    - Flutter
    - SQLite
    - Dart
    
    ## Screenshot
    ![Tampilan Login](screenshot/login.png)
    
  2. Diagram Alur (Flowchart)

    • Tools: Draw.io, Lucidchart, Figma.

    • Buat flow aplikasi, misalnya:

      • User Login → Menu Utama → Input Data → Simpan ke Database → Tampil Rekap.

  3. Skema Basis Data

    • Jika SQLite/Firebase, tunjukkan tabel/collection:

      • Tabel Siswa: id, nama, kelas

      • Tabel Absensi: id, siswa_id, tanggal, status

    • Tools: dbdiagram.io, MySQL Workbench, atau draw manual.


B. Struktur Folder Proyek

  1. Flutter

    • Struktur umum:

      lib/
        main.dart
        screens/
        widgets/
        models/
        services/
      assets/
        images/
        fonts/
      pubspec.yaml
      
    • Penjelasan singkat tiap folder.

      • lib/ → kode utama

      • screens/ → tampilan halaman

      • models/ → struktur data

      • services/ → API/database

      • assets/ → gambar/font

  2. Android (Java/Kotlin)

    app/src/main/
      java/com/example/app/   -> kode sumber
      res/                    -> resource (layout, drawable, values)
      AndroidManifest.xml
    
    • Penjelasan fungsi folder res/layout (UI XML), res/drawable (gambar), res/values (warna/string).


C. Pembuatan Presentasi Aplikasi

  1. Media Presentasi

    • PowerPoint / Canva / Google Slide.

    • Format Pitch Deck sederhana:

      1. Judul & Logo Aplikasi

      2. Latar Belakang Masalah

      3. Solusi (Aplikasi yang dibuat)

      4. Fitur Utama

      5. Demo/Tampilan Aplikasi

      6. Arsitektur & Database

      7. Tim Pengembang

      8. Penutup & Kontak

  2. Tips Desain

    • Gunakan warna konsisten

    • Jangan terlalu banyak teks

    • Perbanyak gambar/screenshot aplikasi


D. Simulasi Presentasi Hasil Aplikasi

  1. Kegiatan

    • Siswa dibagi per kelompok/proyek.

    • Setiap kelompok membuat slide sesuai template pitch deck.

    • Mempresentasikan 10 menit (7 menit presentasi + 3 menit tanya jawab).

  2. Peran Guru/Tim

    • Menjadi evaluator & audiens.

    • Memberikan feedback (isi, desain, cara penyampaian).

  3. Rubrik Penilaian

    • Dokumentasi Teknis (30%)

    • Struktur Folder Jelas (20%)

    • Desain Presentasi (20%)

    • Kemampuan Presentasi (30%)


3. Penutup

  • Refleksi: Apa tantangan menulis dokumentasi? Apa pengalaman saat presentasi aplikasi?

  • Tugas Lanjutan:

    • Upload proyek ke GitHub lengkap dengan README.md.

    • Kirim file presentasi (PPT/Canva).


4. LKPD / Praktikum

  • Langkah Praktikum

    1. Buat file README.md pada project masing-masing.

    2. Gambarkan flowchart aplikasi.

    3. Buat skema basis data.

    4. Jelaskan struktur folder proyek.

    5. Buat slide presentasi sesuai template pitch deck.

    6. Lakukan simulasi presentasi di kelas.


Berminat saya buatkan versi dokumen Word (LKPD + Materi Teori) yang siap dibagikan ke siswa, atau versi PPT template pitch deck juga sekalian?

Share:

36 Ide Aplikasi Mobile Sederhana untuk Siswa SMK (Lengkap dengan Tingkat Kesulitan)

36 Ide Aplikasi Mobile Sederhana untuk Siswa SMK (Lengkap dengan Tingkat Kesulitan)

Belajar pemrograman mobile akan lebih menarik jika langsung dipraktikkan dalam bentuk proyek nyata. Bagi siswa SMK, khususnya jurusan PPLG/RPL, membuat aplikasi sederhana adalah langkah awal untuk memahami konsep pemrograman mobile menggunakan Dart Flutter.

Dalam artikel ini, saya rangkum 36 ide aplikasi mobile sederhana yang bisa dijadikan latihan proyek siswa, lengkap dengan pemetaan tingkat kesulitan (Mudah – Sedang – Menengah).


Mengapa Perlu Proyek Aplikasi?

Belajar pemrograman tidak cukup hanya teori. Dengan mengerjakan proyek kecil, siswa akan:
✅ Memahami konsep CRUD (Create, Read, Update, Delete)
✅ Mengenal komponen UI Flutter (Text, Button, List, Form, dsb.)
✅ Belajar manajemen data (SQLite/Hive, JSON, atau API sederhana)
✅ Terbiasa membuat aplikasi nyata sesuai kebutuhan sehari-hari


36 Ide Aplikasi Mobile Sederhana

🔹 Level Mudah (Dasar – CRUD sederhana, ListView, Input/Output)

Cocok untuk siswa yang baru mulai belajar Flutter/Dart.

  1. Kalkulator Sederhana

  2. Kalkulator BMI

  3. Catatan Harian / To-Do List

  4. Daftar Menu Kantin

  5. Jadwal Pelajaran

  6. Catatan Belanja

  7. Kalkulator Diskon

  8. Catatan Tabungan

  9. Penghitung Langkah (Manual)

  10. Catatan Olahraga


🔹 Level Sedang (CRUD + SQLite/Hive, Form Input, sedikit logika tambahan)

Cocok untuk siswa yang sudah menguasai dasar widget & state.
11. Manajemen Uang Saku
12. Absensi Siswa/Pegawai Offline
13. Buku Tamu Digital
14. Kamus Mini Indo–Inggris
15. Catatan Hutang
16. Reminder Tugas Sekolah
17. Catatan Kesehatan
18. Perpustakaan Mini
19. Jadwal Piketan Kelas
20. Aplikasi Pencucian Mobil/Motor


🔹 Level Menengah (Integrasi lebih kompleks: Notifikasi, Kalender, Audio, Gambar, Chart)

Cocok untuk siswa yang ingin tantangan lebih dan menyiapkan portofolio.
21. Pengingat Ibadah/Jadwal Sholat (dengan notifikasi)
22. Reminder Minum Air (alarm berkala)
23. Aplikasi Alarm Sederhana
24. Aplikasi Belajar Huruf Hijaiyah (gambar + audio)
25. Aplikasi Galeri Foto (Image Picker)
26. Pemesanan Tiket Fiktif (form kompleks)
27. Donasi Sederhana (dengan list kegiatan)
28. Kalender Agenda
29. Stopwatch
30. Quiz Sederhana (pilihan ganda)
31. Voting Kelas (grafik/chart sederhana)
32. Pengingat Ulang Tahun (notifikasi)
33. Catatan Perjalanan (opsional Map)
34. Resep Masakan
35. Resep Kue
36. Catatan Kesehatan Harian (fitur lebih detail)


Bagaimana Menggunakan Ide Ini?

Untuk pembelajaran di kelas, guru bisa membagi proyek ke dalam 3 tahap:

  • Tahap 1 (Pemula): Pilih 1 aplikasi level mudah → melatih dasar CRUD & UI.

  • Tahap 2 (Menengah): Pilih 1 aplikasi level sedang → belajar database & manajemen data.

  • Tahap 3 (Lanjutan): Pilih 1 aplikasi level menengah → belajar integrasi fitur tambahan (notifikasi, kalender, audio, grafik).

Dengan alur ini, siswa tidak hanya belajar coding, tetapi juga memiliki hasil nyata berupa aplikasi yang bisa dipresentasikan atau dijadikan portofolio.


Penutup

Dari 36 ide aplikasi mobile sederhana di atas, siswa bisa memilih sesuai minat dan kemampuan. Guru pun bisa menggunakannya sebagai referensi tugas proyek atau ujian praktik. Dengan latihan bertahap, siswa akan terbiasa menghadapi tantangan nyata dalam dunia pemrograman mobile.

👉 Kalau Anda guru atau siswa SMK yang sedang belajar Flutter, coba pilih salah satu ide di atas, lalu kembangkan sesuai kreativitas Anda. Siapa tahu, dari proyek sederhana bisa berkembang jadi aplikasi bermanfaat bagi banyak orang.


Kalau untuk latihan siswa, 36 ide aplikasi sederhana bisa kita kelompokkan biar lebih mudah dipilih. Saya buatkan dalam tabel:


📱 36 Ide Aplikasi Mobile Sederhana

No Nama Aplikasi Fitur Utama Teknologi/Komponen yang Dipakai
1 Catatan Harian / To-Do List Tambah, edit, hapus catatan CRUD, ListView
2 Pengingat Ibadah / Jadwal Sholat Input jadwal + notifikasi Local DB, Notifikasi
3 Manajemen Uang Saku Catat pemasukan & pengeluaran SQLite / Hive
4 Absensi Siswa / Pegawai Offline Input siswa + status kehadiran SQLite, Dropdown
5 Daftar Menu Kantin List menu + harga ListView, CRUD
6 Kalkulator BMI Hitung BMI dari tinggi & berat Form Input
7 Kamus Mini Indo–Inggris Input kata → arti SQLite / JSON lokal
8 Jadwal Pelajaran Tampilkan Senin–Jumat TabBar, List
9 Buku Tamu Digital Input nama, tujuan, waktu SQLite
10 Kalkulator Sederhana Operasi + - × ÷ OnPressed Button
11 Aplikasi Catatan Hutang Input hutang & status lunas CRUD
12 Reminder Minum Air Notifikasi tiap jam Timer, Notifikasi
13 Aplikasi Resep Masakan List resep + cara masak ListView, Detail Page
14 Daftar Kontak Tambah, edit, hapus kontak CRUD, SQLite
15 Aplikasi Alarm Sederhana Set alarm bangun Time Picker
16 Aplikasi Belajar Huruf Hijaiyah Tampilan huruf + suara Asset gambar & audio
17 Aplikasi Catatan Belanja Daftar barang belanja Checkbox List
18 Aplikasi Galeri Foto Menyimpan & menampilkan foto Image Picker
19 Aplikasi Pemesanan Tiket Fiktif Input nama & jadwal Form Input
20 Aplikasi Reminder Tugas Sekolah Tambah tugas + deadline CRUD, Date Picker
21 Aplikasi Donasi Sederhana List kegiatan donasi CRUD, ListView
22 Aplikasi Kalender Agenda Tambah agenda ke kalender Calendar View
23 Aplikasi Stopwatch Start, pause, reset Timer
24 Aplikasi Penghitung Langkah (Manual) Tambah langkah manual Counter
25 Aplikasi Catatan Kesehatan Catat tekanan darah, gula CRUD
26 Aplikasi Quiz Sederhana Soal pilihan ganda Radio Button, Score
27 Aplikasi Voting Kelas Pilih ketua kelas CRUD, Chart
28 Aplikasi Pengingat Ulang Tahun Input nama + tanggal lahir Notifikasi, Date Picker
29 Aplikasi Catatan Perjalanan Tambah lokasi perjalanan CRUD, Map optional
30 Aplikasi Resep Kue List bahan + langkah ListView, Image
31 Aplikasi Perpustakaan Mini Input judul buku + status pinjam SQLite
32 Aplikasi Kalkulator Diskon Input harga + persentase Form Input
33 Aplikasi Catatan Tabungan Catat jumlah tabungan harian CRUD
34 Aplikasi Catatan Olahraga Input jenis olahraga + durasi CRUD
35 Aplikasi Jadwal Piketan Kelas Daftar piket harian CRUD, ListView
36 Aplikasi Pencucian Mobil/Motor Catat kendaraan masuk, harga, waktu CRUD, SQLite

Jadi total ada 36 ide aplikasi sederhana ✅
Ini bisa dipakai untuk latihan siswa, mulai dari CRUD dasar sampai fitur tambahan seperti notifikasi, kalender, audio, gambar, dan grafik.

👉 Coba saja dulu tingkat kesulitan (mudah – sedang – menengah) biar siswa bisa pilih sesuai levelnya.

Share:

Belajar Menguasai Microsoft Word: Praktikum Font dan Paragraph

Belajar Menguasai Microsoft Word: Praktikum Font dan Paragraph

Microsoft Word merupakan salah satu aplikasi pengolah kata yang paling banyak digunakan di dunia pendidikan maupun dunia kerja. Hampir setiap pekerjaan administrasi, penyusunan laporan, surat-menyurat, hingga pembuatan dokumen resmi membutuhkan keterampilan mengoperasikan Microsoft Word. Oleh karena itu, penting bagi peserta didik untuk memiliki kemampuan dasar dalam mengatur teks dan paragraf agar dokumen terlihat rapi, profesional, dan mudah dibaca.

Praktikum: Mengenal Font dan Paragraph

Dalam pembelajaran kali ini, siswa diajak untuk mengenal lebih dalam dua fitur utama pada Microsoft Word, yaitu Ribbon Font dan Ribbon Paragraph.

  • Ribbon Font berisi kumpulan menu yang digunakan untuk memodifikasi tampilan teks, seperti jenis huruf, ukuran, warna, penebalan (bold), garis bawah (underline), maupun miring (italic). Dengan fitur ini, siswa dapat membuat tulisan yang lebih menarik dan sesuai kebutuhan.

  • Ribbon Paragraph menyediakan berbagai pengaturan tata letak paragraf, mulai dari perataan teks (align), spasi antar baris, indentasi, numbering (penomoran), hingga bullet. Pengaturan ini sangat penting agar dokumen yang dibuat terlihat terstruktur dan profesional.

Langkah Praktikum

Dalam kegiatan praktikum ini, siswa diberikan beberapa tugas untuk melatih keterampilan dasar:

  1. Mengatur Font
    Siswa diminta mengetik teks sederhana, lalu memodifikasinya dengan berbagai gaya font. Misalnya, mengubah ukuran huruf, warna, jenis font, menebalkan teks, memberi garis bawah, dan sebagainya.

  2. Mengatur Paragraph
    Siswa kemudian berlatih mengatur tata letak paragraf, mulai dari justify (rata kiri–kanan), membuat indentasi, mengatur jarak antar baris, hingga memberikan penomoran (numbering) pada daftar teks.

Dengan latihan ini, siswa dapat melihat langsung perbedaan hasil dokumen ketika pengaturan font dan paragraph digunakan dengan benar.

Refleksi Belajar

Melalui praktikum ini, siswa diajak untuk merenungkan pertanyaan penting:

  • Apa perbedaan antara pengaturan Font dan Paragraph?

  • Mengapa pengaturan paragraf penting dalam sebuah dokumen resmi?

Refleksi ini membantu siswa menyadari bahwa penggunaan fitur Word bukan sekadar hiasan, melainkan kebutuhan agar dokumen mudah dibaca dan memenuhi standar profesional.

Kesimpulan

Praktikum Microsoft Word dengan fokus pada Font dan Paragraph memberikan pengalaman langsung kepada siswa dalam mengolah teks dan paragraf. Keterampilan ini sangat bermanfaat, tidak hanya dalam menyelesaikan tugas sekolah, tetapi juga sebagai bekal penting ketika mereka terjun ke dunia kerja.

Dengan menguasai pengaturan font dan paragraph, siswa diharapkan mampu menghasilkan dokumen yang rapi, menarik, dan profesional.


👉 Salam : Redaksi

Share:

Belajar Pemrograman Mobile: Mendesain UI/UX dengan Figma

Belajar Pemrograman Mobile: Mendesain UI/UX dengan Figma


Halo teman-teman! 👋

Hari ini saya ingin berbagi pengalaman saya dalam perjalanan mempelajari pemrograman mobile, khususnya di tahap desain UI/UX.

Sebelum masuk ke coding, salah satu langkah penting dalam pengembangan aplikasi adalah membuat desain antarmuka (UI) dan pengalaman pengguna (UX). Untuk itu, saya menggunakan Figma, sebuah tools desain yang sangat populer dan mudah digunakan, baik untuk pemula maupun profesional.

Proyek Hari Ini: Desain Aplikasi Facebook

Sebagai latihan, saya mencoba membuat tiga tampilan UI/UX sederhana dari aplikasi Facebook, yaitu:

  1. Tampilan Login

    • Berisi logo Facebook, kolom email/nomor telepon, kolom password, tombol login, serta opsi lupa kata sandi.

    • Fokus utama ada pada kesederhanaan dan kemudahan akses agar pengguna tidak kebingungan ketika ingin masuk.

  2. Tampilan Registrasi (Sign Up)

    • Menyediakan form pendaftaran dengan input nama lengkap, email, password, dan tanggal lahir.

    • Desainnya saya buat dengan warna khas Facebook: biru dan putih, agar tetap konsisten dengan identitas brand.

  3. Tampilan Beranda (Feed)

    • Menampilkan postingan sederhana, foto profil, kolom untuk menulis status, serta menu navigasi di bawah.

    • Tujuannya agar pengguna merasa familiar dengan tampilan media sosial yang sering digunakan sehari-hari.

Apa yang Saya Pelajari?

  • Konsistensi desain itu penting, baik dari segi warna, font, maupun tata letak.

  • User-friendly menjadi prioritas utama dalam membuat aplikasi. Desain yang terlalu rumit justru bisa membuat pengguna bingung.

  • Dengan Figma, saya bisa bekerja lebih cepat karena banyak tersedia template dan komponen siap pakai.

Kesimpulan

Belajar UI/UX bukan hanya soal keindahan tampilan, tapi juga bagaimana aplikasi itu bisa digunakan dengan nyaman oleh pengguna. Latihan membuat desain Facebook ini membuat saya semakin paham bagaimana aplikasi besar bisa memiliki tampilan sederhana namun efektif.

Postingan selanjutnya saya berencana untuk melanjutkan ke desain halaman lain dan mungkin mencoba membuat prototype agar bisa lebih interaktif.


Salam Redaksi

Share:

Belajar REST API dengan Flutter: Konsep, Ilustrasi, Praktik, hingga Menghadapi Error

Belajar REST API dengan Flutter: Konsep, Ilustrasi, Praktik, hingga Menghadapi Error

Halo teman-teman, di postingan kali ini saya ingin berbagi pengalaman belajar REST API bersama siswa menggunakan pendekatan yang sederhana namun aplikatif. Materi ini saya angkat dari obrolan dan praktik langsung saat mencoba membuat aplikasi kecil dengan Dart Flutter melalui platform online zapp.run.

Apa itu REST API?

REST API (Representational State Transfer – Application Programming Interface) adalah cara sebuah aplikasi berkomunikasi dengan aplikasi lain melalui internet.
Singkatnya, REST API seperti jembatan penghubung agar aplikasi kita bisa mengambil atau mengirim data ke server.

Metode HTTP yang sering digunakan:

  1. GET → mengambil data dari server.

  2. POST → mengirim data baru ke server.

  3. PUT → memperbarui data yang sudah ada.

  4. DELETE → menghapus data dari server.

Ilustrasi Sederhana: Warung Makan

Agar siswa mudah paham, saya menggunakan analogi warung makan:

  • GET = pembeli meminta daftar menu.

  • POST = pembeli memesan makanan baru.

  • PUT = pembeli mengganti pesanan yang salah.

  • DELETE = pembeli membatalkan pesanan.

Dengan ilustrasi ini, siswa lebih cepat mengingat peran masing-masing metode HTTP.

Mencoba REST API dengan Flutter

Setelah paham konsep, kami lanjut mencoba di Flutter (via zapp.run).
Aplikasi ini sederhana: menampilkan data dari server palsu (dummy API) dan memungkinkan pengguna menambahkan data.

File pubspec.yaml

Kami menambahkan dependency http agar bisa melakukan request ke server.

name: rest_api_demo
description: A simple REST API demo with Flutter
publish_to: 'none'
version: 1.0.0+1

environment:
  sdk: ">=2.17.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  http: ^0.13.5

dev_dependencies:
  flutter_test:
    sdk: flutter

File main.dart

Kami menuliskan kode dasar Flutter untuk melakukan request API dan menampilkan data di layar.

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'REST API Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: ApiDemoPage(),
    );
  }
}

class ApiDemoPage extends StatefulWidget {
  @override
  _ApiDemoPageState createState() => _ApiDemoPageState();
}

class _ApiDemoPageState extends State<ApiDemoPage> {
  List data = [];

  Future<void> fetchData() async {
    final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'));
    if (response.statusCode == 200) {
      setState(() {
        data = json.decode(response.body);
      });
    } else {
      throw Exception('Gagal mengambil data');
    }
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("REST API Demo")),
      body: data.isEmpty
          ? Center(child: CircularProgressIndicator())
          : ListView.builder(
              itemCount: data.length,
              itemBuilder: (context, index) {
                return ListTile(
                  title: Text(data[index]['title']),
                  subtitle: Text(data[index]['body']),
                );
              },
            ),
    );
  }
}

Menghadapi Error

Namanya belajar coding, tentu ada saja error. Berikut beberapa error yang muncul saat praktik:

  1. Error Null Safety pada Timer

    Error: Method 'cancel' cannot be called on 'Timer?' because it is potentially null.
    Try calling using ?. instead.
    

    ✅ Solusi: tambahkan ? agar sesuai dengan sistem null safety:

    gameTimer?.cancel();
    
  2. Error pada DateTime nullable

    Error: The argument type 'DateTime?' can't be assigned to the parameter type 'DateTime'
    

    ✅ Solusi: gunakan operator ! untuk memastikan data tidak null:

    TimeOfDay.fromDateTime(selectedTime!)
    
  3. Error getter ‘width’ bukan milik double (saat mencoba game lain di Flutter).
    ✅ Solusi: pastikan variabel didefinisikan dengan benar dan bukan tipe double ketika ingin akses .width.

Dengan adanya error ini, siswa belajar debugging: tidak hanya menyalin kode, tetapi juga paham bagaimana memperbaiki masalah sesuai pesan error.

Kesimpulan

Belajar REST API tidak harus rumit. Dengan:

  • Ilustrasi sederhana (seperti warung makan),

  • Praktik langsung di Flutter,

  • Menghadapi error nyata,

siswa bisa memahami alur komunikasi aplikasi dengan server sekaligus mengasah keterampilan problem solving.


✍️ Tulisan ini dibuat berdasarkan pengalaman praktik REST API dengan Flutter di zapp.run, lengkap dengan error-error yang muncul dan solusinya.


mau coba aplikasi yg sudah jadi ?
ikuti link ini : https://z313y06jq313z.zapp.page/#/

Share:

Membuat Desain Halaman Login Mobile dengan Flutter di Web Flutter Zapp.run

Membuat Desain Halaman Login Mobile dengan Flutter di Web Flutter Zapp.run


Pendahuluan

Pada era digital saat ini, hampir semua aplikasi mobile memerlukan fitur login sebagai pintu masuk bagi pengguna. Desain halaman login yang menarik dan responsif akan meningkatkan pengalaman pengguna serta memberikan kesan profesional pada aplikasi yang kita buat.

Dalam artikel ini, kita akan belajar membuat desain halaman login mobile menggunakan Dart Flutter, yang dapat dijalankan langsung di web Flutter Zapp.run tanpa perlu instalasi rumit.


Tujuan Pembelajaran

  1. Memahami cara membuat tampilan halaman login di Flutter.

  2. Menggunakan Widget Flutter seperti Scaffold, AppBar, TextField, dan ElevatedButton.

  3. Mendesain tampilan login agar menarik dan responsif.


Fitur Desain Halaman Login

Tampilan login yang akan kita buat memiliki:

  • AppBar dengan judul Login.

  • Background berwarna hijau untuk memberikan nuansa segar.

  • Lingkaran dengan ikon pengguna di bagian atas form login.

  • Kolom Username dan Password dengan ikon di dalamnya.

  • Tombol Sign In berwarna oranye.

  • Tautan Forgot Password?


Kode Program Lengkap

Berikut adalah kode Dart Flutter untuk membuat desain halaman login seperti contoh:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: const LoginPage(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Login"),
        backgroundColor: Colors.blue,
      ),
      backgroundColor: Colors.teal[600],
      body: Center(
        child: SingleChildScrollView(
          child: Column(
            children: [
              // Lingkaran dengan icon user
              Stack(
                alignment: Alignment.center,
                children: [
                  Container(
                    width: 100,
                    height: 100,
                    decoration: const BoxDecoration(
                      color: Colors.white,
                      shape: BoxShape.circle,
                    ),
                  ),
                  const Icon(
                    Icons.person,
                    size: 60,
                    color: Colors.grey,
                  ),
                ],
              ),
              const SizedBox(height: 20),

              // Card form login
              Container(
                margin: const EdgeInsets.symmetric(horizontal: 24),
                padding: const EdgeInsets.all(20),
                decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(8),
                  boxShadow: [
                    BoxShadow(
                      color: Colors.black12,
                      blurRadius: 6,
                      offset: Offset(0, 2),
                    ),
                  ],
                ),
                child: Column(
                  children: [
                    // Username field
                    TextField(
                      decoration: InputDecoration(
                        prefixIcon: Icon(Icons.person_outline),
                        labelText: 'User Name',
                        border: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(8),
                        ),
                      ),
                    ),
                    const SizedBox(height: 16),

                    // Password field
                    TextField(
                      obscureText: true,
                      decoration: InputDecoration(
                        prefixIcon: Icon(Icons.lock_outline),
                        suffixIcon: Icon(Icons.visibility),
                        labelText: 'Password',
                        border: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(8),
                        ),
                      ),
                    ),

                    const SizedBox(height: 8),

                    // Forgot Password link
                    Align(
                      alignment: Alignment.centerRight,
                      child: TextButton(
                        onPressed: () {},
                        child: const Text(
                          "Forgot Password?",
                          style: TextStyle(color: Colors.grey),
                        ),
                      ),
                    ),

                    const SizedBox(height: 8),

                    // Tombol Sign In
                    SizedBox(
                      width: double.infinity,
                      child: ElevatedButton(
                        style: ElevatedButton.styleFrom(
                          backgroundColor: Colors.orange,
                          padding: const EdgeInsets.symmetric(vertical: 14),
                        ),
                        onPressed: () {},
                        child: const Text(
                          "Sign in",
                          style: TextStyle(color: Colors.white, fontSize: 16),
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Cara Menjalankan di Web Flutter Zapp.run

  1. Buka situs https://zapp.run.

  2. Pilih template Flutter.

  3. Hapus kode bawaan dan tempelkan kode di atas.

  4. Klik tombol Run untuk menjalankan program.

  5. Untuk mencoba hasil dari postingan ini ikuti link berikut : 
    https://zy3406gay350.zapp.page/#/


Penutup

Dengan mengikuti tutorial ini, kita telah berhasil membuat halaman login yang menarik menggunakan Flutter. Desain seperti ini dapat menjadi dasar untuk pengembangan fitur autentikasi yang lebih kompleks, seperti validasi input, koneksi ke database, atau integrasi dengan API.

Anda bisa memodifikasi warna, ikon, maupun layout sesuai kebutuhan agar tampilan lebih sesuai dengan tema aplikasi yang sedang dibuat.


Saran pengembangan lanjutan, bisa buat versi dengan animasi fade-in dan smooth transition supaya halaman login lebih hidup untuk diposting di blog Anda.

Share:

Flutter Web REST API — Materi & Proyek (main.dart + panduan)).

 Flutter Web REST API — Materi & Proyek (main.dart + panduan)).

 Aktifitas Pembelajaran:

• Rencana pembelajaran (tujuan, alur 7JP, penilaian, aktivitas nyata)
• Petunjuk singkat cara pakai di zapp.run (tambahkan http: di pubspec.yaml)
• Kode lengkap lib/main.dart — aplikasi ToDo Manager (Flutter Web) yang menunjukkan operasi REST API: GET, POST, PUT, DELETE menggunakan JSONPlaceholder (public demo API).

Beberapa hal yang perlu kamu ketahui langsung:

  • JSONPlaceholder cocok untuk praktik karena menerima POST/PUT/DELETE tetapi tidak menyimpan perubahan permanen — bagus untuk belajar konsep HTTP/REST.

  • Untuk membuat proyek jalan di zapp.run: buat project Flutter Web → tambahkan http: ^0.13.6 di pubspec.yaml → copy-paste main.dart dari canvas → run (web).

  • Aplikasi sudah termasuk: tampil list (GET), tambah item (POST), edit (PUT), hapus (DELETE), toggle completed (PUT), refresh (GET).

Saran Pengembangan:

  • tambahkan versi yang terhubung ke Google Sheets / Firebase (nyata & persistensi), atau

  • ubah resource dari JSONPlaceholder ke backend sederhana (contoh Node/Flask) + skrip quick-start?

Berikut Full Codingan:

import 'dart:convert';

import 'package:flutter/foundation.dart';

import 'package:flutter/material.dart';

import 'package:http/http.dart' as http;


void main() {

  runApp(const MyApp());

}


class MyApp extends StatelessWidget {

  const MyApp({super.key});


  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      title: 'REST API Demo - ToDo Manager',

      debugShowCheckedModeBanner: false,

      theme: ThemeData(

        primarySwatch: Colors.indigo,

      ),

      home: const HomePage(),

    );

  }

}


class TodoItem {

  int id;

  String title;

  bool completed;


  TodoItem({required this.id, required this.title, required this.completed});


  factory TodoItem.fromJson(Map<String, dynamic> json) => TodoItem(

        id: json['id'] is int ? json['id'] : int.parse(json['id'].toString()),

        title: json['title'] ?? '',

        completed: json['completed'] ?? false,

      );


  Map<String, dynamic> toJson() => {

        'id': id,

        'title': title,

        'completed': completed,

      };

}


class ApiService {

  // JSONPlaceholder base URL

  static const String baseUrl = 'https://jsonplaceholder.typicode.com';


  // GET: ambil daftar todos (contoh resource)

  static Future<List<TodoItem>> fetchTodos({int limit = 20}) async {

    final url = Uri.parse('$baseUrl/todos?_limit=$limit');

    final res = await http.get(url);

    if (res.statusCode == 200) {

      final List list = jsonDecode(res.body);

      return list.map((e) => TodoItem.fromJson(e)).toList();

    } else {

      throw Exception('Failed to load todos: ${res.statusCode}');

    }

  }


  // POST: buat todo baru

  static Future<TodoItem> createTodo(String title) async {

    final url = Uri.parse('$baseUrl/todos');

    final res = await http.post(url,

        headers: {'Content-Type': 'application/json; charset=UTF-8'},

        body: jsonEncode({'title': title, 'completed': false, 'userId': 1}));

    if (res.statusCode == 201) {

      return TodoItem.fromJson(jsonDecode(res.body));

    } else {

      throw Exception('Failed to create todo: ${res.statusCode}');

    }

  }


  // PUT: update todo

  static Future<TodoItem> updateTodo(TodoItem todo) async {

    final url = Uri.parse('$baseUrl/todos/${todo.id}');

    final res = await http.put(url,

        headers: {'Content-Type': 'application/json; charset=UTF-8'},

        body: jsonEncode(todo.toJson()));

    if (res.statusCode == 200) {

      return TodoItem.fromJson(jsonDecode(res.body));

    } else {

      throw Exception('Failed to update todo: ${res.statusCode}');

    }

  }


  // DELETE: hapus todo

  static Future<bool> deleteTodo(int id) async {

    final url = Uri.parse('$baseUrl/todos/$id');

    final res = await http.delete(url);

    // JSONPlaceholder returns 200 with empty body

    return res.statusCode == 200 || res.statusCode == 204;

  }

}


class HomePage extends StatefulWidget {

  const HomePage({super.key});


  @override

  State<HomePage> createState() => _HomePageState();

}


class _HomePageState extends State<HomePage> {

  late Future<List<TodoItem>> _futureTodos;

  final TextEditingController _newTodoController = TextEditingController();

  List<TodoItem> _todos = [];

  bool _loadingAction = false;


  @override

  void initState() {

    super.initState();

    _futureTodos = ApiService.fetchTodos(limit: 30);

    _futureTodos.then((value) => setState(() => _todos = value)).catchError((e) {

      if (kDebugMode) print(e);

    });

  }


  Future<void> _refresh() async {

    setState(() => _futureTodos = ApiService.fetchTodos(limit: 30));

    final newList = await _futureTodos;

    setState(() => _todos = newList);

  }


  Future<void> _createTodo() async {

    final title = _newTodoController.text.trim();

    if (title.isEmpty) return;

    setState(() => _loadingAction = true);

    try {

      final created = await ApiService.createTodo(title);

      // JSONPlaceholder returns id = 201 etc; append to local list for UX

      setState(() {

        _todos.insert(0, created);

        _newTodoController.clear();

      });

      ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Berhasil membuat todo (POST)')));

    } catch (e) {

      ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Error create: $e')));

    } finally {

      setState(() => _loadingAction = false);

    }

  }


  Future<void> _toggleCompleted(TodoItem todo) async {

    final updated = TodoItem(id: todo.id, title: todo.title, completed: !todo.completed);

    setState(() => _loadingAction = true);

    try {

      final res = await ApiService.updateTodo(updated);

      setState(() {

        final idx = _todos.indexWhere((t) => t.id == todo.id);

        if (idx != -1) _todos[idx] = res;

      });

      ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Berhasil update (PUT)')));

    } catch (e) {

      ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Error update: $e')));

    } finally {

      setState(() => _loadingAction = false);

    }

  }


  Future<void> _editTodoDialog(TodoItem todo) async {

    final controller = TextEditingController(text: todo.title);

    final result = await showDialog<bool>(

      context: context,

      builder: (context) => AlertDialog(

        title: const Text('Edit Todo (PUT)'),

        content: TextField(

          controller: controller,

          decoration: const InputDecoration(labelText: 'Judul'),

        ),

        actions: [

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

          ElevatedButton(onPressed: () => Navigator.pop(context, true), child: const Text('Simpan')),

        ],

      ),

    );


    if (result == true) {

      final newTitle = controller.text.trim();

      if (newTitle.isEmpty) return;

      setState(() => _loadingAction = true);

      try {

        final updated = TodoItem(id: todo.id, title: newTitle, completed: todo.completed);

        final res = await ApiService.updateTodo(updated);

        setState(() {

          final idx = _todos.indexWhere((t) => t.id == todo.id);

          if (idx != -1) _todos[idx] = res;

        });

        ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Berhasil menyimpan perubahan')));

      } catch (e) {

        ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Error save: $e')));

      } finally {

        setState(() => _loadingAction = false);

      }

    }

  }


  Future<void> _deleteTodoConfirm(TodoItem todo) async {

    final yes = await showDialog<bool>(

      context: context,

      builder: (context) => AlertDialog(

        title: const Text('Hapus Todo (DELETE)?'),

        content: Text('Yakin ingin menghapus: "${todo.title}" ?'),

        actions: [

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

          ElevatedButton(onPressed: () => Navigator.pop(context, true), child: const Text('Hapus')),

        ],

      ),

    );


    if (yes == true) {

      setState(() => _loadingAction = true);

      try {

        final ok = await ApiService.deleteTodo(todo.id);

        if (ok) {

          setState(() => _todos.removeWhere((t) => t.id == todo.id));

          ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Berhasil dihapus')));

        } else {

          throw Exception('Delete failed');

        }

      } catch (e) {

        ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Error delete: $e')));

      } finally {

        setState(() => _loadingAction = false);

      }

    }

  }


  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        title: const Text('REST API Demo — ToDo Manager'),

        actions: [

          IconButton(onPressed: _refresh, icon: const Icon(Icons.refresh), tooltip: 'GET (refresh)'),

        ],

      ),

      body: Column(

        children: [

          Padding(

            padding: const EdgeInsets.all(12.0),

            child: Row(

              children: [

                Expanded(

                  child: TextField(

                    controller: _newTodoController,

                    decoration: const InputDecoration(labelText: 'Tambah todo baru (POST)', border: OutlineInputBorder()),

                    onSubmitted: (_) => _createTodo(),

                  ),

                ),

                const SizedBox(width: 8),

                _loadingAction

                    ? const SizedBox(width: 48, height: 48, child: Center(child: CircularProgressIndicator()))

                    : ElevatedButton(onPressed: _createTodo, child: const Text('Tambah')),

              ],

            ),

          ),

          Expanded(

            child: _todos.isEmpty

                ? FutureBuilder<List<TodoItem>>(

                    future: _futureTodos,

                    builder: (context, snapshot) {

                      if (snapshot.connectionState == ConnectionState.waiting) return const Center(child: CircularProgressIndicator());

                      if (snapshot.hasError) return Center(child: Text('Error: ${snapshot.error}'));

                      final list = snapshot.data ?? [];

                      if (list.isEmpty) return const Center(child: Text('Tidak ada data'));

                      return _buildList(list);

                    },

                  )

                : _buildList(_todos),

          ),

        ],

      ),

      floatingActionButton: FloatingActionButton(

        onPressed: () async {

          // Show short info dialog about HTTP methods

          await showDialog<void>(

            context: context,

            builder: (context) => AlertDialog(

              title: const Text('Metode HTTP ringkas'),

              content: const Text('GET = membaca data\nPOST = membuat data baru\nPUT = memperbarui data (seluruh resource)\nDELETE = menghapus data'),

              actions: [TextButton(onPressed: () => Navigator.pop(context), child: const Text('Tutup'))],

            ),

          );

        },

        child: const Icon(Icons.info_outline),

      ),

    );

  }


  Widget _buildList(List<TodoItem> list) {

    return ListView.separated(

      padding: const EdgeInsets.all(12),

      itemCount: list.length,

      separatorBuilder: (_, __) => const SizedBox(height: 8),

      itemBuilder: (context, index) {

        final todo = list[index];

        return Card(

          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),

          elevation: 3,

          child: ListTile(

            leading: Checkbox(value: todo.completed, onChanged: (_) => _toggleCompleted(todo)),

            title: Text(todo.title, maxLines: 2, overflow: TextOverflow.ellipsis),

            subtitle: Text('ID: ${todo.id}'),

            trailing: Row(

              mainAxisSize: MainAxisSize.min,

              children: [

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

                IconButton(onPressed: () => _deleteTodoConfirm(todo), icon: const Icon(Icons.delete)),

              ],

            ),

          ),

        );

      },

    );

  }

}


Share:

Chat Admin

Chat via WhatsApp

Blogger Tricks

Blogger Themes