rrousselGit / riverpod

A reactive caching and data-binding framework. Riverpod makes working with asynchronous code a breeze.
https://riverpod.dev
MIT License
5.82k stars 888 forks source link

Unexpected "Providers are not allowed to modify other providers during their initialization" error #3433

Open kefasjw opened 1 month ago

kefasjw commented 1 month ago

Describe the bug

Creating this issue as suggested in discord

I got this error: Providers are not allowed to modify other providers during their initialization while updating notifier's state inside a ref.listen's listener. Multiple providers are involved, the dependencies:

The possibility is that the ref.listen's listener is called at the same time as the combine provider is still re-building.

Log:

provider start
provider end
provider start
set new state <-- here is changing the notifier's state while the provider is still building
provider end
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: 'package:riverpod/src/framework/element.dart': Failed assertion: line 477 pos 11: '_debugCurrentlyBuildingElement == null ||
               _debugCurrentlyBuildingElement == this': Providers are not allowed to modify other providers during their initialization.

Full Log

Tried with versions:

To Reproduce

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() => runApp(const ProviderScope(child: MyPage()));

class MyPage extends ConsumerWidget {
  const MyPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    ref.watch(combine);
    return MaterialApp(
        home: Scaffold(
      body: Text(ref.watch(myNotifierProvider).toString()),
    ));
  }
}

final data = FutureProvider.autoDispose((ref) async {
  await Future.delayed(
      const Duration(seconds: 1)); // Simulating network request
  return 'data';
});

final data2 = Provider.autoDispose((ref) => ref.watch(data).valueOrNull);

final data3 = Provider.autoDispose((ref) => ref.watch(data2));

final myNotifierProvider =
    AutoDisposeNotifierProvider<MyNotifier, int>(() => MyNotifier());

class MyNotifier extends AutoDisposeNotifier<int> {
  @override
  build() {
    // change to return ref.watch to fix
    ref.listen(
      data3,
      (previous, next) {
        print('set new state');
        state = 1;
      },
    );
    return 0;
  }
}

final combine = Provider.autoDispose((ref) {
  print('provider start');
  ref.watch(data2);
  // ref.watch(data3); // uncomment this to fix
  ref.watch(
      myNotifierProvider.select((value) => value)); // or remove select to fix
  print('provider end');
  return 0;
});

Expected behavior

There should be no error, or perhaps the listeners and provider building shouldn't be called at the same time