My app keeps the user's favorite food in a SQLite database. A top-level widget, MyApp
, loads this data using a FutureBuilder
and then hoists it deeper using a Provider
.
There's a FoodPage
page that can manage foods. When you "save" on that page, it writes to the SQLite. However, MyApp
doesn't reload its future.
FoodPage
is always reached using several Navigator.push(context, ...)
calls. That means I'm several hops away from the top-level MyApp
, so I'm unsure how to request a "re-fetch" from here.
If I were doing all my work directly in MyApp
, I'd be able to call setState(() {})
to refresh its FutureBuilder. However, I can't because I'm in a different widget.
Also, I'm several nested Navigator.push
's deep, so doing Navigator.pop().then(...)
wouldn't really help me here.
Is my only option to do the clunky thing and reload the SQLite foods on every page?
If you want to refresh the data in the top-level widget, MyApp
, after a user has added or updated a food in FoodPage
, you can use a callback function to notify MyApp
that the data has changed.
Here is an example of how you can use a callback function to achieve this:
In FoodPage
, define a callback function that takes a bool parameter indicating whether the data has changed:
class FoodPage extends StatelessWidget {
final Function(bool) onDataChanged;
FoodPage({required this.onDataChanged});
// ...
void _saveFood() {
// Save the food to SQLite
// Notify the parent widget that t
he data has changed
onDataChanged(true);
}
// ...
}
In MyApp
, pass the callback function to FoodPage and use it to trigger a refresh of the data:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _fetchData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ChangeNotifierProvider(
create: (_) => FoodsProvider(snapshot.data),
child: CupertinoApp(
home: Center(
child: GestureDetector(
onTap: () async {
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FoodPage(
onDataChanged: (bool dataChanged) {
if (dataChanged) {
// Reload the data from SQLite
// This will trigger a rebuild of MyApp
}
},
),
),
);
},
child: const Text('Open FoodPage'),
),
),
),
);
} else {
return const CircularProgressIndicator();
}
},
);
}
}
By passing the onDataChanged
callback function to FoodPage
, you can use it to trigger a refresh of the data in MyApp
when the user adds or updates a food. When the callback function is called, you can reload the data from SQLite
, which will trigger a rebuild of MyApp
and refresh the data in the UI.