Search code examples
flutterriverpodriverpod-annotation

Why riverpod state that store List of String always reset its value?


I have built this riverpod state:

import "package:riverpod_annotation/riverpod_annotation.dart";

part "form_state.g.dart";

@riverpod
class FormState extends _$FormState {

  @override
  List<String?> build() {
    ref.listenSelf((previous, next) {
      log("previous: $previous, next: $next", name: "FORMSTATE 0");
    });

    return List.filled(10, null);
  }

  void change(int index, String value) {
    log("change: $index, value: $value", name: "FORMSTATE 1");
    state = [
      ...state.sublist(0, index),
      value,
      ...state.sublist(index + 1),
    ];
  }
}

On my Widget, I have a list of TextField as Below:

class DummyPage extends ConsumerStatefulWidget {
  const DummyPage({super.key});

  @override
  ConsumerState<DummyPage> createState() =>
      _DummyPageState();
}

class _DummyPageState extends ConsumerState<DummyPage> {
  final List<TextEditingController> _controllers =
      List.generate(10.length, (index) => TextEditingController());

  @override
  void initState() {
    final form = ref.read(formStateProvider);

    for (var i = 0; i < _controllers.length; i++) {
      _controllers[i].text = form[i] ?? "";
    }

    super.initState();
  }

  @override
  void dispose() {
    for (var i = 0; i < _controllers.length; i++) {
      _controllers[i].dispose();
    }

    super.dispose();
  }

  @override
  Widget build(context) {
    return Column(children: [
      ...List.generate(
        _controllers.length,
        (index) => TextField(
          controller: _controllers[index],
          onChanged: (val) {
            ref.read(formStateProvider.notifier).change(index, val);
          }
        )
    ])
  }

The current condition is every time onChanged is called, it always resets the value as below:

[FORMSTATE 0] previous: null, next: [null, null, null, null, null, null]
[FORMSTATE 1] change: 0, value: 2
[FORMSTATE 0] previous: [null, null, null, null, null, null], next: [2, null, null, null, null, null]

[FORMSTATE 0] previous: null, next: [null, null, null, null, null, null]
[FORMSTATE 1] change: 1, value: 3
[FORMSTATE 0] previous: [null, null, null, null, null, null], next: [null, 3, null, null, null, null]

[FORMSTATE 0] previous: null, next: [null, null, null, null, null, null]
[FORMSTATE 1] change: 2, value: 9
[FORMSTATE 0] previous: [null, null, null, null, null, null], next: [null, null, 9, null, null, null]

My question is what causes this? How to change it to fulfill the expected case when a particular TextField in list of TextField at index i changes, the riverpod state at index i will also change?


Solution

  • There is no listener for your notifier and therefore it is auto disposed.

    You can verify it by adding a log statement with ref.dispose inside the notifier or setup a ProviderObserver.

    https://docs-v2.riverpod.dev/docs/concepts/provider_observer

    You could change the behaviour by adding ref.watch(formStateProvider); or ref.listen(formStateProvider); inside the widget's build method or set the notifier's keepAlive to true.