0%

Flutter新的状态管理方案

在Google IO 2019大会上提出了新的状态管理方案Provider用来替代之前的状态管理方案Provide,针对不同类型对象提供了多种不同的Provider;Provider借助了InheritWidget,将共享状态放到顶层Widget。

很细心的讲解文章

  • 借助了InheritWidget,允许将有效信息传递到组件树下的小组件
  • 提供DI
  • 创建和销毁实例
  • 结合Bloc等进行状态管理

Let’s Code

1
2
3
4
5
6
const Provider.value({
Key key,
@required T value,
this.updateShouldNotify,
this.child
}) : dispose = null, super.value(key: key, value: value);

上面的源码value的类型为范型,并没有进行限制,所以可以绑定任意数据类型

如何绑定数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class MyApp extends StatelessWidget {
@override
Widget build (BuildContext context) {
return MaterialApp(
title: 'demo',
home: Provider<String>.value(
value: 'demo',
child: Demo()
)
)
}
}

Provider.of()在Provider窗口小部件对应的后代中BuildContext是必需的;获取BuildContext麻烦时Consumer()是很好的替代方式

class MyApp extends StatelessWidget {
@override
Widget build (BuildContext context) {
return MaterialApp(
title: 'demo',
home: Provider<String>.value(
value: 'demo',
child: Consumer<String>(
builder: (context, value, child) {
return Center(
child: Text(value)
)
}
)
)
)
}
}
如何获取数据

provider需要在绑定的子widget中获取数据,使用静态方法Provider.of(BuildContext context),此方法将从关联的widget树中查找最近的相同类型的数据

1
2
3
4
5
6
7
8
9
Class Demo extends StatelessWidget {
@override
Widget build (BuildContext context) {
final value = Provider.of<String>(context);
return Center(
child: Text(value)
)
}
}

#####以上简述了Provider的数据绑定几获取的方法,下面在应用场景里实现Provider的作用,以官方的计数器的例子来举例

首先创建一个Counter类,并封装他的增和减的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Counter with ChangeNotifier{
int _counter;
Counter(this._counter);
getCouonter => _counter;
setCounter(int counter) => _counter = counter;
void increment() {
_counter ++;
notifyListeners();
}
void decrement() {
_counter --;
notifyListeners();
}
}

现在Counter类拥有了监听的功能,我们需要调用notifyListeners()去通知监听器数据变化,并更新UI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class MyApp extends StatelessWidget {
@override
Widget build (BuildContext context) {
return MaterialApp(
title: 'demo',
home: ChangeNotifierProvider<Counter>(
builder: (_) => Counter(0),
child: HomePage()
)
)
}
}

Class HomePage extends StatelessWidget {
@override
Widget build (BuildContext context) {
final counter = Provider.of<Counter>(context);
return Scaffold(
appBar: AppBar(
title: Text('ChangeNotifierProvider demo')
),
body: Center(
child: Text(counter.getCounter())
),
floatingActionButton: Column(
children: <Widget>[
FloatingActionButton(
onPressed: counter.increment,
child: Icon(Icons.add)
),
FloatingActionButton(
onPressed: counter.decrement,
child: Icon(Icons.remove)
),
]
)
)
}
}
Provider结合Bloc模式进行状态管理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class CounterBloc {
final _valueController = StreamController<String>();
Stream<String> get stream => _valueController.stream;
int _number = 0;
void increment() {
_number++;
_valueController.sink.add(_number.toString());
}
void dispose() {
_valueController.close();
}
}

class ProviderPage extends StatelessWidget {
@override
Widget build (BuildContext context) {
return Provider<CounterBloc>(
builder: (_) => CounterBloc(),
dispose: (_, bloc) => bloc.dispose(),
child: Scaffold(
body: CounterText(),
floatingActionButton: _floatingButton(),
)
)
}


Widget _floatingButton() {
return Consumer<CounterBloc>(
builder: (context, value, child) {
return FloatingActionButton(
onPressed: value.increment,
child: const Icon(Icons.add),
);
}
)
}
}


class CounterText extends StatelessWidget {
@override
Widget build (BuildContext context) {
final bloc = Provider.of<CounterBloc>(context);
return StreamBuilder<String>(
stream: bloc.stream,
builder: (context, snapshot) {
return Center(
child: Text(snapshot.data ?? '0')
)
}
)
}
}
以上可以用Provder.value()实现,引入简单值传播
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class ProviderValuePage extends StatefulWidget {
@override
ProviderValueState createState() => ProviderValueState();
}

class ProviderValueState extends State<ProviderValuePage> {
final _bloc = CounterBloc();

@override
Widget build(BuildContext context) {
return Scaffold(
body: Provider<CounterBloc>.value(
value: _bloc,
child: CounterText(),
),
floatingActionButton: FloatingActionButton(
onPressed: bloc.increment,
child: const Icon(Icons.add),
),
);
}

@override
void dispose() {
_bloc.dispose();
super.dispose();
}
}

class CounterText extends StatelessWidget {
@override
Widget build(BuildContext context) {
final bloc = Provider.of<CounterBloc>(context);

return StreamBuilder<String>(
stream: bloc.stream,
builder: (context, snapshot) {
return Center(
child: Text(snapshot.data ?? '0')
);
},
);
}
}

ChangeNotifierProvider与ChangeNotifierProvider.value()等提供商的唯一区别在于ChangeNotifierProvider.value()创建和销毁模型实例需要自行处理

多提供商的场景

1
2
3
4
5
6
7
8
9
10
Provider<Foo>.value(
value: foo,
child: Provider<Bar>.value(
value: bar,
child: Provider<Baz>.value(
value: baz,
child: someWidget
)
)
)

一层一层的嵌套导致代码可读性降低,所以产生了MultiProvider

1
2
3
4
5
6
7
8
MultiProvider(
providers: [
Provider<Foo>.value(value: foo),
Provider<Bar>.value(value: bar),
Provider<Baz>.value(value: baz),
],
child: someWidget
)
注意的是需要指定不同的类型,如果是相同的类型,将保留最后一个Provider的值