两个屏幕,一个模型
本课属于 Coddy Flutter 学习旅程的“状态管理”单元。第 13 课,共 27 课。
当两个屏幕共享数据时,Provider 就能发挥作用。设置屏幕可以更改名称,主页会显示这个名称,而两个屏幕之间不需要互相传递任何内容。
Provider 的位置很重要。MaterialApp 包含一个 Navigator,你推入的每个屏幕都会和 home 一起放入其中,而不是放在它里面:
ChangeNotifierProvider <- above MaterialApp: every screen finds it
MaterialApp
Navigator
HomePage
SettingsScreen <- pushed next to HomePage如果只在 HomePage 外层包裹一个 provider,它只能被 HomePage 访问。被推入的屏幕不在它的下层,因此无法找到这个模型。把 provider 放在 main 中的 MaterialApp 上方,这样每个屏幕都能共享它。
不要在另一个屏幕上再添加一个 ChangeNotifierProvider 来修复错误。这样会创建第二个模型:错误虽然消失了,但屏幕之间不再共享任何内容。
挑战
中等点按齿轮,输入姓名,然后按 Save:应用会报错,因为 provider 只包装了 HomePage。
- 将
ChangeNotifierProvider从MyApp中移出,放到main中,并让它包装MyApp。
这样,保存 Ada 后,首页就会显示 Hello, Ada!。
亲手试试
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Profile extends ChangeNotifier {
String _name = 'Guest';
String get name => _name;
void rename(String newName) {
_name = newName;
notifyListeners();
}
}
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: ChangeNotifierProvider(
create: (context) => Profile(),
child: const HomePage(),
),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
final profile = context.watch<Profile>();
return Scaffold(
appBar: AppBar(
title: const Text('Home'),
actions: [
IconButton(
icon: const Icon(Icons.settings),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const SettingsScreen()),
);
},
),
],
),
body: Center(
child: Text('Hello, ${profile.name}!', style: const TextStyle(fontSize: 32)),
),
);
}
}
class SettingsScreen extends StatefulWidget {
const SettingsScreen({super.key});
@override
State<SettingsScreen> createState() => _SettingsScreenState();
}
class _SettingsScreenState extends State<SettingsScreen> {
final controller = TextEditingController();
@override
Widget build(BuildContext context) {
final profile = context.watch<Profile>();
return Scaffold(
appBar: AppBar(title: const Text('Settings')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Text('Signed in as ${profile.name}'),
const SizedBox(height: 16),
TextField(
controller: controller,
decoration: const InputDecoration(labelText: 'Your name'),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () {
context.read<Profile>().rename(controller.text);
Navigator.pop(context);
},
child: const Text('Save'),
),
],
),
),
);
}
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
状态管理中的所有课
自己练习: Flutter 在线编译器