Hello I'm having a lot of trouble understanding how to use providers when fetching data.
Lets say I have an http service that fetches the data and updates a list:
my_provider.dart:
class InventoryProvider extends ChangeNotifier {
List<Item> _items = [];
List<Item> get items => List.unmodifiable(_items);
Future<List<Item>> fetchAllItems() async {
_items = await ItemService().fetchAllItems();
notifyListeners();
return items;
}
}
Now I want to build my list. The only solution I have found to work is the following:
my_screen:
class InventoryScreen extends StatefulWidget {
static const routeName = '/';
const InventoryScreen({
super.key,
});
@override
State<InventoryScreen> createState() => _InventoryScreenState();
}
class _InventoryScreenState extends State<InventoryScreen> {
final List<Item> _items = [];
bool _isLoading = false;
String _errorMessage = '';
/// Fetch items
@override
void initState() {
super.initState();
_isLoading = true;
final itemProvider = Provider.of<InventoryProvider>(context, listen: false);
itemProvider.fetchAllItems().then((posts) {
setState(() {
for (Item item in posts) {
_items.add(item);
}
_isLoading = false;
});
}).catchError((e) {
setState(() {
_errorMessage = e.message;
_isLoading = false;
});
});
}
...
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
...
body: _isLoading
? const Center(child: CircularProgressIndicator())
: _errorMessage.isNotEmpty
? Center(child: Text(_errorMessage))
: _items.isEmpty
? const Center(child: Text('You do not have any item yet.'))
: ListView.builder(
itemCount: _items.length,
itemBuilder: (BuildContext context, int index) {
final item = _items[index];
return ListTile(
So this works for the first fetch as:
It does not work for:
What I have tried:
listen: False
. Triggers:Exception has occurred.
FlutterError (dependOnInheritedWidgetOfExactType<_InheritedProviderScope<InventoryProvider?>>() or dependOnInheritedElement() was called before _InventoryScreenState.initState() completed.
When an inherited widget changes, for example if the value of Theme.of() changes, its dependent widgets are rebuilt. If the dependent widget's reference to the inherited widget is in a constructor or an initState() method, then the rebuilt dependent widget will not reflect the changes in the inherited widget.
Typically references to inherited widgets should occur in widget build() methods. Alternatively, initialization based on inherited widgets can be placed in the didChangeDependencies method, which is called after initState and whenever the dependencies change thereafter.)
initState
for didChangeDependencies
with listen false: Enters an infinite loop of requests.Any help on how should this be done?
In your ImageProvider do this changes:
class InventoryProvider extends ChangeNotifier {
List<Item> items = [];
Future<List<Item>> fetchAllItems() async {
items.addAll(await ItemService().fetchAllItems());
notifyListeners();
return items;
}
}
In your initState do this changes:
@override
void initState() {
super.initState();
_isLoading = true;
final itemProvider = Provider.of<InventoryProvider>(context, listen: false);
itemProvider.fetchAllItems().whenComplete(() {
setState(() {
_isLoading = false;
});
}).catchError((e) {
setState(() {
_errorMessage = e.message;
});
});
}
Wrap your scaffold with selector like this:
@override
Widget build(BuildContext context) {
return Selector<InventoryProvider, List<Item>>(
selector: (_, provider) => provider.items,
builder: (_, items, __) {
return Scaffold(...);
},
);
}