Untuk mendemostrasikan cara kerja database sqlite, kita akan membuat contoh aplikasi yang menyimpan daftar kontak (nama, email, nomor HP, dan perusahaan) ke dalam database. Aplikasi ini terdiri dari dua layar. Layar pertama bertugas untuk menampilkan data yang tersimpan di dalam database, sedangkan layar kedua bertugas untuk menampilkan form entri dan perubahan data. Tampilkan kedua layar ini dapat dilihat pada gambar berikut :
Ketika pengguna memilih tombol FAB (Floating Action Button), aplikasi akan menampilkan form entri yang berguna untuk penambahan data baru. Setelah data di dalam form diisi dan pengguna memilih tombol “Add”, data tersebut akan disimpan ke dalam database dan ditampilkan ke layar pertama. Untuk kembali ke layar pertama dapat menekan Button Navigation Up yang ada di ActionBar.
Untuk mengubah data di dalam database, pilih icon “edit” yang terdapat pada data yang tampil pada layar pertama. Untuk menghapus data, gunakan icon “delete” yang terdapat pada bagian kanan data. Tabel di dalam database yang dibutuhkan untuk pembuatan aplikasi di atas dibuat menggunakan struktur berikut :
Kolom | Tipe Data |
id | int |
name | String |
mobileNo | String |
String | |
company | String |
Menyiapkan Proyek Aplikasi
Seperti biasa, buatlah aplikasi baru di dalam direktori kerja anda, misalnya dengan nama contact_apps
- $cd ~/flutterprojects/
- $flutter create contact_apps
Dalam direktori lib, buatlah dua sub-direktori baru dengan nama database, dan model. Selanjutnya buat empat file dart dengan nama db_helper.dart, kontak.dart, form_kontak.dart, dan list_kontak_dart.
Struktur direktori untuk aplikasi “contact apps” dapat dilihat pada gambar dibawah ini.
Kemudian untuk dapat menggunakan database SQLite, tambahkan widget sqflite
pada file pubspec.yaml.
sqflite: ^2.2.0+3
tambahkan library sqflite
dibawah cupertino_icons, kemudian save (CTRL + S) untuk mengimplementasikan library sqflite
.
Membuat Model Data
Model adalah kelas yang memodelkan data yang akan digunakan oleh aplikasi. Dalam projek ini, kita akan membuat model dengan nama kontak. Isi file kontak.dart dengan kode program berikut.
class Kontak { int? id; String? name; String? mobileNo; String? email; String? company; Kontak({this.id, this.name, this.mobileNo, this.email, this.company}); Map<String, dynamic> toMap() { var map = <String, dynamic>{}; if (id != null) { map['id'] = id; } map['name'] = name; map['mobileNo'] = mobileNo; map['email'] = email; map['company'] = company; return map; } Kontak.fromMap(Map<String, dynamic> map) { id = map['id']; name = map['name']; mobileNo = map['mobileNo']; email = map['email']; company = map['company']; } }
Membuat Kelas Helper
Kelas helper adalah kelas pembantu atau pendukung yang dibutuhkan aplikasi. Dalam projek ini, kelas helper akan digunakan untuk membuat database, tabel, dan melakukan CRUD (Create, Read, Update, Delete). Kelas ini akan menggunakan model data yang sudah dibuat pada sub-bab sebelumnya. Isikan kode berikut ke file db_helper.dart yang ada di direktori database yang ada di projek kita.
//dbhelper ini dibuat untuk //membuat database, membuat tabel, proses insert, read, update dan delete import 'package:contact_apps/model/kontak.dart'; import 'package:sqflite/sqflite.dart'; // ignore: depend_on_referenced_packages import 'package:path/path.dart'; class DbHelper { static final DbHelper _instance = DbHelper._internal(); static Database? _database; //inisialisasi beberapa variabel yang dibutuhkan final String tableName = 'tableKontak'; final String columnId = 'id'; final String columnName = 'name'; final String columnMobileNo = 'mobileNo'; final String columnEmail = 'email'; final String columnCompany = 'company'; DbHelper._internal(); factory DbHelper() => _instance; //cek apakah database ada Future<Database?> get _db async { if (_database != null) { return _database; } _database = await _initDb(); return _database; } Future<Database?> _initDb() async { String databasePath = await getDatabasesPath(); String path = join(databasePath, 'kontak.db'); return await openDatabase(path, version: 1, onCreate: _onCreate); } //membuat tabel dan field-fieldnya Future<void> _onCreate(Database db, int version) async { var sql = "CREATE TABLE $tableName($columnId INTEGER PRIMARY KEY, " "$columnName TEXT," "$columnMobileNo TEXT," "$columnEmail TEXT," "$columnCompany TEXT)"; await db.execute(sql); } //insert ke database Future<int?> saveKontak(Kontak kontak) async { var dbClient = await _db; return await dbClient!.insert(tableName, kontak.toMap()); } //read database Future<List?> getAllKontak() async { var dbClient = await _db; var result = await dbClient!.query(tableName, columns: [ columnId, columnName, columnCompany, columnMobileNo, columnEmail ]); return result.toList(); } //update database Future<int?> updateKontak(Kontak kontak) async { var dbClient = await _db; return await dbClient!.update(tableName, kontak.toMap(), where: '$columnId = ?', whereArgs: [kontak.id]); } //hapus database Future<int?> deleteKontak(int id) async { var dbClient = await _db; return await dbClient! .delete(tableName, where: '$columnId = ?', whereArgs: [id]); } }
Membangun Tampilan Antarmuka Aplikasi
Setelah model dan helper disiapkan, selanjutnya kita akan membuat tampilan antarmuka aplikasi. Pada proyek ini tampilan antarmuka aplikasi terdiri dari 2 layar (Screen). Layar pertama digunakan untuk menampilkan data kontak dalam bentuk daftar (List). Layar kedua digunakan untuk menampilkan form input data. Pada layar kedua ini pula pengguna dapat melakukan edit data.
Layar Utama
Layar utama adalah layar yang akan tampil pada saat aplikasi dijalankan. Layar utama pada proyek ini dibuat pada file list_kontak.dart. Tampilan layar utama berbentuk list yang berisi informasi kontak yang diisikan pengguna. Kode program untuk mendesain layar utama adalah sebagai berikut
import 'package:flutter/material.dart'; import 'form_kontak.dart'; import 'database/db_helper.dart'; import 'model/kontak.dart'; class ListKontakPage extends StatefulWidget { const ListKontakPage({Key? key}) : super(key: key); @override // ignore: library_private_types_in_public_api _ListKontakPageState createState() => _ListKontakPageState(); } class _ListKontakPageState extends State<ListKontakPage> { List<Kontak> listKontak = []; DbHelper db = DbHelper(); @override void initState() { //menjalankan fungsi getallkontak saat pertama kali dimuat _getAllKontak(); super.initState(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Center( child: Text("Contact Apps"), ), ), body: ListView.builder( itemCount: listKontak.length, itemBuilder: (context, index) { Kontak kontak = listKontak[index]; return Card( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15.0), side: const BorderSide(color: Colors.red, width: 3), ), margin: const EdgeInsets.only(left: 5, right: 5, top: 10), // elevation: 50, // shadowColor: Colors.black, color: const Color.fromARGB(255, 248, 249, 248), child: Padding( padding: const EdgeInsets.only(left: 8, top: 8, bottom: 8), child: ListTile( leading: const Icon( Icons.person, size: 50, ), title: Text('${kontak.name}'), subtitle: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only( top: 8, ), child: Text("Email: ${kontak.email}"), ), Padding( padding: const EdgeInsets.only( top: 8, ), child: Text("Phone: ${kontak.mobileNo}"), ), Padding( padding: const EdgeInsets.only( top: 8, ), child: Text("Company: ${kontak.company}"), ) ], ), trailing: FittedBox( fit: BoxFit.fill, child: Row( children: [ // button edit IconButton( onPressed: () { _openFormEdit(kontak); }, icon: const Icon(Icons.edit)), // button hapus IconButton( icon: const Icon(Icons.delete), onPressed: () { //membuat dialog konfirmasi hapus AlertDialog hapus = AlertDialog( title: const Text("Information"), content: SizedBox( height: 100, child: Column( children: [ Text( "Yakin ingin Menghapus Data ${kontak.name}") ], ), ), //terdapat 2 button. //jika ya maka jalankan _deleteKontak() dan tutup dialog //jika tidak maka tutup dialog actions: [ TextButton( onPressed: () { _deleteKontak(kontak, index); Navigator.pop(context); }, child: const Text("Ya")), TextButton( child: const Text('Tidak'), onPressed: () { Navigator.pop(context); }, ), ], ); showDialog( context: context, builder: (context) => hapus); }, ) ], ), ), ), ), ); }), //membuat button mengapung di bagian bawah kanan layar floatingActionButton: FloatingActionButton( child: const Icon(Icons.add), onPressed: () { _openFormCreate(); }, ), ); } //mengambil semua data Kontak Future<void> _getAllKontak() async { //list menampung data dari database var list = await db.getAllKontak(); //ada perubahanan state setState(() { //hapus data pada listKontak listKontak.clear(); //lakukan perulangan pada variabel list for (var kontak in list!) { //masukan data ke listKontak listKontak.add(Kontak.fromMap(kontak)); } }); } //menghapus data Kontak Future<void> _deleteKontak(Kontak kontak, int position) async { await db.deleteKontak(kontak.id!); setState(() { listKontak.removeAt(position); }); } // membuka halaman tambah Kontak Future<void> _openFormCreate() async { var result = await Navigator.push( context, MaterialPageRoute(builder: (context) => const FormKontak())); if (result == 'save') { await _getAllKontak(); } } //membuka halaman edit Kontak Future<void> _openFormEdit(Kontak kontak) async { var result = await Navigator.push(context, MaterialPageRoute(builder: (context) => FormKontak(kontak: kontak))); if (result == 'update') { await _getAllKontak(); } } }
Simpan kode diatas ke dalam file contact_apps/lib/list_kontak.dart
Layar Form Tambah / Ubah Kontak
Layar kedua adalah layar yang berfungsi untuk menambah data baru ke dalam database atau untuk mengubah data. Pada saat Floating Action Button ditekan maka akan menjadi Form Tambah data, namun saat icon “edit” ditekan akan berubah menjadi Form Edit Data. Pada saat menjadi Form Edit Data, maka data akan tersedia pada textfield
, sehingga kita tinggal mengganti saja dengan data baru. Kode untuk layar kedua adalah sebagai berikut.
import 'package:flutter/material.dart'; import 'database/db_helper.dart'; import 'model/kontak.dart'; class FormKontak extends StatefulWidget { final Kontak? kontak; const FormKontak({super.key, this.kontak}); @override // ignore: library_private_types_in_public_api _FormKontakState createState() => _FormKontakState(); } class _FormKontakState extends State<FormKontak> { DbHelper db = DbHelper(); TextEditingController? name; TextEditingController? lastName; TextEditingController? mobileNo; TextEditingController? email; TextEditingController? company; @override void initState() { name = TextEditingController( text: widget.kontak == null ? '' : widget.kontak!.name); mobileNo = TextEditingController( text: widget.kontak == null ? '' : widget.kontak!.mobileNo); email = TextEditingController( text: widget.kontak == null ? '' : widget.kontak!.email); company = TextEditingController( text: widget.kontak == null ? '' : widget.kontak!.company); super.initState(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Contact Form'), ), body: ListView( padding: const EdgeInsets.all(16.0), children: [ Padding( padding: const EdgeInsets.only( top: 20, ), child: TextField( controller: name, decoration: InputDecoration( labelText: 'Name', border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), )), ), ), Padding( padding: const EdgeInsets.only( top: 20, ), child: TextField( controller: mobileNo, decoration: InputDecoration( labelText: 'Phone Number', border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), )), ), ), Padding( padding: const EdgeInsets.only( top: 20, ), child: TextField( controller: email, decoration: InputDecoration( labelText: 'Email', border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), )), ), ), Padding( padding: const EdgeInsets.only( top: 20, ), child: TextField( controller: company, decoration: InputDecoration( labelText: 'Company', border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), )), ), ), Padding( padding: const EdgeInsets.only(top: 20), child: SizedBox( height: 45, child: ElevatedButton( child: (widget.kontak == null) ? const Text( 'Add', style: TextStyle(color: Colors.white, fontSize: 25.0), ) : const Text( 'Update', style: TextStyle(color: Colors.white, fontSize: 25.0), ), onPressed: () { upsertKontak(); }, ), ), ) ], ), ); } Future<void> upsertKontak() async { if (widget.kontak != null) { //update await db.updateKontak(Kontak.fromMap({ 'id': widget.kontak!.id, 'name': name!.text, 'mobileNo': mobileNo!.text, 'email': email!.text, 'company': company!.text })); // ignore: use_build_context_synchronously Navigator.pop(context, 'update'); } else { //insert await db.saveKontak(Kontak( name: name!.text, mobileNo: mobileNo!.text, email: email!.text, company: company!.text, )); // ignore: use_build_context_synchronously Navigator.pop(context, 'save'); } } }
Simpan kode diatas ke dalam file contact_apps/lib/form_kontak.dart
.
Kode Utama Aplikasi
Terakhir kita perlu menulis kode utama apliksi yaitu kode pada file lib/main.dart
. Berikut adalah kode yang terdapat pada file main.dart.
import 'package:flutter/material.dart'; import 'list_kontak.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, title: 'SQLite Flutter', theme: ThemeData( primarySwatch: Colors.red, ), home: const ListKontakPage(), ); } }