简介
State management涉及跟踪整个应用程序的状态更改。
provider
包是满足状态管理需求的一种解决方案。
在本文中,您将学习如何将Provider
应用于一个样例Flight应用程序来管理用户帐户信息的状态。
前提条件
要完成本教程,您需要:
- 下载并安装Flutter.
- 下载并安装Android StudioorVisual Studio代码.
- 建议为您的代码编辑器安装插件:
-Android Studio安装的
Flutter
和Dart
插件。 -为Visual Studio代码安装的Flutter
扩展。 - 熟悉导航和航线将是有益的,但不是必需的。
- 熟悉表格状态也是有益的,但不是必需的。
本教程使用Ffltter v2.0.6、Android SDK v31.0.2和Android Studio v4.1进行了验证。
了解问题所在
考虑这样一种情况,您想要构建一个应用程序,该应用程序使用用户的一些数据(如他们的姓名)来定制其某些屏幕。在屏幕之间传递数据的正常方法很快就会变成一堆混乱的回调、未使用的数据和不必要的重新构建的小部件。对于像Reaction这样的前端库,这是一个常见的问题,称为道具钻探。
如果我们想要从这些小部件中的任何一个传递数据,那么您需要使用更多未使用的回调来进一步膨胀每个中间小部件。对于大多数小功能,这可能会使它们几乎不值得付出努力。
对我们来说幸运的是,provider
包允许我们将数据存储在一个更高的小部件中,就像我们初始化MaterialApp
的地方一样,然后直接从子小部件访问和更改它,而不考虑嵌套,也不需要重建其间的所有内容。
第一步-设置项目
将环境设置为颤动后,可以运行以下命令来创建新应用程序:
1flutter create flutter_provider_example
导航到新项目目录:
1cd flutter_provider_example
使用fltter create
将生成一个演示应用程序,其中将显示按钮被点击的次数。
第二步-添加Provider
插件
接下来,我们需要在pubspec.yaml
中添加Provider
插件:
1[label pubspec.yaml]
2dependencies:
3 flutter:
4 sdk: flutter
5
6 provider: ^3.1.0
然后,将更改保存到您的文件。
<$>[备注] 注意: 如果您使用的是VS代码,您可能需要考虑使用PubSpec Assist扩展来快速添加依赖项。 <$>
我们现在可以继续在iOS或Android模拟器或您选择的设备上运行此程序。
Step 3 -搭建项目
我们需要两个屏幕,我们的路由器和一个导航栏。我们正在设置一个页面来显示我们的帐户数据,另一个页面则用存储、更改和从路由器向下传递的状态本身来更新它。
在代码编辑器中打开main.dart
并修改以下代码行:
1[label lib/main.dart]
2import 'package:flutter/material.dart';
3import 'package:provider/provider.dart';
4import './screens/account.dart';
5import './screens/settings.dart';
6
7void main() {
8 runApp(MyApp());
9}
10
11class MyApp extends StatelessWidget {
12 @override
13 Widget build(BuildContext context) {
14 return MaterialApp(
15 title: 'Flutter Provider Demo',
16 home: MyHomePage(),
17 );
18 }
19}
20
21class MyHomePage extends StatefulWidget {
22 @override
23 _MyHomePageState createState() => _MyHomePageState();
24}
25
26class _MyHomePageState extends State<MyHomePage> {
27 @override
28 Widget build(BuildContext context) {
29 return MaterialApp(home: AccountScreen(), routes: {
30 'account_screen': (context) => AccountScreen(),
31 'settings_screen': (context) => SettingsScreen(),
32 });
33 }
34}
创建一个navbar.dart
文件,并使用代码编辑器打开它:
1[label lib/navbar.dart]
2import 'package:flutter/material.dart';
3import './screens/account.dart';
4import './screens/settings.dart';
5
6class Navbar extends StatelessWidget {
7 @override
8 Widget build(BuildContext context) {
9 return Container(
10 color: Colors.blue,
11 child: Row(
12 mainAxisAlignment: MainAxisAlignment.spaceAround,
13 children: <Widget>[
14 TextButton(
15 onPressed: () =>
16 Navigator.pushReplacementNamed(context, AccountScreen.id),
17 child: Icon(Icons.account_circle, color: Colors.white)
18 ),
19 TextButton(
20 onPressed: () =>
21 Navigator.pushReplacementNamed(context, SettingsScreen.id),
22 child: Icon(Icons.settings, color: Colors.white)
23 ),
24 ],
25 ),
26 );
27 }
28}
在lib
目录下,新建一个creens
子目录:
1mkdir lib/screens
在这个子目录中,创建一个settings.dart
文件。这将用于创建表单状态、设置映射以存储输入,以及添加稍后将使用的提交按钮:
1[label lib/screens/settings.dart]
2import 'package:flutter/material.dart';
3import 'package:provider/provider.dart';
4import '../main.dart';
5import '../navbar.dart';
6
7class SettingsScreen extends StatelessWidget {
8 static const String id = 'settings_screen';
9
10 final formKey = GlobalKey<FormState>();
11
12 final Map data = {'name': String, 'email': String, 'age': int};
13
14 @override
15 Widget build(BuildContext context) {
16 return Scaffold(
17 bottomNavigationBar: Navbar(),
18 appBar: AppBar(title: Text('Change Account Details')),
19 body: Center(
20 child: Container(
21 padding: EdgeInsets.symmetric(vertical: 20, horizontal: 30),
22 child: Form(
23 key: formKey,
24 child: Column(
25 mainAxisAlignment: MainAxisAlignment.center,
26 crossAxisAlignment: CrossAxisAlignment.center,
27 children: <Widget>[
28 TextFormField(
29 decoration: InputDecoration(labelText: 'Name'),
30 onSaved: (input) => data['name'] = input,
31 ),
32 TextFormField(
33 decoration: InputDecoration(labelText: 'Email'),
34 onSaved: (input) => data['email'] = input,
35 ),
36 TextFormField(
37 decoration: InputDecoration(labelText: 'Age'),
38 onSaved: (input) => data['age'] = input,
39 ),
40 TextButton(
41 onPressed: () => formKey.currentState.save(),
42 child: Text('Submit'),
43 style: TextButton.styleFrom(
44 primary: Colors.white,
45 backgroundColor: Colors.blue,
46 ),
47 )
48 ]
49 ),
50 ),
51 ),
52 ),
53 );
54 }
55}
同样在该子目录中,创建一个addt.dart
文件。这将用于显示帐户信息:
1[label lib/screens/account.dart]
2import 'package:flutter/material.dart';
3import 'package:provider/provider.dart';
4import '../main.dart';
5import '../navbar.dart';
6
7class AccountScreen extends StatelessWidget {
8 static const String id = 'account_screen';
9
10 @override
11 Widget build(BuildContext context) {
12 return Scaffold(
13 bottomNavigationBar: Navbar(),
14 appBar: AppBar(
15 title: Text('Account Details'),
16 ),
17 body: Center(
18 child: Column(
19 mainAxisAlignment: MainAxisAlignment.center,
20 children: <Widget>[
21 Text('Name: '),
22 Text('Email: '),
23 Text('Age: '),
24 ],
25 ),
26 ),
27 );
28 }
29}
编译代码并让其在模拟器中运行:
等字段
此时,您拥有了一个带有帐户屏幕和设置屏幕的应用程序。
第四步-使用Provider
设置Provider
需要将我们的MaterialApp
包装在一个带有我们的数据类型的Provider
中。
重新访问main.dart
并在代码编辑器中打开它。在本教程中,数据类型为Map
。最后,我们需要设置create
,然后使用我们的context
和data
:
1[label lib/main.dart]
2// ...
3
4class _MyHomePageState extends State<MyHomePage> {
5 Map data = {
6 'name': 'Sammy Shark',
7 'email': '[email protected]',
8 'age': 42
9 };
10
11 @override
12 Widget build(BuildContext context) {
13 return Provider<Map>(
14 create: (context) => data,
15 child: MaterialApp(home: AccountScreen(), routes: {
16 'account_screen': (context) => AccountScreen(),
17 'settings_screen': (context) => SettingsScreen(),
18 }),
19 );
20 }
21}
现在,main.dart
调用并导入Provider
包的所有其他屏幕和小部件中都可以使用data
映射。
我们传递给Provider
创建者的所有内容现在都可以在Provider.of<Map>(上下文)
上使用。请注意,您传入的类型必须与Provider
期望的数据类型匹配。
<$>[备注]
注意: 如果您使用的是VS代码,您可能会经常访问Provider
,所以您可能会考虑使用snippets:
1[label dart.json]
2"Provider": {
3 "prefix": "provider",
4 "body": [
5 "Provider.of<$1>(context).$2"
6 ]
7}
<$>
重新访问count t.dart
并在代码编辑器中打开它。添加以下代码行:
1[label lib/screens/account.dart]
2// ...
3
4body: Center(
5 child: Column(
6 mainAxisAlignment: MainAxisAlignment.center,
7 children: <Widget>[
8 Text('Name: ' + Provider.of<Map>(context)['name'].toString()),
9 Text('Email: ' + Provider.of<Map>(context)['email'].toString()),
10 Text('Age: ' + Provider.of<Map>(context)['age'].toString()),
11 ]),
12 ),
13)
14
15// ...
编译代码并让其在模拟器中运行:
此时,您就拥有了一个应用程序,其中包含显示在帐户屏幕上的硬编码用户数据。
第五步-使用ChangeNotifier
这样使用Provider
似乎是自上而下的,如果我们想向上传递数据并修改我们的地图,该怎么办?光有供应商
是不够的。首先,我们需要将我们的数据分解到它自己的类中,该类扩展了ChangeNotifier。Provider
不能使用它,所以我们需要将其更改为ChangeNotifierProvider
,并传入我们的Data
类的实例。
现在我们传递了一个完整的类,而不仅仅是一个变量,这意味着我们可以开始创建可以操作我们的数据的方法,这些方法也将对访问Provider
的所有对象可用。
在我们更改了任何全局数据之后,我们想要使用nufyListeners
,它将重新构建依赖它的所有小部件。
重新访问main.dart
并在代码编辑器中打开它:
1[label lib/main.dart]
2// ...
3
4class _MyHomePageState extends State<MyHomePage> {
5 @override
6 Widget build(BuildContext context) {
7 return ChangeNotifierProvider<Data>(
8 create: (context) => Data(),
9 child: MaterialApp(home: AccountScreen(), routes: {
10 'account_screen': (context) => AccountScreen(),
11 'settings_screen': (context) => SettingsScreen(),
12 }),
13 );
14 }
15}
16
17class Data extends ChangeNotifier {
18 Map data = {
19 'name': 'Sammy Shark',
20 'email': '[email protected]',
21 'age': 42
22 };
23
24 void updateAccount(input) {
25 data = input;
26 notifyListeners();
27 }
28}
由于我们更改了Provider
类型,因此需要更新对它的调用。重新访问count t.dart
并在代码编辑器中打开它:
1[label lib/screens/account.dart]
2// ...
3
4body: Center(
5 child: Column(
6 mainAxisAlignment: MainAxisAlignment.center,
7 children: <Widget>[
8 Text('Name: ' + Provider.of<Data>(context).data['name'].toString()),
9 Text('Email: ' + Provider.of<Data>(context).data['email'].toString()),
10 Text('Age: ' + Provider.of<Data>(context).data['age'].toString()),
11 ]),
12 ),
13)
14
15// ...
要向上传递数据,我们需要使用在Data
类中向下传递的方法访问Provider
。重新访问settings.dart
并在代码编辑器中打开它:
1[label lib/screens/settings.dart]
2TextButton(
3 onPressed: () {
4 formKey.currentState.save();
5 Provider.of<Data>(context, listen: false).updateAccount(data);
6 formKey.currentState.reset();
7 },
8)
编译代码并让其在模拟器中运行:
至此,您拥有了一个支持在设置屏幕上更新用户信息并在帐户屏幕上显示更改的应用程序。
结论
在本文中,您学习了如何将Provider
应用于一个样例Flight应用程序来管理用户帐户信息的状态。
如果您想了解更多有关Ffltter的知识,请查看我们的Ffltter主题页面以获取练习和编程项目。