《Flutter实战》第七章
2020-05-11 本文已影响0人
番茄tomato
- 本篇参考资料《Flutter实战》
- 本篇文章只是本人看书的理解和整理的笔记,更完整的内容还在书上!
- 电子书链接:https://book.flutterchina.club/
- Flutter中文社区链接:https://flutterchina.club/
- 尊重原作者,能支持购买实体书当然最好
本章两个重点Flutter的数据共享和状态管理InheritedWidget和Provider
一.InheritedWidget
InheritedWidget是Flutter中非常重要的一个功能型Widget,它可以高效的将数据在Widget树中向下传递、共享,这在一些需要在Widget树中共享数据的场景中非常方便
InheritedWidget能在Widget树中单向的从上到下传递共享数据
图一
InheritedWidget也是一个Widget,使用的时候我们通常新建一个Data类继承与它,然后在Data类中加入一些属性作为需要传递的数据。在建立Widget树的时候,讲Data类作为底层,此时只要是其子树都可以获得Data类数据的。并且Data类中数据若改变,可以根据需要判断子树中的数据是否改变,立刻刷新界面。
关于InheritedWidget的用法书上的7.2讲的非常清楚,这里不再赘述
https://book.flutterchina.club/chapter7/inherited_widget.html
但是InheritedWidget只能在子树上传递数据,那如果我们想要在非同一个InheritedWidget也为底层的子树上也共享同个数据该怎么办呢?就像下边这种情况:
图二
此时除非将widget4,5,6的底层也修改为Data类,转化为图一的情况,否则InheritedWidget就无法满足需求了。
比如这种情况:在不同的路由界面中共享同一个数据,此时我们无法将底层树改变成相同的,那么这时候怎么做呢?此时可以使用Provider
二.跨组件状态共享Provider
这部分书上有点模糊,参考链接http://www.luyixian.cn/news_show_332099.aspx
Provider的使用,这里也是实现一个计数器:
第一步 创建Model底层数据管理类
//跨组件共享状态
class CountModel extends ChangeNotifier {
//存储数据
int _count = 0;
//提供外部能够访问的数据 使用get
int get count => _count;
void increment() {
//点击一下+1
_count++;
//通知所有听众进行刷新
notifyListeners();
}
}
第二步 创建在树的底层创建底层数据共享
child: MultiProvider(
//创建底层数据共享
providers: [ChangeNotifierProvider(create: (_) => CountModel())],
child: Scaffold(...)
);
使用providers能添加多个Model
这里只是在某一个树底层添加数据共享,如果我们想要的是全局的数据共享,在全局管理app状态呢,其实只需要将这一步添加到程序入口最底层即可
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
//使用MultiProvider可以创建多个顶层共享数据
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_)=>Counter())
],
child: MaterialApp(
title: "Provider示例",
home: FirstPage(),
),
);
}
}
第三步 在不同子树中调用
读取显示数据:
Text("provider:${Provider.of<CountModel>(context).count}"),
点击按钮+1:
RaisedButton(
child: Text("provider"),
//每点击一次,将count自增,然后重新build,ShareDataWidget的data将被更新
onPressed: () => setState(() =>
Provider.of<CountModel>(context, listen: false)
.increment()),
)
效果图:
效果图
完整代码:
import 'dart:developer';
import 'package:flutter/material.dart';
import "package:provider/provider.dart";
class MyDataSharePage extends StatefulWidget {
@override
_MyDataSharePageState createState() => _MyDataSharePageState();
}
class _MyDataSharePageState extends State<MyDataSharePage> {
int pointCount = 0;
DateTime _lastPressedAt; //上次点击时间
int _currentIndex;
final List<BottomNavigationBarItem> bottomNavItems = [
BottomNavigationBarItem(
backgroundColor: Colors.blue,
icon: Icon(Icons.home),
title: Text("第一页"),
),
BottomNavigationBarItem(
backgroundColor: Colors.green,
icon: Icon(Icons.textsms),
title: Text("第二页"),
),
];
final pages = [MyDataShareFirstPage(), MyDataShareSecondPage()];
@override
void initState() {
super.initState();
_currentIndex = 0;
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
if (_lastPressedAt == null ||
DateTime.now().difference(_lastPressedAt) > Duration(seconds: 1)) {
//两次点击间隔超过1秒则重新计时
_lastPressedAt = DateTime.now();
return false;
}
return true;
},
child: MultiProvider(
//创建底层数据共享
providers: [ChangeNotifierProvider(create: (_) => CountModel())],
child: Scaffold(
bottomNavigationBar: BottomNavigationBar(
items: bottomNavItems,
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
fixedColor: Colors.blue[300],
onTap: (index) {
_changePage(index);
},
),
body: pages[_currentIndex],
)),
);
}
void _changePage(int index) {
if (index != _currentIndex) {
setState(() {
_currentIndex = index;
});
}
}
}
class ShareDataWidget extends InheritedWidget {
//这个不是容器 也没有界面绘制
//它的child 可以共享这个类中的数据 此数据改变 子组件中数据改变
int data; //需要在子树中共享的数据,保存点击次数
ShareDataWidget({@required this.data, Widget child}) : super(child: child);
static ShareDataWidget of(BuildContext context) {
//静态方法,在子widget中调用获取ShareDataWidget对象
return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
}
@override
bool updateShouldNotify(ShareDataWidget oldWidget) {
//如果数据不同了 就表示数据更新了 通知子组件更新数据
return oldWidget.data != data;
}
}
//组件内数据共享 父组件-->子组件
class MyDataShareFirstPage extends StatefulWidget {
@override
_MyDataShareFirstPageState createState() => _MyDataShareFirstPageState();
}
class _MyDataShareFirstPageState extends State<MyDataShareFirstPage> {
int pointCount = 0;
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
child: Center(
child: ShareDataWidget(
//从这开始 以下的子树就可以共享到数据了
data: pointCount,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(bottom: 20.0),
child: _TestShareWidget(), //子widget中依赖ShareDataWidget
),
RaisedButton(
child: Text("InheritedWidget"),
//每点击一次,将count自增,然后重新build,ShareDataWidget的data将被更新
onPressed: () => setState(() => ++pointCount),
),
Padding(
padding: const EdgeInsets.only(bottom: 20.0),
child: Text(
"provider:${Provider.of<CountModel>(context).count}"), //子widget中依赖ShareDataWidget
),
RaisedButton(
child: Text("provider"),
//每点击一次,将count自增,然后重新build,ShareDataWidget的data将被更新
onPressed: () => setState(() =>
Provider.of<CountModel>(context, listen: false)
.increment()),
),
]),
),
),
);
}
}
class _TestShareWidget extends StatefulWidget {
@override
__TestShareWidgetState createState() => __TestShareWidgetState();
}
class __TestShareWidgetState extends State<_TestShareWidget> {
@override
Widget build(BuildContext context) {
return Text(
"InheritedWidget: ${ShareDataWidget.of(context).data.toString()}");
}
}
//跨组件共享状态
class CountModel extends ChangeNotifier {
//存储数据
int _count = 0;
//提供外部能够访问的数据 使用get
int get count => _count;
void increment() {
//点击一下+1
_count++;
//通知所有听众进行刷新
notifyListeners();
}
}
class MyDataShareSecondPage extends StatefulWidget {
@override
_MyDataShareSecondPageState createState() => _MyDataShareSecondPageState();
}
class _MyDataShareSecondPageState extends State<MyDataShareSecondPage> {
Color _themeColor = Colors.teal; //当前路由主题色
@override
Widget build(BuildContext context) {
return Container(
child: Theme(
data: ThemeData(
primarySwatch: _themeColor, //用于导航栏、FloatingActionButton的背景色等
iconTheme: IconThemeData(color: _themeColor) //用于Icon颜色
),
child: Scaffold(
appBar: AppBar(
title: Text("Theme主题修改"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.label_outline),
Icon(Icons.account_balance),
],
),
Text("Provider:${Provider.of<CountModel>(context).count}"),
Container(
height: 200,
width: 200,
color: _themeColor, //树向上一级的主题是白色
child: Center(
child: FutureBuilder<String>(
future: mockNetworkData(),//绑定异步任务
builder:
(BuildContext context, AsyncSnapshot snapshot) {
// 请求已结束
if (snapshot.connectionState ==
ConnectionState.done) {
if (snapshot.hasError) {
// 请求失败,显示错误
return Text("Error: ${snapshot.error}");
} else {
// 请求成功,显示数据
return Text("Contents: ${snapshot.data}");
}
} else {
// 请求未结束,显示loading
return CircularProgressIndicator(
backgroundColor: Colors.red[100],
);
}
},
),
),
),
StreamBuilder<int>(
stream: counter(), //
//initialData: ,// a Stream<int> or null
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('没有Stream');
case ConnectionState.waiting:
return Text('等待数据...');
case ConnectionState.active:
return Text('active: ${snapshot.data}');
case ConnectionState.done:
return Text('Stream已关闭');
}
return null; // unreachable
},
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => //切换主题
setState(() => _themeColor = _themeColor == Colors.teal
? Colors.red
: Colors.teal),
child: Icon(Icons.palette)))),
);
}
Future<String> mockNetworkData() async {
return Future.delayed(Duration(seconds: 3), () => "我是从互联网上获取的数据");
}
Stream<int> counter() {
return Stream.periodic(Duration(seconds: 1), (i) {
return i;
});
}
}