Created with to build something
Version: 5.0.5
cover-menggunakan-changenotifier-dan-provider-sebagai-state-management

Codding

Menggunakan ChangeNotifier dan Provider sebagai State Management

Menggunakan Class ChangeNotifier dan Package Provider sebagai state management pada project aplikasi yang dibuat menggunakan flutter.

Nov 14, 2022

Dalam dunia pengembangan aplikasi flutter topik mengenai state management sangat selalu menjadi perbincangan yang sangatlah panas, baik itu pada forum diskusi lokal yang berbahasa indonesia ataupun forum diskusi internasional. Tak sedikit juga orang yang malah membuat kubunya sendiri - sendiri untuk membangga - banggakan state management yang disukainya.

Jika kalian mencoba untuk mencermati beberapa state management populer yang ada diflutter baik itu mulai dari flutter_bloc, GetX dan lain sebagainya, mereka nyaris memiliki cara yang sama dalam pengoperasian dengan gaya penulisan yang berbeda. Sampai akhirnya saya agak aneh melihat dokumentasi flutter dalam membuat simple state management kalian bisa melihatnya pada halaman dokumentasinya.

Jika kalian melihat disana ada contoh penggunaan ChangeNotifier berserta widget wrappernya untuk mengconsume data yang diolah pada sebuah changenotifier, terdengar familiar jika kalian menggunakan sebuah packages bernama provider. Iyak jika kalian pernah menggunakan package ini maka kalian tidak akan pernah asing dengan sebuah class yang harus di implement bernama ChangeNotifier.

Apa itu Provider ?

Provider merupakan sebuah widget yang biasa digunakan oleh beberapa orang untuk mengakses state management yang berasal dari ChangeNotifier, banyak juga orang yang mengganggap bahwa provider merupakan sebuah state management tapi sayangnya provider hanyalah sebuah widget. Jika kalian pernah mencoba flutter_bloc dalam membuat sebuah state management, maka provider isinya hanyalah BlocBuilder, BlocListener dan kawan - kawannya yang tujuannya hanya menkonsumsi perubahan state pada ChangeNotifier.

Menggunakan ChangeNotifierProvider

Sebelum mengkonsumsi state management pada ChangeNotifier kalian perlu membungkus widget yang ingin mengkonsumsi ChangeNotifier dengan ChangeNotifierProvider, ini ditujukkan untuk menandai bagian mana yang akan menggunakan ChangeNotifier.

ChangeNotifierProvider<UnauthenticatedNotifier>(
      create: (_) => _notifier,
      child: SomeWidget()
)

Dalam penggunaan ChangeNotifierProvider kalian dapat langsung membuat object pada ChangeNotifierProvider atau langsung di inisialisasi pada halaman namun harap di ingat hal tersebut tidak dapat dicombine dengan membuat object notifier secara bersamaan baik pada ChangeNotifierProvider dan sebuah halaman karena itu dapat membuat multiple instance object atau objek ChangeNotifier ganda.

// Jangan lakukan ini
final UnauthenticatedNotifier _notifier = new UnauthenticatedNotifier()

// yang disarankan
ChangeNotifierProvider<UnauthenticatedNotifier>(
      create: (_) => UnauthenticatedNotifier(),
      child: SomeWidget()
)

Penggunaan MultiProvider

Jika dalam suatu kasus kalian perlu menggunakan ChangeNotifier lebih dari satu, saya sarankan untuk menggunakan MultiProvider hal ini akan mempermudah kalian dalam membungkus widget yang mengkonsumsi state management dengan jumlah lebih dari satu.

MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => _notifier)
      ],
      child: SomeWidget()
)

Langkah - langkah menggunakan Provider

Untuk dapat menggunakan provider dalam mengkonsumsi data yang terdapat pada ChangeNotifier, ada beberapa langkah yang harus dilakukan agar setiap widget dapat dengan baik mengkonsumsi state pada ChangeNotifier.

Membungkus Widget dengan Listener Provider Widget

Langkah pertama yang harus dilakukan adalah membungkus widget baik itu sebuah Scaffold widget ataupun widget lainnya yang akan mengkonsumsi ChangeNotifier, untuk melakukan hal tersebut pertama pastikan ada berapa jumlah ChangeNotifier yang akan digunakan pada widget tersebut. Jika membutuhkan lebih dari satu ChangeNotifier maka kalian perlu menggunakan widget MultiProvider() namun jika hanya satu maka kalian perlu menggunakan widget ChangeNotifierProvider(), kedua jenis widget tersebut memiliki fungsi yang sama dan hanya memiliki perbedaan pada jumlah ChangeNotifier yang teregistrasi jadi jika kalian hanya memiliki satu ChangeNotifier namun menggunakan MultiProvider() maka itu bukan sebuah masalah.

return MultiProvider(
      providers: [ChangeNotifierProvider(create: (_) => _notifier)],
      child: SomeWidget()
)

Membungkus Widget dengan Consumer Widget

Langkah kedua yang dilakukan untuk dapat mengkonsumsi perubahan data pada ChangeNotifier pada widget yaitu dengan membungkus widget terkait dengan widget Consumer(), widget Consumer() digunakan untuk menerapkan segala perubahan state yang ada pada ChangeNotifier untuk di render ulang sehingga perubahan data akan dapat terlihat pada screen.

Hal yang mungkin perlu diperhatikan adalah kalian mungkin bisa saja membungkus satu file kode penuh dengan consumer widget, akan tetapi ketika terdapat perubahan pada ChangeNotifier maka seluruh tampilan akan dirender dan memungkinkan terjadi kejadian re-render yang tidak diinginkan sehingga mungkin saja bisa berdampak sedikit atau banyak dengan performa aplikasi.

Consumer<UnauthenticatedNotifier>(
  builder: (context, notifier, _) => Button(
      style: primaryButtonStyle(),
      child: const Text('Login'),
      onPressed:
          notifier.isLoading ? null : () => submit()),
),

Apa itu ChangeNotifier ?

ChangeNotifier merupakan sebuah kelas yang melakukan subscribe atau bahasa indonesianya berlangganan terhadap perubahan-perubahan yang terjadi pada satu state dalam aplikasi, dengan begini dasarnya jika ingin menggunakan ChangeNotifier untuk state management maka kalian tidak perlu menggunakan provider jika belum sampai pada tahap yang advance.

Jika kalian datang dari ekosistem GetX mungkin tidak terlalu susah untuk beradaptasi karena sangatlah terlihat kalau dalam melakukan perubahan state perlakuan yang dilakukan juga berbeda yang satu memanggil method update() dan yang satu lagi memanggil method notifyListener().

Cara Penggunaan

Untuk menggunakan ChangeNotifier sebagai state management kalian perlu membuat sebuah class yang sudah diextend dengan ChangeNotifier, dengan begini kalian dapat menentukan variable yang akan tersimpan pada changenotifier yang telah dibuat. Untuk contoh dibawah merupakan sebuah ChangeNotifier yang digunakan untuk membuat sesi login pada sebuah aplikasi.

import 'package:flutter_desktop_experiment/services/locator.dart';
import 'package:flutter_desktop_experiment/services/navigator.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_desktop_experiment/routes/constant.dart' as routes;

class UnauthenticatedNotifier with ChangeNotifier {
  late NavigatorService _navigatorService;
  late bool isLoading;
  String? username, password;

  UnauthenticatedNotifier() {
    _navigatorService = locator<NavigatorService>();
    isLoading = false;
  }

  bool checkPassword(String password) {
    if (password == 'password') {
      return true;
    } else {
      return false;
    }
  }

  bool checkUsername(String username) {
    if (username == 'admin') {
      return true;
    } else {
      return false;
    }
  }

  Future<void> doLogin() async {
    isLoading = true;
    notifyListeners();
    await Future.delayed(const Duration(seconds: 3));
    if (checkUsername(username!) && checkPassword(password!)) {
      _navigatorService.push('main', routes.authenticated);
    }
    isLoading = false;
    notifyListeners();    
  }
}

Lalu kita coba untuk aplikasikan pada sebuah login form, pada login form kita butuh untuk membuat instance atau membuat objek dari UnauthenticatedNotifier untuk bisa menjalankan perintah atau mengakses variable yang ada di notifier tersebut.

// ignore_for_file: avoid_print

import 'package:flutter_desktop_experiment/notifier/authentication/unauthenticated_notifier.dart';
import 'package:flutter_desktop_experiment/styles/button_style.dart';
import 'package:desktop_window/desktop_window.dart';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:provider/provider.dart';

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

  @override
  State<UnauthenticatedScreen> createState() => _UnauthenticatedScreenState();
}

class _UnauthenticatedScreenState extends State<UnauthenticatedScreen> {
  late GlobalKey<FormState> _formKey;
  final UnauthenticatedNotifier _notifier = UnauthenticatedNotifier();

  @override
  void initState() {
    _formKey = GlobalKey<FormState>();
    DesktopWindow.resetMaxWindowSize().then((value) {
      DesktopWindow.setWindowSize(const Size(1024, 700));
      DesktopWindow.setMinWindowSize(const Size(1024, 700));
    });

    super.initState();
  }

  void submit() {
    if (_formKey.currentState!.validate()) {
      print('log proses login');
      _notifier.doLogin();
    } else {
      print('something happen !');
    }
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => _notifier,
      child: ScaffoldPage(
        content: Container(
          width: MediaQuery.of(context).size.width,
          height: MediaQuery.of(context).size.height,
          padding: const EdgeInsets.all(24),
          child: Column(
              crossAxisAlignment: CrossAxisAlignment.center,
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                const FlutterLogo(
                  size: 156,
                ),
                const SizedBox(
                  height: 36,
                ),
                Form(
                  key: _formKey,
                  child: SizedBox(
                    width: MediaQuery.of(context).size.width * 0.5,
                    child: ContentDialog(
                      title: const Text('Login'),
                      content: Wrap(
                        runSpacing: 12,
                        children: [
                          TextFormBox(
                            header: 'Username',
                            placeholder: 'Masukkan username anda',
                            onChanged: (value) => _notifier.username = value,
                            validator: (value) {
                              if (value == null) {
                                return 'Username tidak boleh kosong';
                              }
                              return null;
                            },
                          ),
                          TextFormBox(
                            header: 'Password',
                            placeholder: 'Masukkan password anda',
                            obscureText: true,
                            onChanged: (value) => _notifier.password = value,
                            validator: (value) {
                              if (value == null) {
                                return 'Password tidak boleh kosong';
                              }
                              return null;
                            },
                          )
                        ],
                      ),
                      actions: [
                        Consumer<UnauthenticatedNotifier>(
                          builder: (_, c, __) => Button(
                              style: primaryButtonStyle(),
                              child: const Text('Login'),
                              onPressed: c.isLoading ? null : () => submit()),
                        ),
                        Button(
                            child: const Text('Reset'),
                            onPressed: () {
                              print('siap');
                            }),
                        const Button(
                            child: Text('Lupa Password'), onPressed: null),
                      ],
                    ),
                  ),
                ),
              ]),
        ),
      ),
    );
  }
}


contoh changenotifier bagian 1

Ketika kalian coba kode diatas, pada dasarnya kalian dapat menjalankannya dengan sangat baik akan tetapi dengan kekurangan kalian tidak dapat mengkonsumsi perubahan state yang ada pada ChangeNotifier secara realtime, untuk melakukan hal tersebut kalian perlu menggunakan satu tambahan widget dari sebuah library yang bernama provider.

Kesimpulan

Diakhir dari artikel ini saya mungkin menganggap bahwa setiap state management yang terdapat pada flutter memiliki cara kerjanya yang hampir mirip dengan gaya penulisan yang sudah pasti berbeda, namun pada package bernama provider kita dapat memanfaatkan apa yang dimiliki bahasa dart dan di integrasikan dengan sebuah widget yang mereka tawarkan. Sejauh ini saya cukup menyukai penggunaan ChangeNotifier yang dipadukan dengan package Provider ini untuk mengelola state management pada aplikasi, mungkin karena saya awalnya terbiasa menggunakan getx mungkin akan ada beberapa fitur yang terasa hilang dan tidak dapat dilakukan hanya dengan ChangeNotifier.

Ingin Berkomentar ?

Gunakan fitur komentar dengan bijak demi keamanan dan kenyamanan anda saat berselancar di dunia maya ini, mungkin undang - undang atau peraturan dari sebagian wilayah akan menjerat aktivitas yang ada pada kolom komentar.