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!
initState
, try to wrap the lineref.read(categoryControllerProvider.notifier).fetchCategory();
with aSchedulerBinding.instance.addPostFrameCallback
:SchedulerBinding.instance.addPostFrameCallback((_) => ref.read(categoryControllerProvider.notifier).fetchCategory());
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.