Search code examples
flutterdartriverpod

How to get the length of a Riverpod 2 state that is a map?


I'm having issues accessing my Map state to get the length of the list MartialArtsMove.hand.

Here is my class/provider:

import 'package:flutter_riverpod/flutter_riverpod.dart';

enum MartialArtsMove {
  hand,
  kick,
  grab;
}

class OneSteps extends Notifier<Map<MartialArtsMove, List<num>>> {
  @override
  Map<MartialArtsMove, List<num>> build() =>
      {for (final k in MartialArtsMove.values) k: <num>[]};

  void setMove(MartialArtsMove martialArtsMove, List<num> items) {
    state[martialArtsMove] = items;
    ref.notifyListeners();
  }

  void clearAll() {
    for (final martialArtsMove in MartialArtsMove.values) {
      state[martialArtsMove]!.clear();
    }
    ref.notifyListeners();
  }
}

final oneStepsProvider = StateProvider<OneSteps>((_) => OneSteps());

and here is how I use it on my page:

var oneStepState = ref.watch(oneStepsProvider);

Text(
                  'Hands ${oneStepState.state[MartialArtsMove.hand]!.length}',
                  textAlign: TextAlign.center,
                  // overflow: TextOverflow.ellipsis,
                  style: const TextStyle(fontSize: 14),
                )

I have also tried

Text(
                  'Hands ${oneStepState[MartialArtsMove.hand]!.length}',
                  textAlign: TextAlign.center,
                  // overflow: TextOverflow.ellipsis,
                  style: const TextStyle(fontSize: 14),
                )


Solution

  • You're creating a Notifier but then exposing it through a StateProvider. Those classes are not designed to work together, and in fact the code you shared contains a linter warning that you shouldn't access Notifier.state from outside the class. The simplest fix is to change your provider type to NotifierProvider and update your ref usage to retrieve the notifier and its state separately:

    typedef OneStepsState = Map<MartialArtsMove, List<num>>;
    
    class OneSteps extends Notifier<OneStepsState> {
        // Unmodified, cut for brevity
    }
    
    final oneStepsProvider =
        NotifierProvider<OneSteps, OneStepsState>(() => OneSteps());
    
    // Example of how to access the state and react to changes
    class HandsText extends ConsumerWidget {
      @override
      Widget build(BuildContext context, ref) {
        final oneStepState = ref.watch(oneStepsProvider);
        final len = oneStepState[MartialArtsMove.hand]!.length;
    
        return Text('Hands $len');
      }
    }
    
    
    // Example of how to call the notifier methods
    class ClearOneStepsButton extends ConsumerWidget {
      @override
      Widget build(context, ref) {
        return IconButton(
          onPressed: () {
            ref.read(oneStepsProvider.notifier).clearAll();
          },
          icon: const Icon(Icons.refresh),
        );
      }
    }
    

    Here's a working Dartpad demonstrating the changes + some other misc refactoring like making the state immutable, which is a bit more idiomatic in Riverpod.