Riverpod V2 : New story to manage your state

For flutter, State Management is always a hot topic. Someone keep asking what is state management ? Which state management technique should I go for ? How should I manage my state ? I am not going to answer all that here but out of this hot topic, I will choose hottest package called Riverpod V2 and I am going to show you how simple it is to solve everything with Riverpod with writing just simple function or class.

We will be using riverpod generator here for our purpose but you can even go without generator, but you may need to write some boilerplate code for that.

Mainly with riverpod, we divide our logic in 2 types i.e. :-

Immutable

These logics are normally, very simple logic whose value doesn’t changes until they are rebuild. And they get rebuild if we forcefully refresh them or any internal provider value changes. Let’s see this with some example :-

// We need to use this annotation to generate provider out of this function
@riverpod
int currentUserBalance(CurrentUserBalanceRef ref) {
  // Whenever currentUserProvider changes it's value
  // This provider will rebuild itself
  return ref.watch(currentUserProvider).balance;
}

@riverpod
int currUserBalance(CurrentUserBalanceRef ref) {
  // Here whenever current user balance will change
  // This provider will rebuild itself and give new value
  // So, this doesn't care about other field in user changes or not
  return ref.watch(currentUserProvider.select((value) => value.balance));
}

@riverpod
Random random(RandomRef ref) {
  // This provider will never to changing its value
  // Until we manually call refresh or invalidate on this
  final dateTime = DateTime.now();
  return Random(dateTime.millisecondsSinceEpoch);
}

@riverpod
Future<int> someFutureValue(SomeFutureValueRef ref) {
  // This is an example of returning simple future
  return Future.value(5);
}

@riverpod
Stream<int> someStreamValue(SomeStreamValueRef ref) {
  // This is an example of returning simple stream
  return Stream.value(5);
}

Mutable

These logics are normally, some complex logic whose value we want to change without them being rebuilt i.e. some internal function that changes it's own output called state.

// We need to use this annotation to generate provider out of this function
@riverpod
class CurrentUserBalance extends _$CurrentUserBalance {
  @override 
  int build() {
    // Here whenever current user balance will change
    // This provider will rebuild itself and give new value
    return ref.watch(currentUserProvider.select((value) => value.balance));
  }

  // So this method will change current state
  void addBalance(int amount) {
    state = state + amount;
  }
}

@riverpod
Class SomeFutureValue extends _$SomeFutureValue {
  @override 
  Future<int> build() {
    // This is an example returning initial value 
    // as future for mutable provider
    return Future.value(5);
  }

  // So this method will change current state
  void update(int value) {
    state = AsyncData(value);
  }
}

@riverpod
Class SomeStreamValue extends _$SomeStreamValue {
  @override 
  Stream<int> build() {
    // This is an example returning initial value 
    // as future for mutable provider
    return Stream.value(5);
  }

  // So this method will change current state
  void update(int value) {
    state = AsyncData(value);
  }
}

And with this we can now decouple our state logic from UI and manage our state.

And that's it for Riverpod V2. Hope to see you in coming articles.