Several Models: MultiProvider
Part of the State Management section of Coddy's Flutter journey. Lesson 15 of 27.
Keep each model about one thing: the score in one class, the lives in another. Small models are easy to read, and a widget watches only what it needs. An app with several models needs several providers.
You could nest them, one provider as the child of the next, but the code drifts to the right with every model. MultiProvider takes a flat list instead:
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => Score()),
ChangeNotifierProvider(create: (context) => Lives()),
],
child: const MyApp(),
),
);The providers in the list have no child of their own: MultiProvider stacks them for you and puts child under them all. Every widget below can watch or read any of the models, each one by its type: context.watch<Lives>().
Challenge
EasyThe game page watches two models, Score and Lives, but main provides only the score, so the app shows an error.
- Replace the provider in
mainwith aMultiProviderthat provides both models.
Try it yourself
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Score extends ChangeNotifier {
int _points = 0;
int get points => _points;
void hit() {
_points += 10;
notifyListeners();
}
}
class Lives extends ChangeNotifier {
int _left = 3;
int get left => _left;
void miss() {
if (_left > 0) {
_left--;
notifyListeners();
}
}
}
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => Score(),
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: GamePage(),
);
}
}
class GamePage extends StatelessWidget {
const GamePage({super.key});
@override
Widget build(BuildContext context) {
final points = context.watch<Score>().points;
final lives = context.watch<Lives>().left;
return Scaffold(
appBar: AppBar(title: Text('Lives: $lives')),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Points: $points', style: const TextStyle(fontSize: 40)),
const SizedBox(height: 16),
Row(
mainAxisSize: MainAxisSize.min,
children: [
ElevatedButton(
onPressed: () => context.read<Score>().hit(),
child: const Text('Hit'),
),
const SizedBox(width: 16),
ElevatedButton(
onPressed: () => context.read<Lives>().miss(),
child: const Text('Miss'),
),
],
),
],
),
),
);
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in State Management
3Provider
Provider: Share a ModelCalling notifyListenersProvider: Watch or ReadProvider: ConsumerTwo Screens, One ModelA List of Objects in a ModelSeveral Models: MultiProviderPractice on your own: Online Flutter compiler