Flutter状态管理:Provider
2021-01-20 本文已影响0人
青叶小小
一、前言
此处系列章节目录,待更新
二、引入Provider第三方库
// pubspec.yaml
dependencies:
flutter:
sdk: flutter
provider: ^4.3.2
三、新增Model(CountProviderModel)
// CountProviderModel.dart
import 'package:flutter/material.dart';
class CountProviderModel extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
print("increment");
_count++;
notifyListeners();
}
}
四、局部刷新(单组件/单页面内部状态)
4.1、新增页面(ProviderPage)
// ProviderPage.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:stateresearch/model/CountProviderModel.dart';
class ProviderPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<CountProviderModel>(
create: (_) => CountProviderModel(),
builder: (context, child) {
return Scaffold(
appBar: AppBar(
title: Text("ProviderPage"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:'),
Consumer<CountProviderModel>(
builder: (context, notifier, child) {
return Text("${notifier.count}");
},
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => context.read<CountProviderModel>().increment(),
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
},
);
}
}
4.2、修改main文件
// 改写 main.dart
import 'package:flutter/material.dart';
import 'package:stateresearch/pages/ProviderPage.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter状态管理',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: ProviderPage(),
);
}
}
五、全局刷新(页面/组件状态共享)
5.1、新增两个页面(ProviderPageTwo和ProviderPageThree)
// ProviderPageTwo.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:stateresearch/model/CountProviderModel.dart';
import 'package:stateresearch/pages/ProviderPageThree.dart';
class ProviderPageTwo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("ProviderPageTwo"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:'),
Consumer<CountProviderModel>(
builder: (context, notifier, child) {
return Text("${notifier.count}");
},
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// 扩展 BuildContext,添加 read 方法,具体看附录
// context.read<T> 实际调用的是 Provider.of<T>
context.read<CountProviderModel>().increment();
// 2秒后跳转至新的页面
Future.delayed(Duration(seconds: 2), () {
Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {
return ProviderPageThree();
}));
});
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
// ProviderPageThree.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:stateresearch/model/CountProviderModel.dart';
class ProviderPageThree extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("ProviderPageThree"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:'),
Consumer<CountProviderModel>(
builder: (context, notifier, child) {
return Text("${notifier.count}");
},
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => Provider.of<CountProviderModel>(context, listen: false).increment(),
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
5.2、修改main文件
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:stateresearch/model/CountProviderModel.dart';
import 'package:stateresearch/pages/ProviderPageTwo.dart';
void main() {
runApp(
// APP顶层进行全局监听
// route 会进行向下传递该 Model
// 因此其它页面无需 ChangeNotifierProvider
// 只需要通过 Provider.of 或者 Consumer 获取 Model 即可
ChangeNotifierProvider(
create: (_) => CountProviderModel(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter状态管理',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: ProviderPageTwo(),
);
}
}
六、多Model全局共享
6.1、新增Model(ListProviderModel)
// ListProviderModel.dart
import 'package:flutter/material.dart';
class ListProviderModel extends ChangeNotifier {
List<String> _list = [];
List<String> get list => _list;
void push(String value) {
_list.add(value);
notifyListeners();
}
}
6.2、修改两个页面(ProviderPageTwo和ProviderPageThree)
// ProviderPageTwo.dart
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:stateresearch/model/CountProviderModel.dart';
import 'package:stateresearch/model/ListProviderModel.dart';
import 'package:stateresearch/pages/ProviderPageThree.dart';
class ProviderPageTwo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("ProviderPageTwo"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:'),
Consumer<CountProviderModel>(
builder: (context, notifier, child) {
return Text("${notifier.count}");
},
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
context.read<CountProviderModel>().increment();
context.read<ListProviderModel>().push("chris-${Random().nextInt(10)}");
Future.delayed(Duration(seconds: 2), () {
Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {
return ProviderPageThree();
}));
});
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
// ProviderPageThree.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:stateresearch/model/CountProviderModel.dart';
import 'package:stateresearch/model/ListProviderModel.dart';
class ProviderPageThree extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("ProviderPageThree"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:'),
Consumer<CountProviderModel>(
builder: (context, notifier, child) {
return Text("${notifier.count}");
},
),
Consumer<ListProviderModel>(
builder: (context, notifier, child) {
return Text("${notifier.list}");
},
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => Provider.of<CountProviderModel>(context, listen: false).increment(),
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
6.3、修改main文件
// main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:stateresearch/model/CountProviderModel.dart';
import 'package:stateresearch/model/ListProviderModel.dart';
import 'package:stateresearch/pages/ProviderPageTwo.dart';
void main() {
runApp(
// 多个 Model 需要全局共享时,需要用 MultiProvider 包一层
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => CountProviderModel()),
ChangeNotifierProvider(create: (_) => ListProviderModel()),
],
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter状态管理',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: ProviderPageTwo(),
);
}
}
七、总结
Provider是2019年Google I/O大会上主推的状态管理工具。
Provider的Model与ScopedModel的Model其实是一样的,都是继承于Listenable。
Provider还有其它继承于ChangeNotifier的子类,大家可以自行去尝试了解。
Provider可以全局+局部使用(即使用了全局Provider,也不影响某个Widget使用自己的Provider);
对比ScopedModel与Provider,我们发现,两者其实在应用上,区别不大:
- Model都是基于Listenable;
- 都可以用于全局监听或局部监听;
- 无论使用ScopedModel还是Provider,都不需要再使用StatefulWidget和State了,直接使用StatelessWidget即可;
- 全局Model时,ScopedModel需要借助于mixin,而Provider使用MultiProvider;
附录、源码之ChangeNotifier
class ChangeNotifier implements Listenable {
ObserverList<VoidCallback>? _listeners = ObserverList<VoidCallback>();
@protected
bool get hasListeners {
return _listeners!.isNotEmpty;
}
@override
void addListener(VoidCallback listener) {
_listeners!.add(listener);
}
@override
void removeListener(VoidCallback listener) {
_listeners!.remove(listener);
}
@mustCallSuper
void dispose() {
_listeners = null;
}
@protected
@visibleForTesting
void notifyListeners() {
if (_listeners != null) {
final List<VoidCallback> localListeners = List<VoidCallback>.from(_listeners!);
for (final VoidCallback listener in localListeners) {
try {
if (_listeners!.contains(listener))
listener();
} catch (exception, stack) {
......
}
}
}
}
}