Flutter 状态管理(StatelessWidget,StatefulWidget),前面介绍了什么是状态,Provider是官方推出的状态管理模式,本文将具体介绍Flutter的状态管理。
有状态及无状态组件
无状态组件(StatelessWidget
)是不可变的,这意味着它们的属性不能改变,所有的值都是最终的。
有状态组件(StatefulWidget
)持有的状态可能在Widget生命周期中发生变化。实现一个StatefulWidget
至少需要两个类:
- StatefulWidget类,本身是不变的。
- State类,在Widget生命周期中始终存在。
Flutter官方给出一个有状态组件的示例,点击右下角的+按钮,应用界面中间的数字会加1,如图所示。
这个示例有几个关键的部分,解析如下。示例代码中MyHomePage
必须继承自StatefulWidget
类,如下所示:
class MyHomePage extends StatefulWidget
重写createState方法,如下所示:
@override
MyHomePageState createState() => _MyHomePageState();
状态类必须继承自State
类,如下所示:
class _MyHomePageState extends State<MyHomePage>
定义一个普通变量_counter作为计数器变量,调用setState
方法来控制这个变量的值的变化,如下所示:
int _counter = 0;
void _incrementCounter() {
setState(() {
// 计数器变量
_counter++;
});
}
完整的示例代码如下所示:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
// MyApp不需要做状态处理,所以此组件继承StatelessWidget即可
class MyApp extends StatelessWidget {
// 这个组件是整个应用的主组件
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter示例',
theme: ThemeData(
// 自定义主题
primarySwatch: Colors.blue,
),
home: MyHomePage(title: '无状态和有状态组件示例'),
);
}
}
// 主页需要继承自StatefulWidget
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// 标题
final String title;
// 必须重写createState方法
@override
_MyHomePageState createState() => _MyHomePageState();
}
// 状态类必须继承State类,注意后面需要指定为<MyHomePage>
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0; //计数器
void _incrementCounter() {
// 调用State类里的setState方法来更改状态值,使得计数器加1
setState(() {
// 计数器变量,每次点击让其加1
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
// 居中布局
body: Center(
// 垂直布局
child: Column(
// 主轴居中对齐
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'你点击右下角按钮的次数:',
),
Text(
'$_counter', //绑定计数器的值
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter, //点击+按钮调用自增函数
tooltip: '增加',
child: Icon(Icons.add),
),
);
}
}
Provider的使用
当我们的应用足够简单时,你可能并不需要状态管理。但是随着功能的增加,应用程序将会有几十个甚至上百个状态,你的应用状态变得难以维护。Flutter实际上在一开始就为我们提供了一种状态管理方式,那就是StatefulWidget
。但是我们很快发现,它正是造成上述问题的罪魁祸首。这时候,我们便迫切需要一个架构来帮助我们厘清这些关系,状态管理框架
应运而生。
Provider顾名思义即为提供者,用于提供数据,无论是在单个页面还是在整个应用中都有它自己的解决方案,可以很方便地管理状态。
这里我们以计数器
为例来详细讲解Provider的使用方法。步骤如下所示。
步骤1:在工具目录下的pubspec.yaml文件中添加Provider的依赖。
步骤2:创建数据Model,这里的Model实际上就是我们的状态,它不仅存储了我们的数据模型,还包含了更改数据的方法,并暴露出它想要暴露出的数据,如下所示:
class Counter with ChangeNotifier {
// 存储数据
int _count = 0;
// 提供外部能够访问的数据
int get count => _count;
// 提供更改数据的方法
void increment(){
_count++;
// 通知所有听众进行刷新
notifyListeners();
}
}
我们要存储的数据即为_count,下划线代表私有。通过get把_count值暴露出来。并提供increment方法用于更改数据。
这里使用了mixin的方式混入了ChangeNotifier,这个类能够帮助我们自动管理所有听众。当调用notifyListeners时,它会通知所有听众进行刷新。
步骤3:创建顶层共享数据。这里使用MultiProvider可以创建多个共享数据,因为实际的应用不可能只有一个数据模型,代码如下:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 使用MultiProvider可以创建多个顶层共享数据
return MultiProvider(
providers: [
ChangeNotifierProvider(builder: (_) => Counter()),
],
child: MaterialApp(
// 首页
),
);
}
}
步骤4:在子页面中获取状态,在这里我们分别编写了FirstPage及SecondPage。获取顶层数据的方法就是Provider.of<T>(context)
,这里的范型<T>
指定为Counter。获取Counter里的值的代码如下所示:
Provider.of<Counter>(context).count
在两个页面中均添加以上代码用于获取Counter的值。同时两个页面也可以改变Counter的值,使用方法如下所示:
Provider.of<Counter>(context).increment();
上述两个页面的增加值的操作是同步的。当在FirstPage页面中点击增加值后,跳转至SecondPage页面时显示的值相同,反之,当在SecondRage中点击增加值后,跳转至FirstPage页面时显示的值相同,如图所示。
从执行的结果来看,第一个页面和第二个页面的数据得到了共享,改变其中一个页面的值会同时影响另一个页面。完整的代码如下所示:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
main() {
runApp(MyApp(),);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 使用MultiProvider可以创建多个顶层共享数据
return MultiProvider(
providers: [
ChangeNotifierProvider(builder: (_) => Counter()),
],
child: MaterialApp(
title: "Provider示例",
home: FirstPage(),
),
);
}
}
// 第一个页面
class FirstPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("第一个页面"),
actions: <Widget>[
FlatButton(
child: Text("下一页"),
// 路由跳转至第二页
onPressed: () =>
Navigator.push(context, MaterialPageRoute(builder: (context) {
return SecondPage();
})),
),
],
),
body: Center(
// 获取计数器中的count值
child: Text("${Provider.of<Counter>(context).count}"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// 调用数据模型中的increment方法更改数据
Provider.of<Counter>(context).increment();
},
child: Icon(Icons.add),
),
);
}
}
// 第二个页面
class SecondPage extends StatelessWidget {
@override
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(
title: Text("第二个页面"),
),
body: Center(
// 获取计数器中的count值
child: Text("${Provider.of<Counter>(context).count}"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// 调用数据模型中的increment方法更改数据
Provider.of<Counter>(context).increment();
},
child: Icon(Icons.add),
),
);
}
}
/**
* 计数器类Counter即为数据Model,实际上就是状态。
* Counter不仅存储了数据,还包含了更改数据的方法,并暴露相关数据。
* 使用mixin混入ChangeNotifier类,这个类能够自动管理所有听众。
* 当调用notifyListeners时,它会通知所有听众进行刷新
*/
class Counter with ChangeNotifier {
// 存储数据
int _count = 0;
// 提供外部能够访问的数据
int get count => _count;
// 提供更改数据的方法
void increment(){
_count++;
// 通知所有听众进行刷新
notifyListeners();
}
}
评论前必须登录!
注册