Skip to content

Firestore

Firestore adalah database NoSQL pada platform Firebase.

CRUD

Kita akan membuat aplikasi sederhana yang dapat melakukan operasi Create Read Update dan Delete ke Firestore.

Firestore App

Setup Firestore

Create Database

Pertama kita buat sebuah project di Firebase.

Create Project

Kemudian pada menu sebelah kiri, klik menu Build - Firestore Database, dan klik tombol Create database.

Create Project

Pada halaman Create database langkah 1, klik tombol Next. Pada langkah 2 pilih Start in production mode dan klik tombol Enable.

Update Rules

Pada halaman Cloud Firestore, klik menu Rules. Kemudian ubah false menjadi true pada baris ke 6 dan tekan tombol Publish.

Update Rules

Create Collection

Pada halaman Cloud Firestore, klik menu Data kemudian klik menu Start collection.

Start Collection

Isikan products pada Collection ID dan klik tombol Next. Pada halaman berikutnya, klik tombol Auto-ID pada Document ID dan buat field berikut ini:

Fields

Klik tombol Save.

Flutter Project

Buat project baru Flutter kemudian jalankan perintah berikut di terminal.

flutterfire configure

flutter pub add firebase_core

flutter pub add cloud_firestore

flutter pub add intl

Buka file android/app/build.gradle kemudian ubah minSdkVersion menjadi 21

minSdkVersion 21

Buka file android/app/src/main/AndroidManifest.xml dan tambahkan kode berikut diantara tag <manifest> dan <application>

<uses-permission android:name="android.permission.INTERNET" />

Main

Sesuaikan kode pada main.dart

main.dart
import 'package:crud_products/firebase_options.dart';
import 'package:crud_products/pages/home_page.dart';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      // Remove the debug banner
      debugShowCheckedModeBanner: false,
      title: 'Firestore CRUD',
      home: HomePage(),
    );
  }
}

Home Page

Buat folder pages kemudian buat file home_page.dart.

home_page.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

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

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  // text fields' controllers
  final TextEditingController _nameController = TextEditingController();
  final TextEditingController _priceController = TextEditingController();

  final CollectionReference _productss =
      FirebaseFirestore.instance.collection('products');

  var formatter = NumberFormat.decimalPatternDigits(
    locale: 'id_ID',
    decimalDigits: 0,
  );

  // Fungsi ini dijalankan saat floatting action button di tekan
  // Menambahkan product jika tidak ada documentSnapshot yang dimasukkan sebagai parameter
  // Jika documentSnapshot != null maka product yang ditampilkan akan di update
  Future<void> _createOrUpdate([DocumentSnapshot? documentSnapshot]) async {
    String action = 'create';
    if (documentSnapshot != null) {
      action = 'update';
      _nameController.text = documentSnapshot['name'];
      _priceController.text = documentSnapshot['price'].toString();
    }

    await showModalBottomSheet(
        isScrollControlled: true,
        context: context,
        builder: (BuildContext ctx) {
          return Padding(
            padding: EdgeInsets.only(
                top: 20,
                left: 20,
                right: 20,
                // prevent the soft keyboard from covering text fields
                bottom: MediaQuery.of(ctx).viewInsets.bottom + 20),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                TextField(
                  controller: _nameController,
                  decoration: const InputDecoration(labelText: 'Name'),
                ),
                TextField(
                  keyboardType:
                      const TextInputType.numberWithOptions(decimal: true),
                  controller: _priceController,
                  decoration: const InputDecoration(
                    labelText: 'Price',
                  ),
                ),
                const SizedBox(
                  height: 20,
                ),
                ElevatedButton(
                  child: Text(action == 'create' ? 'Create' : 'Update'),
                  onPressed: () async {
                    final String? name = _nameController.text;
                    final int? price = int.tryParse(_priceController.text);
                    if (name != null && price != null) {
                      if (action == 'create') {
                        // Persist a new product to Firestore
                        await _productss.add({"name": name, "price": price});
                      }

                      if (action == 'update') {
                        // Update the product
                        await _productss
                            .doc(documentSnapshot!.id)
                            .update({"name": name, "price": price});
                      }

                      // Clear the text fields
                      _nameController.text = '';
                      _priceController.text = '';

                      // Hide the bottom sheet
                      if (!context.mounted) return;
                      Navigator.of(context).pop();
                    }
                  },
                )
              ],
            ),
          );
        });
  }

  // Fungsi untuk menghapus product berdasarkan productId
  Future<void> _deleteProduct(String productId) async {
    await _productss.doc(productId).delete();

    // Show a snackbar
    ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
        content: Text('You have successfully deleted a product')));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Firestore CRUD'),
      ),
      // StreamBuilder digunakan untuk menampilkan semua product dari Firestore
      body: StreamBuilder(
        stream: _productss.snapshots(),
        builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
          if (streamSnapshot.hasData) {
            return ListView.builder(
              itemCount: streamSnapshot.data!.docs.length,
              itemBuilder: (context, index) {
                final DocumentSnapshot documentSnapshot =
                    streamSnapshot.data!.docs[index];
                return Card(
                  margin: const EdgeInsets.all(10),
                  child: ListTile(
                    title: Text(documentSnapshot['name']),
                    subtitle: Text(formatter.format(documentSnapshot['price'])),
                    trailing: SizedBox(
                      width: 100,
                      child: Row(
                        children: [
                          // Tombol untuk edit product
                          IconButton(
                              icon: const Icon(Icons.edit),
                              onPressed: () =>
                                  _createOrUpdate(documentSnapshot)),
                          // Tombol untuk menghapus product
                          IconButton(
                              icon: const Icon(Icons.delete),
                              onPressed: () =>
                                  _deleteProduct(documentSnapshot.id)),
                        ],
                      ),
                    ),
                  ),
                );
              },
            );
          }

          return const Center(
            child: CircularProgressIndicator(),
          );
        },
      ),
      // Menambahkan product
      floatingActionButton: FloatingActionButton(
        onPressed: () => _createOrUpdate(),
        child: const Icon(Icons.add),
      ),
    );
  }
}

Jalankan project.

Chalenge

Tambahkan konfirmasi saat data dihapus menggunakan package rflutter_alert

Alert

Final Project

Informasi mengenai final project dapat dilihat disini https://forms.gle/wPo8K4UoPaX1PxDN9