选择状态的存放位置
本课属于 Coddy Flutter 学习旅程的“状态管理”单元。第 21 课,共 27 课。
设置屏幕上的深色模式开关可以切换,但界面并没有变暗。它通过 setState 保存了自己的值,只有这个开关能读取它。主题是在很远的 MyApp 中设置的。这个值属于整个应用。
应用中的每个值都需要有自己的归属。先想想谁会用到它:
- 一个组件(输入框中的文本、打开的面板、标签页索引):在该组件中使用
setState。 - 几个相邻的组件(一个按钮和旁边的标签):将状态提升到它们的父组件,再向下传递。
- 许多组件或多个屏幕(用户、购物车、设置):使用 Provider 或 Riverpod 共享模型。
选 Provider 还是 Riverpod?两者都能完成任务。Provider 稍微简单一些,会把模型放在组件树中,并通过 context 查找。Riverpod 将提供者放在组件树之外,因此读取值不需要 context,而且提供者可以互相监听。为一个应用选定一种,并在整个应用中使用它。
这里适合使用 StateProvider:一个 bool 值,由 MyApp 读取来选择主题,并由开关更改:
final darkModeProvider = StateProvider((ref) => false);
// 在 MyApp 中,现在是 ConsumerWidget:
themeMode: ref.watch(darkModeProvider) ? ThemeMode.dark : ThemeMode.light,挑战
困难让深色模式开关控制整个应用:
- 在顶部添加
final darkModeProvider = StateProvider((ref) => false);。 - 将
MyApp改为一个ConsumerWidget,使其themeMode跟随该提供器。 - 将
SettingsScreen改为一个ConsumerWidget:开关显示提供器的值,并在值改变时设置该值。
亲手试试
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
themeMode: ThemeMode.light,
home: const HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Notes'),
actions: [
IconButton(
icon: const Icon(Icons.settings),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const SettingsScreen()),
);
},
),
],
),
body: const Center(child: Text('No notes yet')),
);
}
}
class SettingsScreen extends StatefulWidget {
const SettingsScreen({super.key});
@override
State<SettingsScreen> createState() => _SettingsScreenState();
}
class _SettingsScreenState extends State<SettingsScreen> {
bool dark = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Settings')),
body: ListView(
children: [
SwitchListTile(
title: const Text('Dark mode'),
value: dark,
onChanged: (value) {
setState(() {
dark = value;
});
},
),
],
),
);
}
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
状态管理中的所有课
自己练习: Flutter 在线编译器