《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;
    });
  }
}

上一篇 下一篇

猜你喜欢

热点阅读