Provider:Consumer
CoddyのFlutterジャーニー「状態管理」セクションの一部。レッスン 12/27。
ここでは、プロバイダーはMyApp自身のbuild内で作成され、同じbuildがそのすぐ下でランプを要求しています。アプリにはエラーが表示されます。contextはプロバイダーの上にあるMyAppに属しており、ウィジェットは自分より上にあるプロバイダーしか見つけられません。
Consumerを使えば解決できます。これはウィジェットなのでプロバイダーの下に配置され、そのbuilderには新しいcontextとモデル自体が渡されます。
Consumer<Lamp>(
builder: (context, lamp, child) {
return Text(lamp.isOn ? 'On' : 'Off');
},
)ランプがnotifyListeners()を呼び出すたびに、ランプを監視しているウィジェットのように、builderが再実行されます。その中では、モデルのメソッドをlampに対して直接呼び出せます。onPressed: () => lamp.toggle()のようにします。
Consumerはラップした部分だけを再ビルドするため、変化する部分をラップすれば、画面の残りの部分はそのまま保たれます。ほとんどのアプリでは、専用のウィジェット内でcontext.watchを使い、プロバイダーと同じbuild内にいる場合はConsumerを使います。
チャレンジ
中級アプリを開くとエラーが表示されます。ランプの各部分をConsumerの中に配置してください。
ColumnをConsumer<Lamp>で囲み、そのビルダーからColumnを返してください。- その中では、アイコン、テキスト、ボタンにビルダーの
lampを使用してください。
自分で試してみよう
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Lamp extends ChangeNotifier {
bool _isOn = false;
bool get isOn => _isOn;
void toggle() {
_isOn = !_isOn;
notifyListeners();
}
}
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => Lamp(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(title: const Text('Lamp')),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
context.watch<Lamp>().isOn ? Icons.lightbulb : Icons.lightbulb_outline,
size: 96,
color: context.watch<Lamp>().isOn ? Colors.amber : Colors.grey,
),
Text(context.watch<Lamp>().isOn ? 'The lamp is on' : 'The lamp is off'),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () => context.read<Lamp>().toggle(),
child: const Text('Switch'),
),
],
),
),
),
),
);
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
状態管理のすべてのレッスン
自分で練習してみよう: Flutterオンラインコンパイラ