0% menganggap dokumen ini bermanfaat (0 suara)
5 tayangan53 halaman

Modul Pembelajaran Flutter 2025

Diunggah oleh

Puji Anto
Hak Cipta
© All Rights Reserved
Kami menangani hak cipta konten dengan serius. Jika Anda merasa konten ini milik Anda, ajukan klaim di sini.
Format Tersedia
Unduh sebagai DOCX, PDF, TXT atau baca online di Scribd
0% menganggap dokumen ini bermanfaat (0 suara)
5 tayangan53 halaman

Modul Pembelajaran Flutter 2025

Diunggah oleh

Puji Anto
Hak Cipta
© All Rights Reserved
Kami menangani hak cipta konten dengan serius. Jika Anda merasa konten ini milik Anda, ajukan klaim di sini.
Format Tersedia
Unduh sebagai DOCX, PDF, TXT atau baca online di Scribd

MODUL PEMBELAJARAN

FLUTTER
Dari Pemula Hingga Mahir (2025)

DAFTAR ISI
1. Pendahuluan Flutter & Dart
2. Persiapan Lingkungan & Instalasi
3. Struktur Project Flutter
4. Konsep Widget & Layout Dasar
5. Navigasi & Pengiriman Data
6. Form & Validasi Data
7. State Management dengan setState
8. Konsumsi REST API & JSON Parsing
9. Persistent Storage dengan SharedPreferences
10. Database Lokal dengan SQLite
11. Flutter Web & Responsive Design
12. Studi Kasus: Aplikasi Todo Manager

BAB 1: PENDAHULUAN
FLUTTER & DART
1.1 Apa Itu Flutter?
Flutter adalah framework open-source yang dikembangkan oleh Google untuk
membangun aplikasi mobile, web, dan desktop menggunakan satu basis kode
(single codebase). Flutter memungkinkan developer untuk membuat aplikasi
yang indah, cepat, dan responsif di berbagai platform.

Fitur Utama Flutter


Cross-Platform Development
Satu kode Flutter dapat berjalan di Android, iOS, Web, Windows, Mac, dan Linux
tanpa perlu menulis ulang kode untuk setiap platform.

Hot Reload
Ubah kode dan lihat perubahan secara instant tanpa perlu rebuild lengkap. Ini
membuat development cycle jauh lebih cepat dan meningkatkan produktivitas
developer.

Material Design & Cupertino


Flutter menyediakan dukungan penuh untuk UI Material (Android) dan
Cupertino (iOS), memastikan aplikasi Anda terlihat native di setiap platform.

Performance Tinggi
Flutter mengkompilasi ke native code, bukan webview. Ini menghasilkan
performa yang sangat baik dan smooth animations.

Rich Widget Library


Perpustakaan widget Flutter yang lengkap memudahkan developer untuk
membuat UI yang menarik dengan cepat tanpa perlu membuat komponen dari
nol.

1.2 Mengapa Pilih Flutter?


1. Produktivitas Tinggi: Hot reload dan framework yang intuitif
mempercepat development
2. Beautiful UI: Widget dirancang untuk menghasilkan UI yang indah
dengan mudah
3. Strong Community: Komunitas global yang aktif dengan resource
belajar melimpah
4. Backed by Google: Dukungan resmi dan terus diupdate dengan fitur-
fitur baru
5. Satu Developer, Multi Platform: Hemat biaya dengan satu tim
development untuk semua platform

1.3 Pengenalan Bahasa Dart


Dart adalah bahasa pemrograman yang dikembangkan Google dan menjadi
bahasa resmi untuk Flutter. Dart menggabungkan fitur-fitur dari bahasa-bahasa
populer seperti Java, C#, dan JavaScript.

Tipe Data Dasar Dart


// Integer
int age = 25;

// Double (bilangan desimal)


double height = 1.75;
// String (teks)
String name = "Budi Santoso";

// Boolean (true/false)
bool isActive = true;

// Dynamic (tipe otomatis, bisa berubah)


dynamic value = 42; // bisa berubah ke string atau tipe lain

// Null Safety (Dart 2.12+)


String? nullable = null; // bisa berisi null
String notNull = "harus diisi"; // tidak boleh null

Collections (Kumpulan Data)


// List (Array) - urutan data
List<int> numbers = [1, 2, 3, 4, 5];
List<String> names = ['Budi', 'Ani', 'Citra'];

// Map (Dictionary/Object) - pasangan key-value


Map<String, dynamic> user = {
'name': 'Budi',
'age': 25,
'email': 'budi@[Link]',
'isActive': true
};

// Set (kumpulan unik, tidak ada duplikat)


Set<String> tags = {'flutter', 'dart', 'mobile'};

Function (Fungsi)
// Function dengan tipe return
int add(int a, int b) {
return a + b;
}

// Arrow function (shorthand)


int multiply(int a, int b) => a * b;

// Function dengan parameter opsional


String greet(String name, {String title = 'Mr'}) {
return 'Hello $title $name';
}

// Function dengan named parameters


void printUser({
required String name,
required int age,
String city = 'Jakarta',
}) {
print('$name, $age tahun, dari $city');
}
// Penggunaan
int hasil = add(5, 3); // hasil: 8
int kali = multiply(4, 2); // kali: 8
print(greet('Budi')); // Hello Mr Budi
printUser(name: 'Ani', age: 23); // Ani, 23 tahun, dari Jakarta

Class & Object (OOP)


// Mendefinisikan class
class User {
String name;
int age;
String email;

// Constructor
User([Link], [Link], [Link]);

// Method (fungsi dalam class)


void introduce() {
print('Nama saya $name, umur $age tahun');
}

String getInfo() {
return 'n a m e ¿ age tahun) - $email';
}
}

// Inheritance (Pewarisan)
class Admin extends User {
String role;

Admin(String name, int age, String email, [Link])


: super(name, age, email);

@override
void introduce() {
print('Admin: $name dengan role: $role');
}
}

// Penggunaan
var user = User('Budi', 25, 'budi@[Link]');
[Link](); // Output: Nama saya Budi, umur 25 tahun

var admin = Admin('Ani', 28, 'ani@[Link]', 'Manager');


[Link](); // Output: Admin: Ani dengan role: Manager

Exception Handling (Penanganan Error)


try {
int result = [Link]('abc'); // akan error
} on FormatException {
print('Input bukan format angka');
} catch (e) {
print('Error: $e');
} finally {
print('Proses selesai'); // selalu dijalankan
}

1.4 Dart Modern Features (2025)


Dart terus berkembang dengan fitur-fitur baru yang meningkatkan
produktivitas:

 Improved Null Safety: Null Safety yang lebih baik untuk mencegah null
reference errors
 Pattern Matching: Fitur pattern matching yang powerful untuk handling
data kompleks
 Records: Tipe data records untuk mengelompokkan data dengan lebih
efisien
 Extensions: Method extensions untuk menambahkan method ke tipe data
existing
 Enhanced Generics: Generics yang lebih powerful untuk type safety

BAB 2: PERSIAPAN
LINGKUNGAN &
INSTALASI
2.1 Instalasi Flutter SDK
Flutter SDK adalah software development kit yang berisi semua tools dan
libraries yang dibutuhkan untuk membuat aplikasi Flutter.

Windows
Langkah 1: Download Flutter SDK

1. Kunjungi [Link]
2. Download file ZIP untuk Windows (versi stable terbaru)
3. Extract ke lokasi yang mudah diakses, misal: C:\flutter
Langkah 2: Setup Environment Variable

1. Buka Environment Variables (tekan Win+X → Environment)


2. Edit PATH → Tambahkan: C:\flutter\bin
3. Restart terminal dan verifikasi:
flutter --version
flutter doctor

macOS/Linux

Clone repository Flutter


git clone [Link] -b stable

Tambahkan ke PATH
(untuk Linux/Mac)
export PATH=" P A T H :HOME/flutter/bin"

Verifikasi instalasi
flutter doctor

2.2 Setup IDE - Visual Studio Code


(Recommended)
Visual Studio Code adalah IDE yang ringan dan populer untuk Flutter
development. Alternatif lain adalah Android Studio.

Instalasi VS Code + Extensions:

1. Download VS Code dari [Link]


2. Install dua extension penting:
o Flutter (Dart Code)
o Dart (Dart Code)
3. Restart VS Code
4. Buka Command Palette (Ctrl+Shift+P) → Ketik "Flutter: New Project"
Android Studio Alternative:

1. Download Android Studio dari [Link]


2. Install → Buka Plugins → Cari "Flutter" → Install
3. Restart → File → New Flutter Project

2.3 Membuat Project Flutter


Pertama

Membuat project baru


flutter create hello_flutter

Masuk ke folder project


cd hello_flutter

Menjalankan aplikasi
(gunakan emulator/device)
flutter run

Figure 1: Flutter Development Tools - Android Studio dan VS Code

2.4 Struktur Folder Project Flutter


Memahami struktur folder adalah kunci untuk mengorganisir project dengan
baik.
hello_flutter/
├── lib/ # FOLDER UTAMA - Kode aplikasi
│ └── [Link] # Entry point aplikasi
├── test/ # Unit test dan widget test
├── android/ # Native code Android (Java/Kotlin)
├── ios/ # Native code iOS (Swift/Objective-C)
├── web/ # Flutter Web version
├── [Link] # Konfigurasi project & dependencies
├── [Link] # Lock file untuk versions
└── [Link] # Dokumentasi project

CATATAN: Developer akan fokus di folder 'lib/' untuk membuat kode Dart.

2.5 File [Link] Dasar


[Link] adalah file pertama yang dijalankan oleh Flutter.

import 'package:flutter/[Link]';

// Entry point aplikasi


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

// Root widget aplikasi (StatelessWidget)


class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Hello Flutter',
theme: ThemeData(
primarySwatch: [Link],
useMaterial3: true, // Material Design 3 (2025)
),
home: const MyHomePage(title: 'Flutter Demo'),
);
}
}

// Main page (StatefulWidget - bisa berubah)


class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required [Link]}) : super(key: key);
final String title;

@override
State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {


int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++; // Update state dan rebuild UI
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text([Link]),
),
body: Center(
child: Column(
mainAxisAlignment: [Link],
children: <Widget>[
const Text('Anda telah menekan tombol:'),
Text(
'$_counter',
style: [Link](context).[Link],
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
child: const Icon([Link]),
),
);
}
}

2.6 Menambahkan Dependencies


Dependencies adalah library eksternal yang digunakan project. Edit file
[Link]:

dependencies:
flutter:
sdk: flutter
http: ^1.1.0 # Untuk HTTP requests
shared_preferences: ^2.0.0 # Local storage
provider: ^6.0.0 # State management
intl: ^0.19.0 # Internalization

dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.0 # Linting rules

Kemudian jalankan:
flutter pub get
BAB 3: KONSEP WIDGET
& LAYOUT DASAR
3.1 Apa Itu Widget?
Widget adalah unit UI terkecil di Flutter. Setiap elemen visual (text, button,
image, layout) adalah widget. Flutter menggunakan widget tree untuk
membangun UI.

Figure 2: Widget Structure dan Layout Hierarchy

Dua Jenis Widget


1. StatelessWidget - Widget yang tidak berubah

class StaticCard extends StatelessWidget {


const StaticCard({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: [Link](16),
decoration: BoxDecoration(
color: [Link],
borderRadius: [Link](8),
),
child: Text(
'Ini widget statis',
style: TextStyle(color: [Link]),
),
);
}
}

2. StatefulWidget - Widget yang bisa berubah

class CounterWidget extends StatefulWidget {


const CounterWidget({Key? key}) : super(key: key);

@override
State<CounterWidget> createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {


int counter = 0;

@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Counter: $counter'),
ElevatedButton(
onPressed: () {
setState(() {
counter++;
});
},
child: Text('Tambah'),
),
],
);
}
}

3.2 Widget Dasar (Basic Widgets)


Text Widget
Text(
'Hello Flutter',
style: TextStyle(
fontSize: 24,
fontWeight: [Link],
color: [Link],
letterSpacing: 1.5,
),
textAlign: [Link],
)

Button Widgets
// Elevated Button (tombol dengan shadow)
ElevatedButton(
onPressed: () {
print('Elevated button pressed');
},
child: Text('Elevated Button'),
)

// Text Button (tombol text minimal)


TextButton(
onPressed: () {},
child: Text('Text Button'),
)

// Outlined Button (tombol dengan border)


OutlinedButton(
onPressed: () {},
child: Text('Outlined Button'),
)

// Icon Button
IconButton(
icon: Icon([Link]),
onPressed: () {},
)

Image Widget
// Dari asset (file lokal)
[Link](
'assets/my_image.png',
width: 200,
height: 200,
fit: [Link],
)

// Dari URL (internet)


[Link](
'[Link]
width: 200,
height: 200,
)
// CircleAvatar untuk avatar/profil
CircleAvatar(
radius: 50,
backgroundImage: NetworkImage('[Link]
)

Container Widget
Container adalah widget dasar untuk styling dan layout:

Container(
width: 200,
height: 200,
padding: [Link](16),
margin: [Link](horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: [Link],
borderRadius: [Link](12),
border: [Link](color: [Link], width: 2),
boxShadow: [
BoxShadow(
color: [Link](0.2),
blurRadius: 8,
offset: Offset(0, 4),
)
],
),
child: Text('Styled Container'),
)

3.3 Layout Widgets


Column (Vertikal Layout)
Column(
mainAxisAlignment: [Link],
crossAxisAlignment: [Link],
children: [
Text('Item 1'),
SizedBox(height: 16),
Text('Item 2'),
SizedBox(height: 16),
Text('Item 3'),
],
)
Figure 3: Flutter Architecture Overview

Row (Horizontal Layout)


Row(
mainAxisAlignment: [Link],
crossAxisAlignment: [Link],
children: [
Icon([Link]),
Icon([Link]),
Icon([Link]),
],
)

ListView (Daftar Scrollable)


// ListView simple
ListView(
children: [
ListTile(
leading: Icon([Link]),
title: Text('User 1'),
subtitle: Text('user1@[Link]'),
trailing: Icon(Icons.arrow_forward),
),
ListTile(
leading: Icon([Link]),
title: Text('User 2'),
subtitle: Text('user2@[Link]'),
),
],
)

// [Link] (efisien untuk list besar)


[Link](
itemCount: [Link],
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index]),
);
},
)

GridView (Grid Layout)


GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, // 2 kolom
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
children: [Link](6, (index) {
return Container(
color: [Link][100 * (index + 1)],
child: Center(
child: Text('Item ${index + 1}'),
),
);
}),
)

Stack (Overlay/Tumpukan)
Stack(
children: [
// Background
Container(
width: 200,
height: 200,
color: [Link],
),
// Floating element
Positioned(
right: 10,
bottom: 10,
child: Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: [Link],
shape: [Link],
),
child: Icon([Link], color: [Link]),
),
),
],
)

3.4 Contoh Halaman Lengkap: Profil


User
import 'package:flutter/[Link]';

class ProfilePage extends StatelessWidget {


const ProfilePage({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Profil Saya'),
centerTitle: true,
),
body: SingleChildScrollView(
child: Column(
children: [
// Header dengan foto
Container(
padding: [Link](20),
decoration: BoxDecoration(
color: [Link],
),
child: Column(
children: [
CircleAvatar(
radius: 50,
backgroundImage: NetworkImage(
'[Link]
),
),
SizedBox(height: 16),
Text(
'Budi Santoso',
style: TextStyle(
fontSize: 24,
fontWeight: [Link],
color: [Link],
),
),
Text(
'Flutter Developer',
style: TextStyle(
fontSize: 16,
color: Colors.white70,
),
),
],
),
),
// Body dengan informasi
Padding(
padding: [Link](20),
child: Column(
crossAxisAlignment: [Link],
children: [
Text(
'Informasi Kontak',
style: TextStyle(
fontSize: 18,
fontWeight: [Link],
),
),
SizedBox(height: 12),
InfoItem(
icon: [Link],
label: 'Email',
value: 'budi@[Link]',
),
InfoItem(
icon: [Link],
label: 'Phone',
value: '+62 812 345 6789',
),
InfoItem(
icon: Icons.location_on,
label: 'Alamat',
value: 'Jakarta, Indonesia',
),
],
),
),
// Button
Padding(
padding: [Link](horizontal: 20, vertical: 20),
child: Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () {},
child: Text('Edit Profil'),
),
),
SizedBox(width: 12),
Expanded(
child: OutlinedButton(
onPressed: () {},
child: Text('Bagikan'),
),
),
],
),
),
],
),
),
);
}
}

// Widget custom untuk info item


class InfoItem extends StatelessWidget {
final IconData icon;
final String label;
final String value;

const InfoItem({
Key? key,
required [Link],
required [Link],
required [Link],
}) : super(key: key);

@override
Widget build(BuildContext context) {
return Padding(
padding: [Link](vertical: 12),
child: Row(
children: [
Icon(icon, color: [Link], size: 24),
SizedBox(width: 16),
Column(
crossAxisAlignment: [Link],
children: [
Text(
label,
style: TextStyle(
fontSize: 12,
color: [Link][600],
fontWeight: FontWeight.w500,
),
),
Text(
value,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
],
),
],
),
);
}
}

BAB 4: NAVIGASI &


PENGIRIMAN DATA
ANTAR HALAMAN
4.1 Konsep Navigation
Navigation adalah cara berpindah antar halaman/screen dalam aplikasi. Flutter
menyediakan Navigator untuk mengelola navigation stack.

4.2 Unnamed Routes (Navigasi


Dasar)
// Pindah ke halaman baru
[Link](
context,
MaterialPageRoute(builder: (context) => HalamanB()),
);

// Kembali ke halaman sebelumnya


[Link](context);

// Kembali dengan return data


[Link](context, 'Data yang dikirim kembali');

4.3 Mengirim Data Antar Halaman


Contoh: Dari HalamanA ke HalamanB

// halaman_a.dart
class HalamanA extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Halaman A')),
body: Center(
child: ElevatedButton(
onPressed: () {
// Kirim data ke HalamanB
[Link](
context,
MaterialPageRoute(
builder: (context) => HalamanB(
nama: 'Budi',
usia: 25,
),
),
);
},
child: Text('Buka Halaman B'),
),
),
);
}
}

// halaman_b.dart
class HalamanB extends StatelessWidget {
final String nama;
final int usia;

const HalamanB({
Key? key,
required [Link],
required [Link],
}) : super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Halaman B')),
body: Center(
child: Column(
mainAxisAlignment: [Link],
children: [
Text('Nama: $nama'),
Text('Usia: $usia'),
ElevatedButton(
onPressed: () {
[Link](context);
},
child: Text('Kembali'),
),
],
),
),
);
}
}

4.4 Menerima Data Return dari


Halaman Berikutnya
// Halaman A - mengirim dan menerima data
class HalamanA extends StatefulWidget {
@override
State<HalamanA> createState() => _HalamanAState();
}

class _HalamanAState extends State<HalamanA> {


String? dataYangDiterima;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Halaman A')),
body: Center(
child: Column(
mainAxisAlignment: [Link],
children: [
if (dataYangDiterima != null)
Text('Data dari B: $dataYangDiterima'),
ElevatedButton(
onPressed: () async {
// Tunggu hasil dari HalamanB
final result = await [Link](
context,
MaterialPageRoute(builder: (context) => HalamanB()),
);

// Terima data
if (result != null) {
setState(() {
dataYangDiterima = result;
});
}
},
child: Text('Buka Halaman B'),
),
],
),
),
);

}
}
// Halaman B - mengirim data kembali
class HalamanB extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Halaman B')),
body: Center(
child: ElevatedButton(
onPressed: () {
[Link](context, 'Data dari Halaman B');
},
child: Text('Kirim Data dan Kembali'),
),
),
);
}
}

4.5 Named Routes (Recommended


untuk App Besar)
Named routes lebih terstruktur untuk aplikasi dengan banyak halaman.

// [Link]
void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {


@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Named Routes Demo',
initialRoute: '/',
routes: {
'/': (context) => Home(),
'/detail': (context) => Detail(),
'/settings': (context) => Settings(),
},
);
}
}

// Navigasi dengan named routes


ElevatedButton(
onPressed: () {
[Link](context, '/detail');
},
child: Text('Buka Detail'),
)
// Dengan arguments
ElevatedButton(
onPressed: () {
[Link](
context,
'/detail',
arguments: {'id': 123, 'nama': 'Produk A'},
);
},
child: Text('Buka Detail dengan Argument'),
)

// Menerima arguments
class Detail extends StatelessWidget {
@override
Widget build(BuildContext context) {
final args = [Link](context)!.[Link] as Map;
return Scaffold(
appBar: AppBar(title: Text('Detail')),
body: Center(
child: Text('ID: ${args['id']}, Nama: ${args['nama']}'),
),
);
}
}

BAB 5: FORM & VALIDASI


DATA
5.1 TextFormField Dasar
TextFormField adalah widget untuk input text dengan built-in validasi.

TextFormField(
decoration: InputDecoration(
labelText: 'Nama Lengkap',
hintText: 'Masukkan nama Anda',
prefixIcon: Icon([Link]),
border: OutlineInputBorder(),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(color: [Link]),
),
),
validator: (value) {
if (value == null || [Link]) {
return 'Nama tidak boleh kosong';
}
if ([Link] < 3) {
return 'Nama minimal 3 karakter';
}
return null;
},
)

5.2 Contoh Form Lengkap:


Registrasi User
import 'package:flutter/[Link]';

class RegistrationForm extends StatefulWidget {


@override
State<RegistrationForm> createState() => _RegistrationFormState();
}

class _RegistrationFormState extends State<RegistrationForm> {


final _formKey = GlobalKey<FormState>();
final _nameController = TextEditingController();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _phoneController = TextEditingController();

String? _selectedGender;
List<String> genders = ['Laki-laki', 'Perempuan'];
bool _agreeToTerms = false;

@override
void dispose() {
_nameController.dispose();
_emailController.dispose();
_passwordController.dispose();
_phoneController.dispose();
[Link]();
}

void _submitForm() {
if (_formKey.currentState!.validate()) {
if (!_agreeToTerms) {
[Link](context).showSnackBar(
SnackBar(content: Text('Setujui syarat dan ketentuan')),
);
return;
}

// Proses data
print('Nama: ${_nameController.text}');
print('Email: ${_emailController.text}');
print('Gender: $_selectedGender');
[Link](context).showSnackBar(
SnackBar(content: Text('Registrasi berhasil!')),
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Formulir Registrasi')),
body: SingleChildScrollView(
padding: [Link](20),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: [Link],
children: [
// Nama
TextFormField(
controller: _nameController,
decoration: InputDecoration(
labelText: 'Nama Lengkap',
hintText: 'Masukkan nama Anda',
prefixIcon: Icon([Link]),
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || [Link]) {
return 'Nama tidak boleh kosong';
}
if ([Link] < 3) {
return 'Nama minimal 3 karakter';
}
return null;
},
),
SizedBox(height: 16),

// Email
TextFormField(
controller: _emailController,
keyboardType: [Link],
decoration: InputDecoration(
labelText: 'Email',
hintText: 'example@[Link]',
prefixIcon: Icon([Link]),
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || [Link]) {
return 'Email tidak boleh kosong';
}
if (![Link]('@')) {
return 'Format email tidak valid';
}
return null;
},
),
SizedBox(height: 16),

// Password
TextFormField(
controller: _passwordController,
obscureText: true,
decoration: InputDecoration(
labelText: 'Password',
hintText: 'Minimal 6 karakter',
prefixIcon: Icon([Link]),
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || [Link]) {
return 'Password tidak boleh kosong';
}
if ([Link] < 6) {
return 'Password minimal 6 karakter';
}
return null;
},
),
SizedBox(height: 16),

// Phone
TextFormField(
controller: _phoneController,
keyboardType: [Link],
decoration: InputDecoration(
labelText: 'No. Telepon',
hintText: '08xx xxxx xxxx',
prefixIcon: Icon([Link]),
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || [Link]) {
return 'No. telepon tidak boleh kosong';
}
if (![Link]('08') || [Link] < 10) {
return 'Format telepon tidak valid';
}
return null;
},
),
SizedBox(height: 16),

// Gender Dropdown
DropdownButtonFormField<String>(
value: _selectedGender,
decoration: InputDecoration(
labelText: 'Jenis Kelamin',
border: OutlineInputBorder(),
prefixIcon: Icon([Link]),
),
items: [Link]((gender) {
return DropdownMenuItem(
value: gender,
child: Text(gender),
);
}).toList(),
onChanged: (value) {
setState(() {
_selectedGender = value;
});
},
validator: (value) {
if (value == null) {
return 'Pilih jenis kelamin';
}
return null;
},
),
SizedBox(height: 16),

// Checkbox
CheckboxListTile(
title: Text('Saya setuju dengan syarat dan
ketentuan'),
value: _agreeToTerms,
onChanged: (value) {
setState(() {
_agreeToTerms = value ?? false;
});
},
),
SizedBox(height: 24),

// Button Submit
SizedBox(
width: [Link],
height: 50,
child: ElevatedButton(
onPressed: _submitForm,
child: Text('Daftar'),
),
),
],
),
),
),
);

}
}

BAB 6: STATE
MANAGEMENT DENGAN
SETSTATE
6.1 Konsep State
State adalah data yang dapat berubah dan mempengaruhi tampilan UI. Ketika
state berubah, widget akan di-rebuild untuk menampilkan data terbaru.

class Counter extends StatefulWidget {


@override
State<Counter> createState() => _CounterState();
}

class _CounterState extends State<Counter> {


int counter = 0;

void increment() {
setState(() {
counter++; // Update state
});
// UI akan di-rebuild otomatis dengan counter value baru
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: Center(
child: Column(
mainAxisAlignment: [Link],
children: [
Text(
'Counter: $counter',
style: TextStyle(fontSize: 32),
),
ElevatedButton(
onPressed: increment,
child: Text('Increment'),
),
],
),
),
);
}
}

6.2 Lifting State Up


Ketika multiple widgets perlu mengakses data yang sama, state harus di-"lift up"
ke parent widget.

class ParentWidget extends StatefulWidget {


@override
State<ParentWidget> createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {


int sharedCounter = 0;

void increaseCounter() {
setState(() {
sharedCounter++;
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Lifting State Up')),
body: Column(
children: [
ChildA(counter: sharedCounter),
ChildB(onIncrement: increaseCounter),
],
),
);
}
}

class ChildA extends StatelessWidget {


final int counter;
const ChildA({required [Link]});
@override
Widget build(BuildContext context) {
return Text('Counter dari Parent: $counter');
}
}

class ChildB extends StatelessWidget {


final VoidCallback onIncrement;
const ChildB({required [Link]});

@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onIncrement,
child: Text('Increment'),
);
}
}

6.3 Contoh: Todo List Manager


class TodoListScreen extends StatefulWidget {
@override
State<TodoListScreen> createState() => _TodoListScreenState();
}

class _TodoListScreenState extends State<TodoListScreen> {


List<String> todos = [];
final _controller = TextEditingController();

void _addTodo() {
if (_controller.[Link]) return;

setState(() {
[Link](_controller.text);
_controller.clear();
});

void _deleteTodo(int index) {


setState(() {
[Link](index);
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Todo List'),
centerTitle: true,
),
body: Column(
children: [
// Input
Padding(
padding: [Link](16),
child: Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: InputDecoration(
hintText: 'Tambah todo baru...',
border: OutlineInputBorder(),
),
),
),
SizedBox(width: 8),
ElevatedButton(
onPressed: _addTodo,
child: Icon([Link]),
),
],
),
),
// List
Expanded(
child: [Link]
? Center(child: Text('Tidak ada todo'))
: [Link](
itemCount: [Link],
itemBuilder: (context, index) {
return ListTile(
title: Text(todos[index]),
trailing: IconButton(
icon: Icon([Link]),
onPressed: () {
_deleteTodo(index);
},
),
);
},
),
),
],
),
);
}

@override
void dispose() {
_controller.dispose();
[Link]();
}
}

BAB 7: KONSUMSI REST


API & JSON PARSING
7.1 Konsep REST API
REST API adalah cara aplikasi berkomunikasi dengan server melalui HTTP
requests. Flutter menyediakan package http untuk membuat HTTP requests.

HTTP Methods
 GET: Mengambil data dari server
 POST: Mengirim data baru ke server
 PUT: Update data yang sudah ada
 DELETE: Menghapus data

7.2 Setup HTTP Package


Edit [Link]:

dependencies:
http: ^1.1.0

Jalankan: flutter pub get

7.3 Membuat Model dari JSON


import 'dart:convert';

class User {
int id;
String name;
String email;
String phone;
String website;

User({
required [Link],
required [Link],
required [Link],
required [Link],
required [Link],
});

// Factory untuk membuat User dari JSON


factory [Link](Map<String, dynamic> json) {
return User(
id: json['id'],
name: json['name'],
email: json['email'],
phone: json['phone'],
website: json['website'],
);
}

// Convert User ke JSON


Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'email': email,
'phone': phone,
'website': website,
};
}
}

7.4 Fetch Data dari API


import 'package:http/[Link]' as http;
import 'dart:convert';

class UserService {
static const String _baseUrl = '[Link]

// Fetch satu user


static Future<User> fetchUser(int id) async {
try {
final response = await [Link](
[Link]('❑❑ a s e U r l/u s e r s/¿ id'),
);

if ([Link] == 200) {
return [Link](jsonDecode([Link]));
} else {
throw Exception('Gagal memuat user');
}
} catch (e) {
throw Exception('Error: $e');
}

}
// Fetch list users
static Future<List<User>> fetchUsers() async {
try {
final response = await [Link](
[Link]('$_baseUrl/users'),
);

if ([Link] == 200) {
List<dynamic> data = jsonDecode([Link]);
return [Link]((user) => [Link](user)).toList();
} else {
throw Exception('Gagal memuat users');
}
} catch (e) {
throw Exception('Error: $e');
}

// Post data baru


static Future<User> createUser(String name, String email) async {
try {
final response = await [Link](
[Link]('$_baseUrl/users'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'name': name,
'email': email,
'phone': '08xx',
'website': '[Link]',
}),
);

if ([Link] == 201) {
return [Link](jsonDecode([Link]));
} else {
throw Exception('Gagal membuat user');
}
} catch (e) {
throw Exception('Error: $e');
}

}
}

7.5 UI dengan FutureBuilder


class UserListScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Daftar User')),
body: FutureBuilder<List<User>>(
future: [Link](),
builder: (context, snapshot) {
if ([Link] == [Link]) {
return Center(child: CircularProgressIndicator());
} else if ([Link]) {
return Center(child: Text('Error: {[Link]}'),
),
title: Text([Link]),
subtitle: Text([Link]),
onTap: () {
[Link](context).showSnackBar(
SnackBar(content: Text('Tapped ${[Link]}')),
);
},
);
},
);
}
return Center(child: Text('Tidak ada data'));
},
),
);
}
}

BAB 8: PERSISTENT
STORAGE DENGAN
SHAREDPREFERENCES
8.1 Konsep Persistent Storage
Persistent storage adalah menyimpan data lokal di device agar tetap ada setelah
app ditutup. SharedPreferences adalah cara paling sederhana untuk small data.

8.2 Setup SharedPreferences


Edit [Link]:

dependencies:
shared_preferences: ^2.0.0
8.3 Menyimpan & Membaca Data
import 'package:shared_preferences/shared_preferences.dart';

class PreferenceService {
static late SharedPreferences _prefs;

// Initialize
static Future<void> init() async {
_prefs = await [Link]();
}

// Save string
static Future<void> saveString(String key, String value) async {
await _prefs.setString(key, value);
}

// Get string
static String? getString(String key) {
return _prefs.getString(key);
}

// Save int
static Future<void> saveInt(String key, int value) async {
await _prefs.setInt(key, value);
}

// Get int
static int? getInt(String key) {
return _prefs.getInt(key);
}

// Save bool
static Future<void> saveBool(String key, bool value) async {
await _prefs.setBool(key, value);
}

// Get bool
static bool? getBool(String key) {
return _prefs.getBool(key);
}

// Delete
static Future<void> delete(String key) async {
await _prefs.remove(key);
}

// Clear all
static Future<void> clear() async {
await _prefs.clear();
}
}
8.4 Contoh: Dark Mode Toggle
class ThemeSettingsScreen extends StatefulWidget {
@override
State<ThemeSettingsScreen> createState() => _ThemeSettingsScreenState();
}

class _ThemeSettingsScreenState extends State<ThemeSettingsScreen> {


bool _isDarkMode = false;

@override
void initState() {
[Link]();
_loadThemePreference();
}

void _loadThemePreference() {
final isDark = [Link]('isDarkMode') ?? false;
setState(() {
_isDarkMode = isDark;
});
}

void _toggleTheme(bool value) {


setState(() {
_isDarkMode = value;
});
[Link]('isDarkMode', value);
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Pengaturan Tema')),
body: Padding(
padding: [Link](20),
child: SwitchListTile(
title: Text('Dark Mode'),
value: _isDarkMode,
onChanged: _toggleTheme,
),
),
);
}
}
BAB 9: DATABASE LOKAL
DENGAN SQLITE
9.1 Setup SQLite
Edit [Link]:

dependencies:
sqflite: ^2.0.0
path: ^1.8.0

9.2 Model & Database Helper


// models/[Link]
class Note {
int? id;
String title;
String content;
DateTime date;

Note({
[Link],
required [Link],
required [Link],
required [Link],
});

// Convert ke Map (untuk database)


Map<String, dynamic> toMap() {
return {
'id': id,
'title': title,
'content': content,
'date': date.toIso8601String(),
};
}

// Create dari Map (dari database)


factory [Link](Map<String, dynamic> map) {
return Note(
id: map['id'],
title: map['title'],
content: map['content'],
date: [Link](map['date']),
);
}
}
// services/database_helper.dart
import 'package:sqflite/[Link]';
import 'package:path/[Link]';

class DatabaseHelper {
static final DatabaseHelper _instance = DatabaseHelper._internal();
static Database? _database;

DatabaseHelper._internal();

factory DatabaseHelper() {
return _instance;
}

Future<Database> get database async {


if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}

Future<Database> _initDatabase() async {


final databasesPath = await getDatabasesPath();
final path = join(databasesPath, '[Link]');

return await openDatabase(


path,
version: 1,
onCreate: _onCreate,
);

Future<void> _onCreate(Database db, int version) async {


await [Link]('''
CREATE TABLE notes(
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
content TEXT NOT NULL,
date TEXT NOT NULL
)
''');
}

// CRUD Operations
Future<int> insertNote(Note note) async {
final db = await database;
return await [Link]('notes', [Link]());
}

Future<List<Note>> getAllNotes() async {


final db = await database;
final maps = await [Link]('notes', orderBy: 'date DESC');
return [Link]([Link], (i) => [Link](maps[i]));
}

Future<int> updateNote(Note note) async {


final db = await database;
return await [Link]('notes', [Link](), where: 'id = ?', whereArgs:
[[Link]]);
}

Future<int> deleteNote(int id) async {


final db = await database;
return await [Link]('notes', where: 'id = ?', whereArgs: [id]);
}
}

BAB 10: FLUTTER WEB &


RESPONSIVE DESIGN
10.1 Build untuk Web

Enable web support


flutter config --enable-web

Run di web
flutter run -d chrome

Build untuk web


flutter build web

10.2 Responsive Design dengan


LayoutBuilder
class ResponsiveLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
if ([Link] < 600) {
return MobileLayout();
} else if ([Link] < 1200) {
return TabletLayout();
} else {
return DesktopLayout();
}
},
);
}
}

class MobileLayout extends StatelessWidget {


@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Mobile')),
body: ListView(
children: [Link](6, (index) {
return Card(
margin: [Link](16),
child: Padding(
padding: [Link](16),
child: Text('Item ${index + 1}'),
),
);
}),
),
);
}
}

class TabletLayout extends StatelessWidget {


@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Tablet')),
body: [Link](
crossAxisCount: 2,
padding: [Link](16),
mainAxisSpacing: 16,
crossAxisSpacing: 16,
children: [Link](6, (index) {
return Card(
child: Center(child: Text('Item ${index + 1}')),
);
}),
),
);
}
}
class DesktopLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
Container(
width: 250,
color: [Link][800],
child: ListView(
children: [
ListTile(
title: Text('Home', style: TextStyle(color: [Link])),
),
ListTile(
title: Text('Profile', style: TextStyle(color: [Link])),
),
ListTile(
title: Text('Settings', style: TextStyle(color: [Link])),
),
],
),
),
Expanded(
child: [Link](
crossAxisCount: 3,
padding: [Link](20),
mainAxisSpacing: 20,
crossAxisSpacing: 20,
children: [Link](9, (index) {
return Card(
child: Center(child: Text('Item ${index + 1}')),
);
}),
),
),
],
),
);
}
}

BAB 11: STUDI KASUS -


APLIKASI TODO
MANAGER
11.1 Overview Project
Aplikasi Todo Manager dengan fitur lengkap:

 CRUD todo items


 Database lokal (SQLite)
 Kategori todo
 Status todo (completed/pending)
 Filter dan search
 Responsive design

11.2 Project Structure


todo_manager/
├── lib/
│ ├── [Link]
│ ├── models/
│ │ └── [Link]
│ ├── services/
│ │ └── database_helper.dart
│ ├── screens/
│ │ ├── home_screen.dart
│ │ ├── add_todo_screen.dart
│ │ └── edit_todo_screen.dart
│ └── widgets/
│ └── todo_item_widget.dart
├── test/
└── [Link]

11.3 Implementation
Model Todo
// models/[Link]
class Todo {
int? id;
String title;
String description;
String category;
bool isCompleted;
DateTime dueDate;
DateTime createdAt;

Todo({
[Link],
required [Link],
required [Link],
required [Link],
[Link] = false,
required [Link],
required [Link],
});

Map<String, dynamic> toMap() {


return {
'id': id,
'title': title,
'description': description,
'category': category,
'isCompleted': isCompleted ? 1 : 0,
'dueDate': dueDate.toIso8601String(),
'createdAt': createdAt.toIso8601String(),
};
}

factory [Link](Map<String, dynamic> map) {


return Todo(
id: map['id'],
title: map['title'],
description: map['description'],
category: map['category'],
isCompleted: map['isCompleted'] == 1,
dueDate: [Link](map['dueDate']),
createdAt: [Link](map['createdAt']),
);
}
}

Database Helper
// services/database_helper.dart
import 'package:sqflite/[Link]';
import 'package:path/[Link]';
import '../models/[Link]';

class DatabaseHelper {
static final DatabaseHelper _instance = DatabaseHelper._internal();
static Database? _database;

DatabaseHelper._internal();

factory DatabaseHelper() => _instance;

Future<Database> get database async {


if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}
Future<Database> _initDatabase() async {
final databasesPath = await getDatabasesPath();
final path = join(databasesPath, 'todo_manager.db');

return await openDatabase(


path,
version: 1,
onCreate: _onCreate,
);

Future<void> _onCreate(Database db, int version) async {


await [Link]('''
CREATE TABLE todos(
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
description TEXT,
category TEXT NOT NULL,
isCompleted INTEGER DEFAULT 0,
dueDate TEXT NOT NULL,
createdAt TEXT NOT NULL
)
''');
}

// CRUD Methods
Future<int> insertTodo(Todo todo) async {
final db = await database;
return await [Link]('todos', [Link]());
}

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


final db = await database;
final maps = await [Link]('todos', orderBy: 'dueDate DESC');
return [Link]([Link], (i) => [Link](maps[i]));
}

Future<List<Todo>> getTodosByCategory(String category) async {


final db = await database;
final maps = await [Link](
'todos',
where: 'category = ?',
whereArgs: [category],
orderBy: 'dueDate DESC',
);
return [Link]([Link], (i) => [Link](maps[i]));
}

Future<int> updateTodo(Todo todo) async {


final db = await database;
return await [Link]('todos', [Link](), where: 'id = ?', whereArgs:
[[Link]]);
}

Future<int> deleteTodo(int id) async {


final db = await database;
return await [Link]('todos', where: 'id = ?', whereArgs: [id]);
}
}

Home Screen
// screens/home_screen.dart
import 'package:flutter/[Link]';
import '../models/[Link]';
import '../services/database_helper.dart';
import 'add_todo_screen.dart';

class HomeScreen extends StatefulWidget {


const HomeScreen({Key? key}) : super(key: key);

@override
State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {


late DatabaseHelper _dbHelper;
List<Todo> _todos = [];
String _selectedCategory = 'Semua';
final List<String> _categories = ['Semua', 'Pekerjaan', 'Pribadi', 'Belanja',
'Kesehatan'];
final _searchController = TextEditingController();

@override
void initState() {
[Link]();
_dbHelper = DatabaseHelper();
_loadTodos();
}

void _loadTodos() async {


List<Todo> todos;
if (_selectedCategory == 'Semua') {
todos = await _dbHelper.getAllTodos();
} else {
todos = await _dbHelper.getTodosByCategory(_selectedCategory);
}
setState(() {
_todos = todos;
});
}

void
addTodo() {[Link](context,MaterialPageRoute(builder: (context) =>
const AddTodoScreen()),).then(() => _loadTodos());
}

void _deleteTodo(int id) {


_dbHelper.deleteTodo(id);
_loadTodos();
}

void _toggleTodo(Todo todo) {


[Link] = ![Link];
_dbHelper.updateTodo(todo);
_loadTodos();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Todo Manager'),
centerTitle: true,
),
body: Column(
children: [
// Category Filter
Padding(
padding: const [Link](8.0),
child: SingleChildScrollView(
scrollDirection: [Link],
child: Row(
children: _categories.map((cat) {
return Padding(
padding: const [Link](horizontal: 4),
child: FilterChip(
label: Text(cat),
selected: _selectedCategory == cat,
onSelected: (selected) {
setState(() {
_selectedCategory = cat;
});
_loadTodos();
},
),
);
}).toList(),
),
),
),
// Todo List
Expanded(
child: _todos.isEmpty
? const Center(child: Text('Tidak ada todo'))
: [Link](
itemCount: _todos.length,
itemBuilder: (context, index) {
final todo =

todos[index];return ListTile(leading: Checkbox(value:


[Link],onChanged: () => _toggleTodo(todo),
),
title: Text(
[Link],
style: TextStyle(
decoration: [Link]
? [Link]
: null,
),
),
subtitle: Text(
'${[Link]} • ${[Link]().split(' ')[0]}',
),
trailing: IconButton(
icon: const Icon([Link]),
onPressed: () => _deleteTodo([Link]!),
),
);
},
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: _addTodo,
child: const Icon([Link]),
),
);
}

@override
void dispose() {
_searchController.dispose();
[Link]();
}
}

Add Todo Screen


// screens/add_todo_screen.dart
import 'package:flutter/[Link]';
import '../models/[Link]';
import '../services/database_helper.dart';
class AddTodoScreen extends StatefulWidget {
const AddTodoScreen({Key? key}) : super(key: key);

@override
State<AddTodoScreen> createState() => _AddTodoScreenState();
}

class _AddTodoScreenState extends State<AddTodoScreen> {


final _formKey = GlobalKey<FormState>();
final _titleController = TextEditingController();
final _descriptionController = TextEditingController();
final _dbHelper = DatabaseHelper();

String? _selectedCategory;
DateTime? _selectedDate;
final List<String> _categories = ['Pekerjaan', 'Pribadi', 'Belanja', 'Kesehatan'];

void _pickDate() async {


final pickedDate = await showDatePicker(
context: context,
initialDate: [Link](),
firstDate: [Link](),
lastDate: DateTime(2030),
);
if (pickedDate != null) {
setState(() {
_selectedDate = pickedDate;
});
}
}

void _submitForm() {
if (_formKey.currentState!.validate()) {
if (_selectedCategory == null || _selectedDate == null) {
[Link](context).showSnackBar(
SnackBar(content: Text('Pilih kategori dan tanggal')),
);
return;
}

final todo = Todo(


title: _titleController.text,
description: _descriptionController.text,
category: _selectedCategory!,
dueDate: _selectedDate!,
createdAt: [Link](),
);

_dbHelper.insertTodo(todo);
[Link](context);
}
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Tambah Todo')),
body: SingleChildScrollView(
padding: [Link](20),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: [Link],
children: [
TextFormField(
controller: _titleController,
decoration: InputDecoration(
labelText: 'Judul',
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || [Link]) {
return 'Judul tidak boleh kosong';
}
return null;
},
),
SizedBox(height: 16),
TextFormField(
controller: _descriptionController,
decoration: InputDecoration(
labelText: 'Deskripsi',
border: OutlineInputBorder(),
),
maxLines: 4,
),
SizedBox(height: 16),
DropdownButtonFormField<String>(
value: _selectedCategory,
decoration: InputDecoration(
labelText: 'Kategori',
border: OutlineInputBorder(),
),
items: _categories.map((cat) {
return DropdownMenuItem(value: cat, child: Text(cat));
}).toList(),
onChanged: (value) {
setState(() {
_selectedCategory = value;
});
},
),
SizedBox(height: 16),
ElevatedButton(
onPressed: _pickDate,
child: Text(_selectedDate == null
? 'Pilih Tanggal'
: 'Tanggal: ${_selectedDate!.toString().split(' ')[0]}'),
),
SizedBox(height: 24),
SizedBox(
width: [Link],
height: 50,
child: ElevatedButton(
onPressed: _submitForm,
child: Text('Simpan Todo'),
),
),
],
),
),
),
);
}

@override
void dispose() {
_titleController.dispose();
_descriptionController.dispose();
[Link]();
}
}

11.4 Main Application


// [Link]
import 'package:flutter/[Link]';
import 'services/database_helper.dart';
import 'screens/home_screen.dart';

void main() async {


[Link]();
await DatabaseHelper().database;
runApp(const TodoManagerApp());
}

class TodoManagerApp extends StatelessWidget {


const TodoManagerApp({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Todo Manager',
theme: ThemeData(
primarySwatch: [Link],
useMaterial3: true,
),
home: const HomeScreen(),
);
}
}

KESIMPULAN & LANGKAH


SELANJUTNYA
Selamat! Anda telah mempelajari Flutter dari dasar hingga membuat aplikasi
kompleks dengan database. Perjalanan Flutter development Anda baru saja
dimulai.

Topik Advanced untuk Dipelajari


Selanjutnya
1. State Management Advanced

o Provider package untuk state management yang lebih scalable


o Riverpod sebagai evolusi dari Provider
o GetX untuk pendekatan yang berbeda
2. Advanced Features

o Custom animations dan transitions


o Custom widgets dan themes
o Platform channels untuk native code integration
3. Production-Ready

o Analytics dan crash reporting


o Performance optimization
o Testing (unit, widget, integration testing)
4. Publishing

o Build dan sign APK untuk Android


o Build dan deploy iOS ke App Store
o Deploy Flutter Web ke hosting
5. Modern Flutter (2025)

o AI/ML integration dengan TensorFlow Lite


o Foldable device support
o Enhanced accessibility features
o Better web performance

Best Practices Summary


 ✅ Gunakan const constructors untuk optimize rebuilds
 ✅ Implement proper error handling dan loading states
 ✅ Test kode Anda dengan unit tests dan widget tests
 ✅ Gunakan linting tools untuk code quality
 ✅ Follow Flutter style guide dan conventions
 ✅ Optimize performance dengan profiling tools
 ✅ Manage state dengan konsisten
 ✅ Keep learning dan follow Flutter updates

Resources Berguna
 Official Flutter Documentation: [Link]
 Dart Programming Language: [Link]
 Flutter YouTube Channel: [Link]
 [Link] - Flutter Packages: [Link]
 Stack Overflow - Flutter Tag:
[Link]
 Medium - Flutter Publication: [Link]

Modul Pembelajaran Flutter 2025 - Dari Pemula hingga Mahir

Created for comprehensive Flutter learning with practical examples and best
practices

Last Updated: Desember 2025

Keep Flutter, Keep Learning! 🚀

Anda mungkin juga menyukai