rrousselGit / state_notifier

ValueNotifier, but outside Flutter and with some extra perks
MIT License
311 stars 28 forks source link

Implement ChangeNotifier vs StateNotifer #31

Closed zgramming closed 4 years ago

zgramming commented 4 years ago

I familiar with Provider package and combine it with ChangeNotifier . I have 3 getter and method with 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 use ChangeNotifier , i only need create 1 file and just call method like globalChangeNotifier.METHOD() or value like globalChangeNotifier.value.

But now i learn about Riverpod package , and in the documentation used StateNotifier. I want migrate my above code from ChangeNotifier to StateNotifier , but in my understanding StateNotifier only can hold 1 type data, so if i want migrate above code i should create 3 file , let's say provider_isloading.dart,provider_isimageloading.dart,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 create 1 file to export all those file:

GlobalStateNotifier.dart

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

My question is , it's best practice make it like this ?

Structure Folder

tanya statenotifier
tbm98 commented 4 years ago

You should create a class extend stateNotifier it hold all of them

Vào 10:41 Th 6, 17 thg 7 2020 Zeffry Reynando notifications@github.com đã viết:

I familiar with [Provider] package (https://pub.dev/packages/provider) and combine it with ChangeNotifier . I have 3 getter and method with 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 use ChangeNotifier , i only need create 1 file and just call method like globalChangeNotifier.METHOD() or value like globalChangeNotifier.value.

But now i learn about Riverpod https://pub.dev/packages/riverpod package , and in the documentation used StateNotifier. I want migrate my above code from ChangeNotifier to StateNotifier , but in my understanding StateNotifier only can hold 1 type data, so if i want migrate above code i should create 3 file , let's say provider_isloading.dart,provider_isimageloading.dart,provider_obsecurepassword.dart . Using StateNotifier

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

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

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

// provider_isimageloading.dart

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

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

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

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

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

And i create 1 file to export all those file: GlobalStateNotifier.dart

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

My question is , it's best practice make it like this ? Structure Folder

[image: tanya statenotifier] https://user-images.githubusercontent.com/38829404/87745925-699f3300-c819-11ea-8cae-b63b4bfed0dd.PNG

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/rrousselGit/state_notifier/issues/31, or unsubscribe https://github.com/notifications/unsubscribe-auth/AMRATJFEVPPIHIXYWRDUR2LR37B53ANCNFSM4O5N5A5Q .

rrousselGit commented 4 years ago

Indeed

Just because StateNotifier has a single property doesn't mean you need multiple ones for multiple variables

You can do:

class Model {
  final int foo;
  final int bar;
}

class Notifier extends StateNotifier<Model> {
... 
} 
zgramming commented 4 years ago

@rrousselGit i follow your instruction and change my code like this :


class GlobalStateNotifierModel {
  final bool isLoading;
  final bool isImageLoading;
  final bool isObsecurePassword;

  GlobalStateNotifierModel({
    this.isImageLoading,
    this.isLoading,
    this.isObsecurePassword,
  });
}

class GlobalStateNotifierProvider extends StateNotifier<GlobalStateNotifierModel> {
  GlobalStateNotifierProvider([GlobalStateNotifierModel state])
      : super(GlobalStateNotifierModel(
          isImageLoading: false,
          isLoading: false,
          isObsecurePassword: false,
        ));

  void toggleLoading(bool value) {
    state = GlobalStateNotifierModel(
      isLoading: value,
      isImageLoading: state.isImageLoading,
      isObsecurePassword: state.isImageLoading,
    );
  }

  void toggleImageLoading(bool value) {
    state = GlobalStateNotifierModel(
      isImageLoading: value,
      isLoading: state.isLoading,
      isObsecurePassword: state.isObsecurePassword,
    );
  }

  void toggleObsecurePassword(bool value) {
    state = GlobalStateNotifierModel(
      isImageLoading: state.isImageLoading,
      isLoading: state.isLoading,
      isObsecurePassword: !value,
    );
  }
}

final globalStateNotifierProvider =
    StateNotifierProvider<GlobalStateNotifierProvider>((ref) => GlobalStateNotifierProvider());

With this approach i get same result with earlier code , but all consumer related to globalStateNotifierProvider will rebuild. is it behaviour like this or i missed something ?

tanya statenotifier 2

rrousselGit commented 4 years ago

Yes you can then filter rebuilds using context.select

zgramming commented 4 years ago

how i can access context.select ? in Riverpod documentation i can't see about it. If using provider this case can use Selector.

rrousselGit commented 4 years ago

With Riverpod, use Computed instead. Or if you are using hooks, use useProvider(my Provider.select(...))