【Flutter从入门到实战】⑭、状态管理、Flutter为什么需要状态管理、共享状态管理inheriedWidget的使用、Flutter官方推荐第三方Provider的使用

Flutter从入门到实战
一共分为23个系列
①(Flutter、Dart环境搭建篇) 共3个内容 已更新
②(Dart语法1 篇) 共4个内容 已更新
③(Dart语法2 篇) 共2个内容 已更新
④(Flutter案例开发篇) 共4个内容 已更新
⑤(Flutter的StatelessWidget 共3个内容 已更新
⑥(Flutter的基础Widget篇) 共2个内容 已更新
⑦(布局Widget篇) 共1个内容 已更新
⑧(Flex、Row、Column以及Flexible、Stack篇) 共1个内容 已更新
⑨(滚动的Widget篇) 共4个内容 已更新
⑩(Dart的Future和网络篇) 共3个内容 已更新
⑪(豆瓣案例-1篇) 共3个内容 已更新
⑫(豆瓣案例-2篇) 共3个内容 已更新
⑬(Flutter渲染流程篇) 共3个内容 已更新
⑭(状态管理篇) 共3个内容 已更新

官方文档说明

官方视频教程
Flutter的YouTube视频教程-小部件

请添加图片描述


①、状态State

1. 为什么需要状态管理

State官方文档说明
StateFulWidget
StateFulWidget视频详细说明
StatelessWidget
StatelessWidget视频详细说明
setState文档

在编写一个应用的过程中,我们有大量的State需要来进行管理,而正是对这些State的改变,来更新界面的刷新:

2. State的分类

1.1 短时状态

比如简单计数器counter
比如PagerView组件记录当前页面
比如BottomNavigationBar被选择的tab

1.2 应用AppState状态

比如用户一个个性化选项
比如用户登录状态
电商应用的后误差
新闻应用的已读 或者未读

3.共享状态管理InheritedWidget

InheritedWidget官方文档
作用:能获取最顶层的共享状态

3.1 共享状态管理InheritedWidget的使用

页面有两个Widget 想获取一个常量进行赋值
可以这两个Widget是在同一层层级上的。
可以 继承于 InheritedWidget的类 包裹这两个Widget
然后两个widget可以获取上层的变量

main.dart

import 'dart:math';

import 'package:flutter/material.dart'; // runApp在这个material库里面

main() => runApp(MyApp());

class MyApp extends StatelessWidget {
    
    
  const MyApp({
    
    Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    
    
    return MaterialApp(
      home: YHHomePage(),
    );
  }
}

class YHHomePage extends StatelessWidget {
    
    
  const YHHomePage({
    
    Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    
    
    return Scaffold(
      appBar: AppBar(
        title: Text("InheritedWidget"),
      ),
    // 如果想共享状态 那么需要在最顶层包裹一个继承与 InheritedWidget widget即可
    body: YHCounterWidget(
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            YHShowData01(),
            YHShowData02(),
          ],
        ),
      ),
    ),
    );
  }
}

class YHShowData01 extends StatelessWidget {
    
    
  const YHShowData01({
    
    Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    
    
    int? counter = YHCounterWidget.of(context)?.counter; // 获取最顶层的counter
    return Card(
      color: Colors.red,
      child:Text("当前计数 : ${counter}",style: TextStyle(fontSize: 30)),
    );
  }
}
class YHShowData02 extends StatelessWidget {
    
    
  const YHShowData02({
    
    Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    
    
  int? counter = YHCounterWidget.of(context)?.counter; // 获取最顶层的counter
  return Container(
      color: Colors.blue,
      child:Text("当前计数 : ${counter}",style: TextStyle(fontSize: 30),),
    );
  }
}

// 共享状态管理
class YHCounterWidget extends InheritedWidget {
    
    

  final int counter = 100;
  static YHCounterWidget? of(BuildContext context)
  {
    
    
    // 沿着Element树,去找到最近的YHCounterElement,从Element中取出Widget对象
    return context.dependOnInheritedWidgetOfExactType();
  }

  YHCounterWidget({
    
    required Widget child}) : super(child: child);


  @override
  bool updateShouldNotify(covariant InheritedWidget oldWidget) {
    
    
      return true;
  }

}


3.1 效果图 - 共享状态管理InheritedWidget的使用

请添加图片描述

3.2 通过点击事件改变InheritedWidget内部的属性

如何让继承于 InheritedWidget 的类 通过外部点击按钮改变内部的值

  1. 可以在构造函数里面 添加参数
import 'dart:math';

import 'package:flutter/material.dart'; // runApp在这个material库里面

main() => runApp(MyApp());

class MyApp extends StatelessWidget {
    
    
  const MyApp({
    
    Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    
    
    return MaterialApp(
      home: YHHomePage(),
    );
  }
}

class YHHomePage extends StatefulWidget {
    
    

  const YHHomePage({
    
    Key? key}) : super(key: key);

  @override
  State<YHHomePage> createState() => _YHHomePageState();
}

class _YHHomePageState extends State<YHHomePage> {
    
    
  int _counter = 100;

  @override
  Widget build(BuildContext context) {
    
    
    return Scaffold(
      appBar: AppBar(
        title: Text("InheritedWidget"),
      ),
    // 如果想共享状态 那么需要在最顶层包裹一个继承与 InheritedWidget widget即可
    body: YHCounterWidget(
      counter:_counter,
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            YHShowData01(),
            YHShowData02(),
          ],
        ),
      ),
    ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: (){
    
    
          setState(() {
    
    
            _counter++;
          });
        },
      ),
    );
  }
}

class YHShowData01 extends StatelessWidget {
    
    
  const YHShowData01({
    
    Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    
    
    int? counter = YHCounterWidget.of(context)?.counter; // 获取最顶层的counter
    return Card(
      color: Colors.red,
      child:Text("当前计数 : ${counter}",style: TextStyle(fontSize: 30)),
    );
  }
}
class YHShowData02 extends StatelessWidget {
    
    
  const YHShowData02({
    
    Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    
    
  int? counter = YHCounterWidget.of(context)?.counter; // 获取最顶层的counter
  return Container(
      color: Colors.blue,
      child:Text("当前计数 : ${counter}",style: TextStyle(fontSize: 30),),
    );
  }
}

// 共享状态管理
// InheritedWidget的属性是不能够改变的
class YHCounterWidget extends InheritedWidget {
    
    

  final int counter;
  static YHCounterWidget? of(BuildContext context)
  {
    
    
    // 沿着Element树,去找到最近的YHCounterElement,从Element中取出Widget对象
    return context.dependOnInheritedWidgetOfExactType();
  }

  YHCounterWidget({
    
    required this.counter, required Widget child}) : super(child: child);


  @override
  bool updateShouldNotify(covariant InheritedWidget oldWidget) {
    
    
      return true;
  }

}


3.2 效果图-通过点击事件改变InheritedWidget内部的属性

请添加图片描述

4.updateShouldNotify的作用 更新时是否要通知

还记得我们之前说过的StatefulWidgetdidChangeDependencies函数吧
我们如果设置updateShouldNotify 为false的时候
StatefulWidgetdidChangeDependencies 不会执行的
我们如果设置updateShouldNotify 为true的时候
StatefulWidgetdidChangeDependencies 会执行的
我们可以根据旧的值是否发生改变来通知

import 'dart:math';

import 'package:flutter/material.dart'; // runApp在这个material库里面

main() => runApp(MyApp());

class MyApp extends StatelessWidget {
    
    
  const MyApp({
    
    Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    
    
    return MaterialApp(
      home: YHHomePage(),
    );
  }
}

class YHHomePage extends StatefulWidget {
    
    

  const YHHomePage({
    
    Key? key}) : super(key: key);

  @override
  State<YHHomePage> createState() => _YHHomePageState();
}

class _YHHomePageState extends State<YHHomePage> {
    
    
  int _counter = 100;

  @override
  Widget build(BuildContext context) {
    
    
    return Scaffold(
      appBar: AppBar(
        title: Text("InheritedWidget"),
      ),
    // 如果想共享状态 那么需要在最顶层包裹一个继承与 InheritedWidget widget即可
    body: YHCounterWidget(
      counter:_counter,
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            YHShowData01(),
            YHShowData02(),
          ],
        ),
      ),
    ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: (){
    
    
          setState(() {
    
    
            _counter++;
          });
        },
      ),
    );
  }
}

class YHShowData01 extends StatefulWidget {
    
    
  const YHShowData01({
    
    Key? key}) : super(key: key);

  @override
  State<YHShowData01> createState() => _YHShowData01State();
}

class _YHShowData01State extends State<YHShowData01> {
    
    

  @override
  void didChangeDependencies() {
    
    
    // TODO: implement didChangeDependencies
    super.didChangeDependencies();

    print("执行了 didChangeDependencies");
  }
  @override
  Widget build(BuildContext context) {
    
    
    int? counter = YHCounterWidget.of(context)?.counter; // 获取最顶层的counter
    return Card(
      color: Colors.red,
      child:Text("当前计数 : ${counter}",style: TextStyle(fontSize: 30)),
    );
  }
}
class YHShowData02 extends StatelessWidget {
    
    
  const YHShowData02({
    
    Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    
    
  int? counter = YHCounterWidget.of(context)?.counter; // 获取最顶层的counter
  return Container(
      color: Colors.blue,
      child:Text("当前计数 : ${counter}",style: TextStyle(fontSize: 30),),
    );
  }
}

// 共享状态管理
// InheritedWidget的属性是不能够改变的
class YHCounterWidget extends InheritedWidget {
    
    
  //  1.共享数据
  final int counter;
  // 2. 定义构造方法
  YHCounterWidget({
    
    required this.counter, required Widget child}) : super(child: child);

  // 3.获取组件最近的InheritedWidget
  static YHCounterWidget? of(BuildContext context)
  {
    
    
    // 沿着Element树,去找到最近的YHCounterElement,从Element中取出Widget对象
    return context.dependOnInheritedWidgetOfExactType();
  }
  // 4. 决定要不要回调State中的didChangeDependencies
  // 如果返回true:执行依赖当前的InheritedWidget的State中的didChangeDependencies
  @override
  bool updateShouldNotify(covariant YHCounterWidget oldWidget) {
    
    
      return oldWidget.counter != counter;
  }

}


②、Provider

Provider是目前官方推荐的全局状态管理工具,由社区作者Remi Rousselet 和 Flutter Team共同编写。
通过 pub.dev 第三方库进行获取安装指令
进入终端使用pub get 来安装
请添加图片描述

1.案例使用 - 在App最顶层包含Provider

void main (){
    
    
  runApp(
    //
      ChangeNotifierProvider(
        create: (cxt) =>YHCounterViewModel(),
        child:MyApp() ,)
  );

}

ViewModel

// viewmodel
import 'package:flutter/material.dart';

//  一般都继承于 ChangeNotifier
class YHCounterViewModel extends ChangeNotifier {
    
    
  int _counter = 100;

  int get counter => _counter;

  set counter(int value) {
    
    
    _counter = value;
    notifyListeners();// 通知所有的监听者 变量发生了改变
  }
}

1.1 使用 Provider.of 获取ViewModel的数据 会重新调用build

class YHShowData01 extends StatelessWidget {
    
    
  const YHShowData01({
    
    Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    
    
    int? counter = Provider.of<YHCounterViewModel>(context).counter; // 获取Provider的属性
    return Container(
      color: Colors.blue,
      child:Text("当前计数 : ${counter}",style: TextStyle(fontSize: 30),),
    );
  }
}

1.2.使用 Consumer<XXXXViewModel>(builder)获取ViewModel的数据 (开发常用) 不会重新调用build

class YHShowData02 extends StatelessWidget {
    
    
  const YHShowData02({
    
    Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    
    
    // int? counter = Provider.of<YHCounterViewModel>(context).counter; // 获取Provider的属性
    print("YHShowData02 build");

    return Container(
      color: Colors.red,
      child: Consumer<YHCounterViewModel>(
        builder: (ctx,counterVM,child){
    
    
          return Text("当前计数 : ${counterVM.counter}",style: TextStyle(fontSize: 30),);
        }
      ),
    );
  }
}

1.3 Consumer的build重新构建问题

不希望重新构建的部分放到builder。然后里面使用child进行包裹

      floatingActionButton: Consumer<YHCounterViewModel>(
        // 不希望重新构建的部分 可以放到Consumer 里面的 child里面
        child: Icon(Icons.add),
        builder: (cxt,counterVM,child){
    
    
          print("floatingActionButton build方法被执行了");
          return FloatingActionButton(onPressed: (){
    
    
            counterVM.counter+=1;
          },
            child: child,
          );
        },
      ),

1.4 不希望 floatingActionButton 重新构建 那么就不使用Consumer对象 使用Selector

 // Selector第二个参数 可以对第一个参数进行一个转换。一般很少用到
      // 所以直接 填写两个一样 。那么返回的第二个参数 就直接返回一样的就行
      // ShouldRebuild 是不是要重新构建的 返回false 就是不需要重新构建
      floatingActionButton: Selector<YHCounterViewModel,YHCounterViewModel>(
        selector: (ctx,counverVM) => counverVM,
        shouldRebuild: (prev,next) => false,
        // 不希望重新构建的部分 可以放到Consumer 里面的 child里面
        child: Icon(Icons.add),
        builder: (cxt,counterVM,child){
    
    
          print("floatingActionButton build方法被执行了");
          return FloatingActionButton(onPressed: (){
    
    
            counterVM.counter+=1;
          },
            child: child,
          );
        },
      ),

2.创建多个ViewModel和Provider建立联系

  1. 使用 MultiProvider绑定多个ViewModel
  2. Widget关联多个ViewModel 使用 Consumer2、Consumer3、Consumer4、Consumer5、Consumer6

UserInfo.dart

class UserInfo {
    
    
  String nickname = "";
  int level = 0;
  String ImageUrl = "";

  UserInfo(this.nickname,this.level,this.ImageUrl);
}

YHUserViewModel.dart

import 'package:flutter/material.dart';

import '../model/UserInfo.dart';

class YHUserViewModel extends ChangeNotifier {
    
    
  UserInfo _user;
  YHUserViewModel(this._user);

  UserInfo get user => _user;

  set user(UserInfo value) {
    
    
    _user = value;
    notifyListeners();
  }
}

YHCounterViewModel.dart

// viewmodel
import 'package:flutter/material.dart';

//  一般都继承于 ChangeNotifier
class YHCounterViewModel extends ChangeNotifier {
  int _counter = 100;

  int get counter => _counter;

  set counter(int value) {
    _counter = value;
    notifyListeners();// 通知所有的监听者 变量发生了改变
  }
}

initialize_providers.dart

初始化配置多个ViewModel

import 'package:learn_flutter/day10%E5%85%B1%E4%BA%AB%E7%8A%B6%E6%80%81/viewmodel/counter_view_model.dart';
import 'package:provider/provider.dart';
import 'package:provider/single_child_widget.dart';

import '../model/UserInfo.dart';
import 'user_view_model.dart';

List<SingleChildWidget> providers = [
  ChangeNotifierProvider(create:(ctx)=> YHCounterViewModel()),
  ChangeNotifierProvider(create:(ctx)=> YHUserViewModel(UserInfo("宇夜iOS",30,"url"))),
];

main.dart

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:learn_flutter/day10%E5%85%B1%E4%BA%AB%E7%8A%B6%E6%80%81/viewmodel/counter_view_model.dart';
import 'viewmodel/initialize_providers.dart';
import 'package:provider/provider.dart'; // runApp在这个material库里面
import 'viewmodel/user_view_model.dart';
import 'model/UserInfo.dart';

// main() => runApp(MyApp());
/**
 * 1.创建自己需要共享的数据
 * 2.在应用程序的顶层ChangeNotifierProvider
 * 3.在其它位置使用共享的数据
 *  > Provider.of: 当Provider中的数据发生改变时, Provider.of所在的Widget整个build方法都会重新构建
 *  > Consumer(相对推荐): 当Provider中的数据发生改变时, 执行重新执行Consumer的builder
 *  > Selector: 1.selector方法(作用,对原有的数据进行转换) 2.shouldRebuild(作用,要不要重新构建)
 */
void main (){
    
    
  // runApp(
  //   //
  //     ChangeNotifierProvider(
  //       create: (cxt) =>YHCounterViewModel(),
  //       child:MyApp()  ,
  //     )
  // );

  // runApp(
  //   //
  //     ChangeNotifierProvider(
  //       create: (cxt) =>YHCounterViewModel(),
  //       child:ChangeNotifierProvider(
  //         create: (cxt) => YHUserViewModel(UserInfo("宇夜iOS",30,"url")),
  //         child: MyApp(),
  //       )  ,
  //     )
  // );

  runApp(
    MultiProvider(providers: providers,
    child: MyApp(),
    )
  );

}

class MyApp extends StatelessWidget {
    
    
  const MyApp({
    
    Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    
    
    return MaterialApp(
      home: YHHomePage(),
    );
  }
}

class YHHomePage extends StatefulWidget {
    
    

  const YHHomePage({
    
    Key? key}) : super(key: key);

  @override
  State<YHHomePage> createState() => _YHHomePageState();
}

class _YHHomePageState extends State<YHHomePage> {
    
    
  int _counter = 100;

  @override
  Widget build(BuildContext context) {
    
    
    return Scaffold(
      appBar: AppBar(
        title: Text("InheritedWidget"),
      ),
      // 如果想共享状态 那么需要在最顶层包裹一个继承与 InheritedWidget widget即可
      body: YHCounterWidget(
        counter:_counter,
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              YHShowData01(),
              YHShowData02(),
              YHShowData03(),
            ],
          ),
        ),
      ),
      // Selector第二个参数 可以对第一个参数进行一个转换。一般很少用到
      // 所以直接 填写两个一样 。那么返回的第二个参数 就直接返回一样的就行
      // ShouldRebuild 是不是要重新构建的 返回false 就是不需要重新构建
      floatingActionButton: Selector<YHCounterViewModel,YHCounterViewModel>(
        selector: (ctx,counverVM) => counverVM,
        shouldRebuild: (prev,next) => false,
        // 不希望重新构建的部分 可以放到Consumer 里面的 child里面
        child: Icon(Icons.add),
        builder: (cxt,counterVM,child){
    
    
          print("floatingActionButton build方法被执行了");
          return FloatingActionButton(onPressed: (){
    
    
            counterVM.counter+=1;
          },
            child: child,
          );
        },
      ),
    );
  }
}

class YHShowData01 extends StatelessWidget {
    
    
  const YHShowData01({
    
    Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    
    
    int? counter = Provider.of<YHCounterViewModel>(context).counter; // 获取Provider的属性
    print("YHShowData01 build");
    return Container(
      color: Colors.blue,
      child:Text("当前计数 : ${counter}",style: TextStyle(fontSize: 30),),
    );
  }
}
class YHShowData02 extends StatelessWidget {
    
    
  const YHShowData02({
    
    Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    
    
    // int? counter = Provider.of<YHCounterViewModel>(context).counter; // 获取Provider的属性
    print("YHShowData02 build");

    return Container(
      color: Colors.red,
      child: Consumer<YHCounterViewModel>(
        builder: (ctx,counterVM,child){
    
    
          print("YHShowData02 Consumer build");

          return Text("当前计数 : ${counterVM.counter}",style: TextStyle(fontSize: 30),);
        }
      ),
    );
  }
}

class YHShowData03 extends StatelessWidget {
    
    

  @override
  Widget build(BuildContext context) {
    
    

    return Consumer2<YHUserViewModel,YHCounterViewModel>(
          builder: (ctx,userVM,counterVM,child){
    
    
            return Text("nickname : ${userVM.user.nickname} , counter :${counterVM.counter}",style: TextStyle(fontSize: 15),);
          },
      );
  }
}

// 共享状态管理
// InheritedWidget的属性是不能够改变的
class YHCounterWidget extends InheritedWidget {
    
    
  //  1.共享数据
  final int counter;
  // 2. 定义构造方法
  YHCounterWidget({
    
    required this.counter, required Widget child}) : super(child: child);

  // 3.获取组件最近的InheritedWidget
  static YHCounterWidget? of(BuildContext context)
  {
    
    
    // 沿着Element树,去找到最近的YHCounterElement,从Element中取出Widget对象
    return context.dependOnInheritedWidgetOfExactType();
  }
  // 4. 决定要不要回调State中的didChangeDependencies
  // 如果返回true:执行依赖当前的InheritedWidget的State中的didChangeDependencies
  @override
  bool updateShouldNotify(covariant YHCounterWidget oldWidget) {
    
    
    return oldWidget.counter != counter;
  }

}



效果图- Provider的综合案例

1.使用of,Consumer、Consumer2、Selector、
2. MultiProvider创建多个ViewModel

请添加图片描述

猜你喜欢

转载自blog.csdn.net/qq_42816425/article/details/123622438