Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implement ChangeNotifier vs StateNotifier

I'm quite familiar with Provider package and combine it with the ChangeNotifier.

Let's say I have 3 getters and method with a different function :

  1. Toggle Loading
  2. Toggle Image Loading
  3. Toggle ObsecurePassword

Using ChangeNotifer

import 'package:flutter/foundation.dart';

class GlobalChangeNotifier extends ChangeNotifier {
  bool _isLoading = false;
  bool _isImageLoading = false;
  bool _isObsecurePassword = false;

  bool get isLoading => _isLoading;
  bool get isImageLoading => _isImageLoading;
  bool get isObsecurePassword => _isObsecurePassword;

  void setLoading(bool value) {
    _isLoading = value;
    notifyListeners();
  }

  void setImageLoading(bool value) {
    _isImageLoading = value;
    notifyListeners();
  }

  void setObsecurePassword(bool value) {
    _isObsecurePassword = !value;
    notifyListeners();
  }
}

final globalChangeNotifier = GlobalChangeNotifier();

If I'm using ChangeNotifier, I only need to create 1 file and just call a method like globalChangeNotifier.METHOD() or value like globalChangeNotifier.value.

But now, I've learned about Riverpod package, and in the documentation, it's using StateNotifier.

I want to migrate my previous code from ChangeNotifier to StateNotifier. But in my understanding, StateNotifier only can hold 1 type data, so if I want to migrate above code I should create 3 files, let's say:

  1. provider_isloading.dart,
  2. provider_isimageloading.dart and
  3. provider_obsecurepassword.dart.

Using StateNotifier

// provider_isloading.dart
class IsImageLoading extends StateNotifier<bool> {
  IsImageLoading() : super(false);

  void toggleImageLoading(bool value) {
    state = value;
  }
}

final isImageLoadingProvider = StateNotifierProvider((ref) => IsImageLoading());

// provider_isimageloading.dart

class IsLoading extends StateNotifier<bool> {
  IsLoading() : super(false);
  void toggleLoading(bool value) => state = value;
}

final isLoadingProvider = StateNotifierProvider((ref) => IsLoading());

// provider_obsecurepassword.dart
class IsObsecurePassword extends StateNotifier<bool> {
  IsObsecurePassword() : super(false);

  void toggleObsecurePassword(bool value) {
    state = !value;
  }
}

final isObsecurePasswordProvider = StateNotifierProvider((ref) => IsObsecurePassword());

And I also need to create 1 file to export all of those files:

GlobalStateNotifer.dart

export './provider_loading.dart';
export './provider_imageloading.dart';
export './provider_obsecurepassword.dart';

My question is, is it the best practice to make it as I've explained earlier?

My Folder's Structure

My Folder's Structure

like image 508
Zeffry Reynando Avatar asked Jul 17 '20 03:07

Zeffry Reynando


People also ask

What is statenotifier in flutter and how to use it?

You may think of State Notifier as the Flutter independent state management component. State Notifier Provider is another Provider variant in Riverpod That is where Riverpod StateNotifierProvider enters into the scene. Of course, it takes help from StateNotifier to provide the state that to a bottom-most widget that needs the state.

What is a statenotifier provider?

Now StateNotifier can either operate with a single primitive data type, like integer; or, it can handle a very complex user-defined data type. Whatever, the nature of state, simple or complex, State Notifier Provider takes the child of State Notifier on its shoulder and carries the state to any widget that needs it.

How many types of data can statenotifier hold?

But in my understanding, StateNotifier only can hold 1 type data, so if I want to migrate above code I should create 3 files, let's say: provider_obsecurepassword.dart.

What are the limitations of changenotifier in flutter?

ChangeNotifier might be the simplest and least boilerplate state management in flutter but it does have its own limitations. The ability to use immutable states is the one I would say lacks the most, ChangeNotifier also has slower time complexities such as O (N²) for notifylisteners () and O (N) for addListener ().


Video Answer


1 Answers

When using Riverpod, it makes a great deal of sense to create static providers on the class they are providing. From your example, you could refactor to:

class IsImageLoading extends StateNotifier<bool> {
  IsImageLoading() : super(false);

  static final provider = StateNotifierProvider((ref) => IsImageLoading());

  void toggleImageLoading(bool value) {
    state = value;
  }
}

You should also consider whether or not you need your providers to be available outside of the class you are actually using them. Something tells me you probably won't be using your password provider anywhere but your login page. Consider creating a private provider in that class.

However, if your desire is to keep your current approach, you could create a class, A, that contains the 3 bool values and a class that extends StateNotifier<A>.

For example:

enum LoadingType { A, B, C }

class LoadingToggles {
  bool A, B, C;

  LoadingToggles({this.A = false, this.B = false, this.C = false});

  static final provider = StateNotifierProvider.autoDispose((ref) => LoadingState(LoadingToggles()));
}

class LoadingState extends StateNotifier<LoadingToggles> {
  LoadingState(LoadingToggles state) : super(state ?? LoadingToggles());

  void toggle(LoadingType type) {
    switch (type) {
      case LoadingType.A:
        state.A = !state.A;
        break;
      case LoadingType.B:
        state.B = !state.B;
        break;
      case LoadingType.C:
        state.C = !state.C;
        break;
      default:
        // Handle error state
    }
  }
}

Finally, just want to add that there is likely a better way to handle loading overall. Consider if you can use a FutureProvider/StreamProvider with Riverpod's AsyncValue instead of manually toggling a loading state.

like image 83
Alex Hartford Avatar answered Sep 25 '22 01:09

Alex Hartford