1

I had previously developed a Flutter app using Riverpod(StateNotifierProvider, and everything was working smoothly. Recently, I've been trying to migrate from regular Riverpod to Riverpod Generator(NotifierProvider) and Flutter Hooks. However, I've run into an issue and could use some guidance.

When running my app, I'm encountering the following error message:

FlutterError (Tried to modify a provider while the widget tree was building.
If you are encountering this error, chances are you tried to modify a provider
in a widget life-cycle, such as but not limited to:
- build
- initState
- dispose
- didUpdateWidget
- didChangeDependencies

Modifying a provider inside those life-cycles is not allowed, as it could
lead to an inconsistent UI state. For example, two widgets could listen to the
same provider, but incorrectly receive different states.

To fix this problem, you have one of two solutions:
- (preferred) Move the logic for modifying your provider outside of a widget
  life-cycle. For example, maybe you could update your provider inside a button's
  onPressed instead.

- Delay your modification, such as by encapsulating the modification
  in a `Future(() {...})`.
  This will perform your update after the widget tree is done building.)

Here's the code snippet of the widget where I'm encountering the issue:

class CategoryScreen extends HookConsumerWidget {
  const CategoryScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    useEffect(() {
      ref.read(categoryControllerProvider.notifier).fetchCategory();
      return null;
    }, []);

    final categoryList = ref.watch(categoryControllerProvider);

    return Scaffold(
      appBar: AppBar(
        title: const Text('Select Category'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            CategoryGrid(categoryList: categoryList),
            GestureDetector(
              onTap: () {
                Navigator.pop(context);
              },
              child: const BoxContainer(
                height: 40,
                width: 80,
                colorGradient: Palette.gWhite,
                child: Center(child: Text('Back')),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

And here's the corresponding controller code:

@Riverpod(keepAlive: true)
class CategoryController extends _$CategoryController {
  @override
  List<CategoryModel> build() {
    return [];
  }

  Future<List<CategoryModel>> fetchCategory() async {
    try {
      final result =
          await ref.read(categoryRepositoryProvider).getCategoryList();

      state = result;

      return result;
    } catch (e) {
      rethrow;
    }
  }
}

Previously, I was calling the fetchCategory method inside initState, but now this approach is also not working. Here's how I was previously calling it:

class CategoryScreen extends ConsumerStatefulWidget {
  const CategoryScreen({Key? key}) : super(key: key);

  @override
  ConsumerState<CategoryScreen> createState() => _CategoryScreenState();
}

class _CategoryScreenState extends ConsumerState<CategoryScreen> {
  @override
  void initState() {
    ref.read(categoryControllerProvider.notifier).fetchCategory();
    super.initState();
  }

  // Rest of the code remains the same
}

I've tried to fetch data using the fetchCategory method both within initState and the useEffect hook, but I'm still encountering the error. How can I resolve this error and continue my migration to Riverpod Generator and Flutter Hooks? Any insights or guidance would be greatly appreciated. Thank you!

4
  • 1
    In your initState, try to wrap the line ref.read(categoryControllerProvider.notifier).fetchCategory(); with a SchedulerBinding.instance.addPostFrameCallback: SchedulerBinding.instance.addPostFrameCallback((_) => ref.read(categoryControllerProvider.notifier).fetchCategory()); Commented Aug 26, 2023 at 14:37
  • It's working, thank you so much. Could you let me know if my approach is correct?
    – Nashaf
    Commented Aug 26, 2023 at 14:50
  • 1
    The literature is rushing to catch up with Remi's prolific development. In brief, avoid legacy ChangeNotifier, StateNotifier (and their providers) and StateProvider. Use only Provider, FutureProvider, StreamProvider, and Notifier, AsyncNotifier, StreamNotifier (and their providers). Commented Aug 27, 2023 at 0:10
  • 1
    ref.read(categoryControllerProvider.notifier).fetchCategory(). You should not use notifier methods to create the initial build. Put that work into the build() and it should work better. Commented Aug 27, 2023 at 0:12

1 Answer 1

1

Would it be possible to simply use a FutureProvider instead? Your current approach needs to call fetchCategory separately.

@riverpod
FutureOr<List<CategoryModel>> catagories(CatagoriesRef ref) {
  return ref.watch(categoryRepositoryProvider).getCategoryList();
}

// usage
Widget build(BuildContext context, WidgetRef ref) {
  final catagoriesAsync = ref.watch(catagoriesProvider);
  ...
  CategoryGrid(categoryList: catagoriesAsync.when(...)),
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.