Несколько моделей: MultiProvider
Часть раздела Управление состоянием путешествия по Flutter на Coddy. Урок 15 из 27.
Пусть каждая модель отвечает за что-то одно: счёт — в одной, жизни — в другой. Небольшие модели легко читать, а виджет следит только за тем, что ему нужно. Приложению с несколькими моделями нужно несколько провайдеров.
Можно вложить их друг в друга, разместив один провайдер внутри другого, но с каждой моделью код будет сдвигаться всё правее. Вместо этого MultiProvider принимает плоский список:
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => Score()),
ChangeNotifierProvider(create: (context) => Lives()),
],
child: const MyApp(),
),
);У провайдеров в списке нет собственного child: MultiProvider размещает их друг над другом и помещает под ними child. Каждый виджет ниже может следить за любой моделью или читать её, указывая её тип: context.watch<Lives>().
Задание
ЛегкоСтраница игры отслеживает две модели — Score и Lives, — но main предоставляет только очки, поэтому приложение показывает ошибку.
- Замени провайдер в
mainнаMultiProvider, который предоставляет обе модели.
Попробуйте сами
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'),
),
],
),
],
),
),
);
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Управление состоянием
3Provider
Provider: делимся модельюВызов notifyListenersProvider: watch или readProvider: ConsumerДва экрана — одна модельСписок объектов в моделиНесколько моделей: MultiProviderПотренируйтесь самостоятельно: Онлайн-компилятор Flutter