Riverpod: List provider is not rebuilding

868

Solution 1

Firstly, you are not creating the correct provider to listen to a StateNotifier. You need to change this:

static final provider = StateProvider(
  (ref) => CounterState(),
);

to this:

static final provider = StateNotifierProvider<CounterState, List<int>>(
  (ref) => CounterState(),
);

Please refer to the Riverpod documentation about the different types of providers.

Secondly, you are not actually watching for state changes, but you are just getting the state object from the notifier.

Change this line:

var count = watch(CounterState.provider.notifier).state.length;

to this:

final count = watch(CounterState.provider).length;

also, your increment method is not correct for StateNotifier providers. Please change this:

context.read(CounterState.provider.notifier).state.add(_count);

to this:

context.read(CounterState.provider.notifier).add(_count);

It should rebuild now when the state changes. However, you do need an implementation of your add method that actually changes the state object itself. I would suggest the second variant you mentioned, that is in my opinion the nicest way to do this:

add(int p) {
  state = [...state, p];
}

Solution 2

@TmKVU explained well, so I'm skipping that part. You can also follow riverpod document.

here is my example of riverPod:

Your widget

import 'dart:math';

import 'package:stack_overflow/exports.dart';

class CounterState extends StateNotifier<List<int>> {
  static final provider = StateNotifierProvider(
    (ref) => CounterState(),
  );

  int get last {
    print('last');
    return state.last;
  }

  int get length {
    print('len');
    return state.length;
  }

  // the body of this will be provided below
  add(int p) {}

  CounterState() : super(<int>[0]);
}

class MyHomePageSSSS extends ConsumerWidget {
  @override
  Widget build(BuildContext context, watch) {
    void _incrementCounter() {
      final _count = Random.secure().nextInt(100);

      context.read(CounterState.provider.notifier).state =
          context.read(CounterState.provider.notifier).state..add(_count);
    }

    final countprovider = watch(CounterState.provider);

    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Text(
          'You have pushed the button this many times: ${countprovider.length}',
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        child: Icon(Icons.add),
      ),
    );
  }
}


Share:
868
Adnan
Author by

Adnan

Junior computer engineering student, Full-time Software engineer. mainly focused on flutter.

Updated on December 31, 2022

Comments

  • Adnan
    Adnan over 1 year

    Flutter riverpod is not notifying the Consumer on the state change when the StateNotifier's type is List, while the same implementation works just fine for other types.

    here, I provided a minimal reproducable example:

    import 'dart:math';
    
    import 'package:flutter/material.dart';
    import 'package:flutter_riverpod/flutter_riverpod.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return ProviderScope(
          child: MaterialApp(
            home: MyHomePage(),
          ),
        );
      }
    }
    
    class CounterState extends StateNotifier<List<int>> {
      static final provider = StateProvider(
        (ref) => CounterState(),
      );
    
      int get last {
        print('last');
        return state.last;
      }
    
      int get length {
        print('len');
        return state.length;
      }
    
      // the body of this will be provided below
      add(int p) {}
    
      CounterState() : super(<int>[0]);
    }
    
    class MyHomePage extends ConsumerWidget {
      @override
      Widget build(BuildContext context, watch) {
        void _incrementCounter() {
          final _count = Random.secure().nextInt(100);
    
          context.read(CounterState.provider.notifier).state.add(_count);
        }
    
        var count = watch(CounterState.provider.notifier).state.length;
    
        return Scaffold(
          appBar: AppBar(),
          body: Center(
            child: Text(
              'You have pushed the button this many times: $count',
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: _incrementCounter,
            child: Icon(Icons.add),
          ),
        );
      }
    }
    

    as for the add method, I tried implementing it in a lot of ways, but neither works.

    here is what I tried:

    1: just add it straight away:

    add(int p) {
      state.add(p);
    }
    

    2: I also tried the solution suggested in this answer:

    add(int p) {
      state = [...state, p];
    }
    

    3: I tried to destroy the list entirely, and reassign it:

    add(int p) {
      final _state = [];
    
      // copy [state] to [_state]
      for (var item in state) {
        _state.add(item);
      }
    
      // empty the state
      state = [];
    
      // add the new element
      _state.add(p);
    
      // refill [state] from [_state]
      for (var item in _state) {
        state.add(item);
      }
    
    
      print(state.length); // it continues until here and prints
    }