Beranda Coding Database Flutter: SQLite, Local Storage, dan CRUD untuk Pemula
CodingFlutter

Database Flutter: SQLite, Local Storage, dan CRUD untuk Pemula

Pelajari database Flutter dan local storage dari nol, mulai dari SQLite, shared_preferences, CRUD, model data, query, hingga penyimpanan offline.

Bagikan
database flutter
Bagikan

Setelah mempelajari REST API dan Pengolahan Data pada Level 8, Anda sudah memahami bagaimana aplikasi Flutter mengambil dan mengirim data melalui server.

Namun, aplikasi modern tidak selalu bergantung pada internet. Ada banyak situasi ketika aplikasi perlu menyimpan data langsung di perangkat pengguna.

Misalnya:

  • preferensi tema aplikasi,
  • status login tertentu,
  • pengaturan pengguna,
  • data favorit,
  • draft formulir,
  • daftar tugas,
  • cache data,
  • riwayat transaksi,
  • data yang harus tetap tersedia ketika offline.

Di sinilah konsep database dan local storage Flutter menjadi penting.

Flutter menyediakan berbagai pendekatan untuk persistence atau penyimpanan data. Dokumentasi resminya membagi kebutuhan persistence antara lain menjadi key-value storage, file storage, dan database SQLite.

Untuk data sederhana, Anda dapat menggunakan key-value storage seperti shared_preferences. Untuk data yang lebih kompleks dan membutuhkan query, relasi, serta operasi CRUD, SQLite dapat menjadi pilihan yang lebih tepat.

Pada Level 9 tentang database Flutter ini, Anda akan mempelajari:

Local Storage
      ↓
Key-Value
      ↓
File Storage
      ↓
SQLite Database
      ↓
CRUD
      ↓
Repository
      ↓
Offline Data

Tujuannya bukan sekadar mengetahui cara menyimpan data, tetapi memahami kapan harus menggunakan masing-masing metode penyimpanan.

Database Flutter SQLite ini merupakan materi pembelajaran Level 9 dari Roadmap Belajar Flutter dari Nol Sampai Siap Kerja yang dibahas pada situs Raja Putra Media.

Apa Itu Local Storage?

Local storage adalah mekanisme untuk menyimpan data secara lokal pada perangkat tempat aplikasi berjalan.

Berbeda dengan REST API pada Level 8 yang biasanya berkomunikasi dengan server, local storage memungkinkan aplikasi menyimpan data tanpa harus selalu melakukan request ke internet.

Gambaran sederhananya:

REST API

Flutter
   ↓
Internet
   ↓
Server
   ↓
Database Server

Sedangkan local storage:

Flutter
   ↓
Local Storage
   ↓
Perangkat Pengguna

Karena data disimpan di perangkat, aplikasi dapat membaca data tersebut kembali pada saat aplikasi dibuka.

Konsep inilah yang disebut persistence.

Apa Itu Data Persistence?

Persistence berarti data tetap tersedia setelah aplikasi ditutup atau dijalankan kembali.

Misalnya aplikasi memiliki counter:

Nilai: 10

Jika nilai hanya disimpan dalam state:

Aplikasi ditutup
      ↓
State hilang
      ↓
Aplikasi dibuka
      ↓
Nilai kembali ke 0

Tetapi jika nilai disimpan secara persistent:

Nilai: 10
      ↓
Disimpan ke perangkat
      ↓
Aplikasi ditutup
      ↓
Aplikasi dibuka kembali
      ↓
Nilai: 10

Inilah perbedaan antara state sementara dan persistent storage.

Jenis Penyimpanan Data di Flutter

Secara sederhana, Anda dapat membagi penyimpanan lokal menjadi tiga kategori utama:

MetodeCocok Untuk
Key-value storageSetting sederhana
File storageFile atau data berbentuk dokumen
SQLiteData terstruktur dan kompleks

Flutter menyediakan cookbook resmi untuk ketiga pendekatan tersebut. Mari kita bahas satu per satu.

1. Key-Value Storage

Key-value storage menyimpan data dalam pasangan:

key → value

Contohnya:

theme → dark
language → id
counter → 10
isFirstLaunch → false

Pendekatan ini cocok untuk data sederhana.

Contoh penggunaan:

  • dark mode,
  • bahasa aplikasi,
  • setting pengguna,
  • flag tertentu,
  • nilai sederhana,
  • preferensi aplikasi.

Salah satu package yang umum digunakan adalah:

shared_preferences

Dokumentasi Flutter menggunakan package tersebut untuk menyimpan data key-value secara persistent.

Mengenal shared_preferences

Tambahkan package:

flutter pub add shared_preferences

Package ini dapat menyimpan tipe data sederhana seperti:

  • int
  • double
  • bool
  • String
  • List<String>

Dokumentasi package saat ini juga menyediakan tiga API: SharedPreferences, SharedPreferencesAsync, dan SharedPreferencesWithCache. Untuk kode baru, dokumentasi package merekomendasikan API yang lebih baru seperti SharedPreferencesAsync atau SharedPreferencesWithCache; SharedPreferences merupakan API legacy.

Untuk pemula, konsep utamanya tetap sama:

Save
 ↓
Key + Value
 ↓
Read
 ↓
Update
 ↓
Remove

Menyimpan Data Sederhana

Dengan API modern SharedPreferencesAsync, contoh sederhana:

import 'package:shared_preferences/shared_preferences.dart';

final prefs = SharedPreferencesAsync();

await prefs.setString(
  'username',
  'Budi',
);

Untuk angka:

await prefs.setInt(
  'counter',
  10,
);

Boolean:

await prefs.setBool(
  'darkMode',
  true,
);

Membaca Data

Data yang telah disimpan dapat dibaca kembali.

final prefs = SharedPreferencesAsync();

final username = await prefs.getString(
  'username',
);

Untuk integer:

final counter = await prefs.getInt(
  'counter',
);

Jika data belum tersedia, hasilnya dapat berupa null.

Karena itu, Anda dapat memberikan nilai default:

final counter =
    await prefs.getInt('counter') ?? 0;

Konsep seperti ini sangat sering digunakan dalam aplikasi Flutter.

Menghapus Data

Untuk menghapus sebuah key:

await prefs.remove('username');

Dengan demikian:

setString()
getString()
remove()

sudah cukup untuk banyak kebutuhan sederhana.

Kapan Menggunakan shared_preferences?

Gunakan key-value storage ketika datanya sederhana.

Contohnya:

darkMode = true
language = "id"
onboardingCompleted = true
fontSize = 16

Jangan gunakan shared_preferences sebagai database utama untuk menyimpan ribuan produk, transaksi, atau data relasional.

Dokumentasi Flutter menyebutkan bahwa key-value storage ditujukan untuk koleksi key-value yang relatif kecil dan tidak dirancang untuk menyimpan data dalam jumlah besar.

2. File Storage

Selain key-value storage, Flutter juga dapat membaca dan menulis file ke perangkat.

Pendekatan ini cocok ketika aplikasi perlu menyimpan:

  • file teks,
  • JSON file,
  • hasil export,
  • dokumen,
  • cache file,
  • file yang perlu dibaca kembali.

Flutter menggunakan kombinasi path_provider dan dart:io dalam recipe resmi untuk membaca dan menulis file pada perangkat mobile dan desktop.

Baca juga:  Roadmap Belajar Flutter dari Nol Sampai Siap Kerja

Contoh sederhananya:

import 'dart:io';
import 'package:path_provider/path_provider.dart';

Kemudian mendapatkan lokasi penyimpanan:

final directory =
    await getApplicationDocumentsDirectory();

final file = File(
  '${directory.path}/data.txt',
);

Menulis file:

await file.writeAsString(
  'Hello Flutter',
);

Membaca:

final content =
    await file.readAsString();

print(content);

Kapan Menggunakan File Storage?

File storage lebih cocok ketika yang ingin disimpan memang berbentuk file.

Misalnya aplikasi editor dokumen:

Flutter
   ↓
Document
   ↓
File
   ↓
Device

Atau aplikasi yang menyimpan data JSON:

JSON
   ↓
data.json
   ↓
Local Device

Namun, jika data memiliki banyak record, pencarian, filtering, sorting, relasi, dan CRUD kompleks, database biasanya lebih sesuai.

Apa Itu Database?

Database adalah sistem yang digunakan untuk menyimpan dan mengelola data secara terstruktur.

Misalnya aplikasi toko memiliki tabel:

products

dengan kolom:

id
name
price
stock
category

Contohnya:

idnamepricestock
1Laptop120000005
2Mouse25000020
3Keyboard75000010

Database memungkinkan aplikasi melakukan:

Create
Read
Update
Delete

atau biasa disingkat: CRUD

Apa Itu SQLite?

SQLite adalah database SQL yang dapat disimpan langsung sebagai file pada perangkat.

Untuk Flutter, salah satu package yang populer untuk SQLite adalah:

sqflite

Dokumentasi Flutter menggunakan sqflite untuk contoh persistence menggunakan SQLite. Recipe tersebut menunjukkan proses membuka database, membuat tabel, insert, query, update, dan delete.

Package sqflite saat ini mendukung Android, iOS, dan macOS secara langsung. Untuk Linux dan Windows terdapat pendekatan sqflite_common_ffi, sedangkan dukungan web melalui implementasi FFI web masih bersifat eksperimental.

Mengapa Menggunakan SQLite?

SQLite cocok ketika aplikasi memiliki data yang lebih kompleks.

Misalnya:

Todo App
Inventory
Expense Tracker
Offline Notes
Product Catalog
Local Cart

SQLite memberikan kemampuan untuk:

  • membuat tabel,
  • menyimpan banyak record,
  • melakukan query,
  • filtering,
  • sorting,
  • update,
  • delete,
  • relasi antar tabel,
  • transaksi.

Berbeda dengan key-value storage yang hanya cocok untuk data sederhana.

Menambahkan sqflite

Untuk project Flutter mobile sederhana:

flutter pub add sqflite path

Dokumentasi Flutter menggunakan sqflite dan path untuk contoh SQLite. Package path membantu membangun lokasi file database dengan cara yang lebih aman lintas platform.

Kemudian:

import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';

Membuka Database

Database harus dibuka sebelum digunakan.

Contoh:

final database = openDatabase(
  join(
    await getDatabasesPath(),
    'app_database.db',
  ),
);

Dalam aplikasi Flutter, operasi tersebut dilakukan secara asynchronous.

Karena itu:

await

diperlukan.

Membuat Table

Misalnya kita membuat aplikasi Todo.

Struktur tabel:

todos

id
title
completed

SQL sederhananya:

CREATE TABLE todos(
  id INTEGER PRIMARY KEY,
  title TEXT,
  completed INTEGER
)

Dalam Flutter:

final database = openDatabase(
  join(
    await getDatabasesPath(),
    'app_database.db',
  ),
  onCreate: (db, version) {
    return db.execute(
      '''
      CREATE TABLE todos(
        id INTEGER PRIMARY KEY,
        title TEXT,
        completed INTEGER
      )
      ''',
    );
  },
  version: 1,
);

Flutter menggunakan version database untuk membantu menangani perubahan schema dari waktu ke waktu.

Memahami Database Schema

Schema adalah struktur database.

Misalnya:

Database
│
└── todos
    ├── id
    ├── title
    └── completed

Ketika aplikasi berkembang, schema mungkin berubah.

Versi pertama:

todos
- id
- title

Versi kedua:

todos
- id
- title
- completed

Versi ketiga:

todos
- id
- title
- completed
- created_at

Perubahan tersebut disebut:

Database Migration

Migration penting ketika aplikasi sudah digunakan oleh pengguna dan struktur database harus berubah tanpa menghilangkan data yang sudah ada.

Membuat Model Dart

Sama seperti REST API Flutter: Panduan HTTP, JSON, dan Pengolahan Data pada Level 8, database juga sebaiknya memiliki model.

Contoh:

class Todo {
  final int id;
  final String title;
  final bool completed;

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

Kemudian buat fungsi untuk mengubah model menjadi Map:

Map<String, Object?> toMap() {
  return {
    'id': id,
    'title': title,
    'completed': completed ? 1 : 0,
  };
}

Mengapa bool diubah menjadi 0 atau 1?

SQLite memiliki tipe data yang berbeda dari Dart. sqflite mendokumentasikan tipe seperti INTEGER, REAL, TEXT, dan BLOB; bool dapat direpresentasikan menggunakan INTEGER dengan nilai 0 dan 1.

Insert Data

Sekarang kita dapat menyimpan Todo:

Future<void> insertTodo(
  Todo todo,
) async {
  final db = await database;

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

Contoh penggunaannya:

await insertTodo(
  Todo(
    id: 1,
    title: 'Belajar Flutter',
    completed: false,
  ),
);

Read Data

Untuk membaca data:

Future<List<Map<String, Object?>>> getTodos()
    async {
  final db = await database;

  return db.query('todos');
}

Hasil query berupa list.

Kemudian dapat diubah kembali menjadi model:

final rows = await getTodos();

final todos = rows.map((row) {
  return Todo(
    id: row['id'] as int,
    title: row['title'] as String,
    completed:
        (row['completed'] as int) == 1,
  );
}).toList();

Sekarang aplikasi memiliki:

List<Todo>

yang dapat digunakan oleh UI.

Update Data

Misalnya pengguna menyelesaikan sebuah Todo.

Gunakan:

await db.update(
  'todos',
  {
    'completed': 1,
  },
  where: 'id = ?',
  whereArgs: [todo.id],
);

Perhatikan penggunaan:

where: 'id = ?'

dan:

whereArgs: [todo.id]

Dokumentasi Flutter secara khusus menyarankan penggunaan whereArgs untuk parameter pada klausa where, bukan string interpolation, untuk membantu mencegah SQL injection.

Delete Data

Untuk menghapus:

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

Dengan demikian, operasi database dasar sudah lengkap:

INSERT
   ↓
SELECT
   ↓
UPDATE
   ↓
DELETE

CRUD dalam Flutter

Mari sederhanakan:

OperasiSQLiteTujuan
Createinsert()Menambah data
Readquery()Membaca data
Updateupdate()Mengubah data
Deletedelete()Menghapus data

CRUD merupakan konsep yang sangat penting karena hampir semua aplikasi dengan database akan menggunakannya.

Query dengan WHERE

Tidak semua data harus diambil. Misalnya hanya ingin mengambil Todo yang belum selesai:

final result = await db.query(
  'todos',
  where: 'completed = ?',
  whereArgs: [0],
);

Atau mencari berdasarkan ID:

final result = await db.query(
  'todos',
  where: 'id = ?',
  whereArgs: [10],
);

Inilah salah satu keunggulan database dibandingkan penyimpanan key-value sederhana.

Baca juga:  REST API Flutter: Panduan HTTP, JSON, dan Pengolahan Data

Sorting Data

Misalnya ingin mengurutkan data:

final result = await db.query(
  'todos',
  orderBy: 'id DESC',
);

Dengan demikian data terbaru dapat ditampilkan terlebih dahulu.

Limit Data

Database juga dapat membatasi jumlah hasil.

Misalnya:

final result = await db.query(
  'todos',
  limit: 10,
);

Konsep seperti ini akan berguna ketika jumlah data sudah besar.

Relasi Antar Tabel

Aplikasi nyata sering memiliki lebih dari satu tabel.

Misalnya aplikasi e-commerce:

users
products
categories
orders
order_items

Relasi dapat dibuat:

Category
   ↓
Products
   ↓
Order Items
   ↓
Orders
   ↓
User

Contoh sederhana:

CREATE TABLE categories(
  id INTEGER PRIMARY KEY,
  name TEXT
);

CREATE TABLE products(
  id INTEGER PRIMARY KEY,
  name TEXT,
  category_id INTEGER,
  FOREIGN KEY(category_id)
    REFERENCES categories(id)
);

Flutter juga mendokumentasikan contoh hubungan antar tabel menggunakan foreign key.

Untuk pemula, Anda belum perlu menguasai seluruh konsep database relational sekaligus. Namun, memahami primary key dan foreign key akan sangat membantu ketika masuk ke project nyata.

Primary Key

Primary key adalah kolom yang digunakan sebagai identitas unik sebuah record.

Contohnya:

id

Data:

idname
1Laptop
2Mouse
3Keyboard

Tidak boleh ada dua record dengan ID yang sama jika id digunakan sebagai primary key.

Contoh:

id INTEGER PRIMARY KEY

Foreign Key

Foreign key digunakan untuk menghubungkan tabel.

Misalnya:

products
id
category_id

category_id dapat merujuk ke:

categories.id

Dengan demikian:

categories
    ↓
products

Konsep ini akan menjadi semakin penting ketika Anda membuat aplikasi yang memiliki banyak jenis data.

Database vs REST API

Setelah mempelajari Level 8 dan Level 9, Anda harus memahami bahwa REST API dan database lokal memiliki fungsi berbeda.

REST APILocal Database
Berkomunikasi dengan serverMenyimpan data di perangkat
Membutuhkan jaringan untuk data onlineDapat digunakan tanpa internet
Data terpusatData lokal
Cocok untuk sinkronisasiCocok untuk offline/persistence
Backend biasanya mengelola database serverAplikasi mengelola database lokal

Dalam aplikasi nyata, keduanya sering digunakan bersama.

Contohnya:

             REST API
                ↓
           Server Data
                ↓
          Flutter App
             ↙   ↘
        State     Local DB
                  ↓
               Offline

Menggabungkan REST API dengan Local Database

Inilah tahap yang lebih menarik. Misalnya aplikasi berita mengambil data:

Internet
   ↓
REST API
   ↓
Flutter
   ↓
SQLite

Ketika internet tersedia:

API
 ↓
Data baru
 ↓
Simpan SQLite
 ↓
Tampilkan

Ketika internet tidak tersedia:

SQLite
 ↓
Data terakhir
 ↓
Tampilkan

Pola seperti ini merupakan dasar dari aplikasi offline-capable atau offline-first.

Dokumentasi arsitektur Flutter juga membahas penyimpanan persisten dan offline-first sebagai bagian dari pola aplikasi yang lebih matang.

Local Database untuk Cache

Database lokal tidak selalu berarti sumber data utama. Kadang database lokal hanya digunakan sebagai cache.

Contohnya:

REST API
   ↓
Response
   ↓
SQLite
   ↓
Cache

Saat aplikasi dibuka:

Ada internet?
   ├── Ya → Ambil data API
   │          ↓
   │       Update cache
   │
   └── Tidak → Ambil cache lokal

Strategi ini dapat membuat aplikasi terasa lebih cepat dan lebih tahan terhadap koneksi internet yang tidak stabil.

Arsitektur Penyimpanan Data

Ketika aplikasi semakin besar, jangan menempatkan semua database logic di dalam widget.

Struktur sederhana:

lib/
├── models/
│   └── todo.dart
│
├── database/
│   └── database_service.dart
│
├── repositories/
│   └── todo_repository.dart
│
├── screens/
│   └── todo_page.dart
│
└── main.dart

Flutter saat ini juga memiliki panduan arsitektur persistence menggunakan pemisahan UI layer, domain/model layer, repository, dan database service.

Database Service

Database service bertanggung jawab terhadap operasi database.

Contohnya:

DatabaseService
     │
     ├── open()
     ├── insert()
     ├── query()
     ├── update()
     └── delete()

UI tidak perlu mengetahui bagaimana database dibuka.

Repository

Repository menjadi lapisan antara UI/application logic dengan sumber data.

Contohnya:

UI
 ↓
ViewModel / State
 ↓
Repository
 ↓
Database Service
 ↓
SQLite

Repository dapat menjadi tempat untuk menentukan sumber data.

Misalnya:

ProductRepository
       ↓
 ┌──────────────┐
 │              │
API          SQLite

Dengan pola tersebut, aplikasi dapat mengambil data dari API sekaligus menggunakan cache lokal.

Mengapa Tidak Langsung Menggunakan SQLite di Widget?

Bayangkan setiap screen memiliki:

openDatabase()
insert()
query()
update()
delete()

Jika aplikasi memiliki 20 screen, logic database dapat tersebar ke banyak tempat.

Akibatnya:

  • sulit dirawat,
  • sulit diuji,
  • mudah terjadi duplikasi,
  • perubahan database menjadi lebih rumit.

Lebih baik:

Widget
  ↓
Repository
  ↓
Database

Pemisahan ini akan menjadi dasar penting ketika Anda masuk ke Level 12 — Architecture.

Shared Preferences vs SQLite

Perbedaan paling penting:

KebutuhanPilihan
Dark modeshared_preferences
Bahasa aplikasishared_preferences
Boolean sederhanashared_preferences
User preferenceshared_preferences
Banyak recordSQLite
Query dataSQLite
Relasi tabelSQLite
CRUD kompleksSQLite
Offline catalogSQLite
TransactionSQLite

Jadi jangan berpikir bahwa SQLite selalu lebih baik.

Yang benar adalah:

Gunakan teknologi penyimpanan sesuai bentuk dan kebutuhan data.

File Storage vs Database

File storage juga memiliki tempatnya sendiri.

Gunakan file jika:

Data → File

Misalnya:

  • PDF,
  • TXT,
  • JSON,
  • dokumen,
  • file export.

Gunakan database jika:

Data → Records

Misalnya:

  • users,
  • products,
  • orders,
  • transactions,
  • todos.

Flutter menyediakan path_provider untuk membantu menemukan lokasi direktori file aplikasi pada platform yang didukung. Recipe resmi untuk file storage menggunakan dart:io bersama path_provider.

Jangan Menyimpan Password Secara Sembarangan

Ini merupakan bagian penting.

shared_preferences bukan tempat yang tepat untuk menyimpan data sensitif hanya karena praktis.

Misalnya:

password
private key
secret credential
sensitive token

Untuk kebutuhan keamanan tertentu, gunakan solusi secure storage yang memang dirancang untuk data sensitif. Pada tahap Level 11 nanti, pembahasan authentication dan penyimpanan credential dapat dilakukan lebih mendalam.

Untuk Level 9, prinsip yang perlu diingat adalah:

Local storage bukan berarti semua data aman untuk disimpan tanpa perlindungan.

Database Migration

Bayangkan versi pertama aplikasi memiliki:

todos
- id
- title

Kemudian versi baru membutuhkan:

todos
- id
- title
- completed

Database pengguna lama masih memiliki struktur versi pertama. Aplikasi perlu melakukan migration.

Baca juga:  Setup Flutter Development Environment untuk Pemula

Contoh konsep:

version: 2,
onUpgrade: (db, oldVersion, newVersion) async {
  if (oldVersion < 2) {
    await db.execute(
      'ALTER TABLE todos '
      'ADD COLUMN completed INTEGER DEFAULT 0',
    );
  }
},

Migration harus dirancang dengan hati-hati agar data pengguna tidak hilang ketika aplikasi diperbarui.

Transaction

Ketika beberapa operasi database harus dianggap sebagai satu kesatuan, gunakan transaction.

Misalnya:

Buat Order
   ↓
Buat Order Item
   ↓
Kurangi Stock

Jika operasi kedua gagal, Anda mungkin tidak ingin operasi pertama tetap tersimpan.

Secara konsep:

BEGIN
   ↓
Operation 1
   ↓
Operation 2
   ↓
Operation 3
   ↓
COMMIT

Jika terjadi masalah:

ROLLBACK

Package sqflite juga menyediakan dukungan transaction dan batch. Untuk pemula, cukup pahami konsepnya terlebih dahulu.

Mini Project Level 9: Todo App dengan SQLite

Sekarang saatnya menggabungkan konsep.

Target:

Todo App

Fitur:

Create Todo
Read Todo
Update Todo
Delete Todo

Struktur:

lib/
├── models/
│   └── todo.dart
│
├── database/
│   └── database_service.dart
│
├── repositories/
│   └── todo_repository.dart
│
├── screens/
│   └── todo_page.dart
│
└── main.dart

Database:

todos
---------------------
id
title
completed

Flow:

User
 ↓
Todo Page
 ↓
Repository
 ↓
Database Service
 ↓
SQLite

Contoh Database Service

Contoh sederhana:

class DatabaseService {
  Future<Database> open() async {
    return openDatabase(
      join(
        await getDatabasesPath(),
        'todo_database.db',
      ),
      version: 1,
      onCreate: (db, version) {
        return db.execute(
          '''
          CREATE TABLE todos(
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            title TEXT,
            completed INTEGER
          )
          ''',
        );
      },
    );
  }
}

Kemudian repository dapat menggunakan service tersebut.

Contoh Repository

class TodoRepository {
  TodoRepository(
    this.databaseService,
  );

  final DatabaseService databaseService;

  Future<List<Todo>> getTodos() async {
    final db =
        await databaseService.open();

    final rows = await db.query(
      'todos',
      orderBy: 'id DESC',
    );

    return rows.map((row) {
      return Todo.fromMap(row);
    }).toList();
  }
}

Dengan struktur tersebut, UI tidak perlu berinteraksi langsung dengan SQL.

Menghubungkan Database dengan State Management

Level 7 sudah membahas state management. Sekarang database dapat menjadi sumber data bagi state.

Contohnya:

SQLite
   ↓
Repository
   ↓
State
   ↓
UI

Ketika user menambahkan Todo:

Button
 ↓
Repository.insert()
 ↓
SQLite
 ↓
Reload / Update State
 ↓
UI rebuild

Ini merupakan kombinasi konsep yang sangat penting.

Database dan Offline-First

Setelah memahami local storage, Anda mulai dapat membangun aplikasi yang tetap memiliki fungsi ketika internet terputus.

Contohnya:

             Internet
                │
                ▼
             REST API
                │
                ▼
             Repository
                │
                ▼
              SQLite
                │
                ▼
                UI

Saat offline:

             No Internet
                  X
                  │
                  ▼
              SQLite
                  │
                  ▼
                  UI

Strategi offline-first akan menjadi semakin penting ketika Anda membangun aplikasi produksi.

Kesalahan Umum Saat Belajar Database Flutter

1. Menggunakan shared_preferences untuk Semua Data

Jangan gunakan key-value storage untuk database kompleks.

2. Tidak Membuat Model

Hindari menggunakan Map di seluruh aplikasi jika struktur data sudah jelas.

Lebih baik:

Database
 ↓
Map
 ↓
Model
 ↓
State
 ↓
UI

3. Menaruh SQL di Widget

UI sebaiknya tidak menjadi tempat utama untuk SQL query.

4. Tidak Memikirkan Migration

Begitu aplikasi digunakan pengguna, perubahan schema harus direncanakan.

5. Mengabaikan Error

Database operation juga dapat gagal.

Contohnya:

  • database tidak dapat dibuka,
  • schema bermasalah,
  • query salah,
  • data tidak sesuai,
  • migration gagal.

6. Tidak Menggunakan Parameter Query

Hindari:

where: "id = ${todo.id}"

Gunakan:

where: 'id = ?',
whereArgs: [todo.id],

Selain lebih aman, pendekatan tersebut sesuai dengan pola yang ditunjukkan dokumentasi Flutter untuk query SQLite.

Latihan Level 9

Agar konsep benar-benar dipahami, kerjakan beberapa latihan berikut.

Latihan 1 — Counter Persistence

Buat counter yang tetap memiliki nilai terakhir setelah aplikasi ditutup.

Gunakan:

shared_preferences

Latihan 2 — Dark Mode

Buat fitur:

Light
Dark

Simpan pilihan pengguna secara lokal.

Latihan 3 — Todo SQLite

Buat:

Add Todo
Edit Todo
Delete Todo
Complete Todo

Gunakan SQLite.

Latihan 4 — Search Todo

Tambahkan pencarian:

Search
 ↓
SQLite query
 ↓
Filtered Result

Latihan 5 — REST API + SQLite

Gabungkan materi Level 8 dan Level 9:

REST API
   ↓
Fetch Data
   ↓
SQLite
   ↓
Local Cache
   ↓
UI

Kemudian buat aplikasi yang tetap dapat menampilkan data terakhir ketika internet tidak tersedia.

Checklist Level 9

Sebelum melanjutkan ke Level 10, pastikan Anda sudah memahami:

Local Storage

  • Memahami local storage
  • Memahami persistence
  • Memahami key-value storage
  • Mengetahui fungsi shared_preferences
  • Dapat menyimpan String
  • Dapat menyimpan Integer
  • Dapat menyimpan Boolean
  • Dapat membaca data
  • Dapat menghapus data

File Storage

  • Memahami file storage
  • Mengetahui fungsi path_provider
  • Mengetahui dart:io
  • Dapat membaca file
  • Dapat menulis file

SQLite

  • Memahami SQLite
  • Memahami table
  • Memahami column
  • Memahami row
  • Memahami primary key
  • Memahami foreign key
  • Dapat membuat database
  • Dapat membuat table
  • Dapat melakukan INSERT
  • Dapat melakukan SELECT
  • Dapat melakukan UPDATE
  • Dapat melakukan DELETE
  • Memahami WHERE
  • Memahami query parameter
  • Memahami database version
  • Memahami migration dasar

Architecture

  • Memahami Database Service
  • Memahami Repository
  • Memahami Model
  • Memahami hubungan Database → Repository → State → UI

Advanced Concept

  • Memahami cache lokal
  • Memahami offline data
  • Mengetahui konsep offline-first
  • Mengetahui transaction
  • Memahami perbedaan API dan local database

Sampai Level 9, Anda sudah mempelajari sebagian besar fondasi teknis penting:

Flutter
 ↓
Dart
 ↓
Widgets
 ↓
Layout
 ↓
Navigation
 ↓
State Management
 ↓
REST API
 ↓
Database
 ↓
Local Storage

Namun, belajar teori saja belum cukup untuk menjadi Flutter Developer. Pada Level 10, Anda akan mulai menggabungkan berbagai kemampuan tersebut ke dalam sebuah real project Flutter.

Konsepnya akan berubah dari:

"Bagaimana cara menggunakan fitur Flutter?"

menjadi:

"Bagaimana cara membangun aplikasi Flutter?"

Anda akan mulai belajar menentukan:

  • ide aplikasi,
  • fitur,
  • struktur project,
  • UI flow,
  • navigation,
  • state management,
  • REST API,
  • database,
  • validasi,
  • error handling,
  • dan pengembangan fitur secara bertahap.

Dengan kata lain, Level 10 menjadi titik penting untuk mengubah pengetahuan yang telah dipelajari dari Level 0–9 menjadi sebuah aplikasi yang benar-benar dapat digunakan.

Kesimpulan

Database dan local storage merupakan kemampuan penting bagi Flutter Developer karena aplikasi nyata tidak selalu bergantung pada data dari server.

Pada Level 9, Anda telah mempelajari tiga pendekatan utama:

Key-Value
   ↓
shared_preferences

File
   ↓
path_provider + dart:io

Database
   ↓
SQLite + sqflite

Gunakan key-value storage untuk data sederhana seperti preferensi aplikasi. Gunakan file storage ketika aplikasi perlu menyimpan file atau data berbasis file. Untuk data terstruktur yang membutuhkan query dan CRUD, gunakan database seperti SQLite. Flutter secara resmi menyediakan recipe untuk ketiga kebutuhan persistence tersebut.

Anda juga telah belajar:

CREATE
READ
UPDATE
DELETE

serta konsep:

Model
 ↓
Database Service
 ↓
Repository
 ↓
State
 ↓
UI

Struktur tersebut menjadi dasar yang sangat baik untuk aplikasi Flutter yang lebih besar. Yang tidak kalah penting adalah memahami hubungan antara Level 8 dan Level 9:

             REST API
                ↓
             Flutter
                ↓
          Local Database
                ↓
             Offline

Dengan kombinasi REST API dan local storage, aplikasi dapat mengambil data dari server sekaligus mempertahankan data tertentu di perangkat.

Setelah menguasai konsep ini, Anda sudah memiliki fondasi yang cukup untuk mulai membangun aplikasi Flutter yang lebih realistis.

Selanjutnya, kita masuk ke Level 10 — Real Project Flutter, yaitu tahap ketika seluruh kemampuan dari Level 0 sampai Level 9 mulai digabungkan menjadi sebuah project aplikasi yang lebih lengkap.

Bagikan

Tinggalkan komentar

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *

Artikel Terkait
rest api flutter
CodingFlutter

REST API Flutter: Panduan HTTP, JSON, dan Pengolahan Data

Pelajari REST API Flutter dari nol, mulai dari HTTP request, GET, POST,...

navigation dan state management flutter
CodingFlutter

Navigation dan State Management Flutter untuk Pemula

Pelajari Navigation dan State Management Flutter dari nol, mulai dari Navigator, Route,...

layout responsive ui flutter
CodingFlutter

Layout dan Responsive UI Flutter: Panduan untuk Pemula

Pelajari layout dan responsive UI Flutter dari nol, mulai dari constraints, Row,...

menguasai widget flutter
CodingFlutter

Menguasai Widget Flutter: Jenis, Fungsi, dan Contohnya untuk Pemula

Pelajari widget Flutter dari nol, mulai dari widget tree, StatelessWidget, StatefulWidget, BuildContext,...