百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

Flutter状态管理之Provider数据共享的底层实现

wptr33 2025-04-26 21:40 29 浏览

#头条创作挑战赛#

简介

高级的Flutter工程师往往都是从最简单的架构开始研究学习,今天给大家带来的是Flutter中比较重要的状态管理框架Provider,也是每位Flutter开发者必学的框架。Provider功能比较强大,能够轻松实现一键换肤和语言切换等功能。本篇文章手把手教大家实现一个最简单的Provider框架,还不会的同学赶紧收藏起来慢慢学。

框架结构图


原理分析

在讲解Provider之前,读者需要了解Flutter原生提供的InheritedWidgetChangeNotifier的作用才能真正理解Provider的共享数据机制。

InheritedWidget的作用

InheritedWidget是Flutter提供的一种重要的Widget组件,它提供了在Widget树中从上到下共享数据的能力,当InheritedWidget作为父Widget时,其内部的子Widget都能访问到父Widget中保存的数据。


示例如下:

  1. 定义一个ShareWidget继承自InheritedWidget,其中定义一个data用来保存共享数据。其ShareWidget代码如下:
class ShareWidget extends InheritedWidget{
  const ShareWidget({super.key, required super.child,required this.data});
  final int data; //在子树中共享的数据

  
 //定义静态方法,数据共享给子Widget使用
  static ShareWidget? of(BuildContext context){
  //调用子控件的didChangeDependencies和rebuild方法
    return context.dependOnInheritedWidgetOfExactType<ShareWidget>();
  }
  //当回调决定当data发生变化时,通知子widget中使用date数据的widget执行build构建。
  @override
  bool updateShouldNotify(covariant ShareWidget oldWidget) {
    //当新旧数据发生变化时,通知子Widget重新构建
    return oldWidget.data != data;
  }

}
  1. 子Widget通过ShareWidget.of方法访问ShareWidget中的共享数据data。当data数据发生改变时,会通知子Widget重新构建,内部调用了子Widget的setState(){}方法。

ChangeNotifier的作用

相信各位同学对Android中的EventBus都很熟悉这种订阅者模式,Flutter中为我们提供了ChangeNotifier实现订阅者模式。只需要继承ChangeNotifier就能实现数据的监听。我们来看一下ChangeNotifier内部源码就明白了。

class ChangeNotifier implements Listenable {
  List listeners=[];
  @override
  void addListener(VoidCallback listener) {
     //添加监听器
     listeners.add(listener);
  }
  @override
  void removeListener(VoidCallback listener) {
    //移除监听器
    listeners.remove(listener);
  }
  
  void notifyListeners() {
    //通知所有监听器,触发监听器回调 
    listeners.forEach((item)=>item());
  }
....
}

ChangeNotifier继承自Listenable,内部提供了addListener,removeListener,notifyListeners三个方法。这个是典型的订阅者模式。

Provider的代码实现

  1. 定义InheritedProvider类继承于InheritedWidget。这里传的是泛型。便于共享所有数据类型。
class InheritedProvider<T> extends InheritedWidget{
  const InheritedProvider({super.key, required Widget child,
    required this.data}):super(child: child);
  ///需要共享的数据
  final T data;

  @override
  bool updateShouldNotify(covariant InheritedProvider<T> oldWidget) {
   //数据变化时通知子widget重建
   return oldWidget.data != data;
  }

}
  1. 定义ShopModel继承ChangeNotifier,用来提供需要共享的数据。这里定义int类型的集合。当添加完数据之后调用notifyListeners(),通知所有的订阅者更新数据。
class ShopModel extends ChangeNotifier{
  final List<int> _items = [];
  List<int> get items =>_items;
  int get totalCount =>_items.fold<int>(0, (value, element)=>value+element);
  ///添加数据后,通知所有订阅者
  void addShop(int num){
    _items.add(num);
    notifyListeners();
  }

}
  1. 自定义ChangeNotifierProvider继承于StatefulWidget,由于ChangeNotifierProvider内部涉及到widget的重建,所以需要继承有状态的StatefulWidget。
class ChangeNotifierProvider<T extends ChangeNotifier> extends StatefulWidget{
  const ChangeNotifierProvider({super.key, required this.data, required this.child});
  final Widget child;
  final T data;


  static T of<T>(BuildContext context){
    var provider =context.dependOnInheritedWidgetOfExactType<InheritedProvider<T>>();
    return provider!.data;
  }
   @override
  State<ChangeNotifierProvider<T>> createState() =>_ChangeNotifierProviderState<T>();
}

在ChangeNotifierProvider中提供了静态方法of用来提供给子Widget获取共享数据 T,这里的T指的的ShopModel。

class _ChangeNotifierProviderState<T extends ChangeNotifier> extends State<ChangeNotifierProvider<T>>{
  ///更新重新构建wiget
  void update(){
    setState(() {
    });
  }
  @override
  void didUpdateWidget(covariant ChangeNotifierProvider<T> oldWidget) {
    if(oldWidget.data != widget.data){
       oldWidget.data.removeListener(update);
       widget.data.addListener(update);
     }
    super.didUpdateWidget(oldWidget);
  }
  @override
  void initState() {
    ///注册监听
    widget.data.addListener(update);
    super.initState();
  }
  @override
  void dispose() {
     ///移除监听
    widget.data.removeListener(update);
    super.dispose();
  }
  @override
  Widget build(BuildContext context) {
    ///这里使用InheritedProvider作为父widget,共享数据给子widget
    return InheritedProvider<T>(data: widget.data, child: widget.child);
  }

}


_ChangeNotifierProviderState注册了data的数据监听,本文指的是ShopModel,当ShopModel发生变化时,通过调用ShopModel中的notifyListeners()方法,通知所有的监听者。调用
_ChangeNotifierProviderState中的update方法,update方法调用了setState(){},调用当前Widget的rebuild方法发生重新构建。
_ChangeNotifierProviderState使用InheritedProvider作为父widget是为了共享数据data也就是ShopModel给子Widget。

  1. 我们来看一下最后的使用。
ChangeNotifierProvider<CarModel>(
  data: ShopModel(),
  child: Builder(builder: (context) {
      return Center(child: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          Builder(builder: (context) {
            var cart =
              ChangeNotifierProvider.of<ShopModel>(context);
            return Text("计算结果:${cart.totalCount}");
          }),
          const SizedBox(
            height: 40,
          ),
          ElevatedButton(
            onPressed: () {
                ChangeNotifierProvider.of<CarModel>(context)
                  .addShop(10);
              },
            child: const Text('增加数字'))
        ],
      ),);
    }))

当我们点击增加数字按钮,通过ChangeNotifierProvider.of<ShopModel>(context)返回的ShopModel,调用ShopModel的addShop方法,addShop又调用了notifyListeners,通监听者ChangeNotifierProvider重新构建,显示最新的数据。

总结

InheritedWidget,ChangeNotifier,Provider在Flutter中的使用非常广泛,也比较重要,也是作为Flutter开发者必须要学会使用的组件,除了这些能实现共享数据,还有Bloc等第三方优秀框架需要读者自己研究学习,理解框架实现的思路,学习框架的搭建。本文学习的重点不是写代码,而是根据现有的组件去实现一款优秀的框架,这也是小编所学习追求的。

相关推荐

MySQL进阶五之自动读写分离mysql-proxy

自动读写分离目前,大量现网用户的业务场景中存在读多写少、业务负载无法预测等情况,在有大量读请求的应用场景下,单个实例可能无法承受读取压力,甚至会对业务产生影响。为了实现读取能力的弹性扩展,分担数据库压...

Postgres vs MySQL_vs2022连接mysql数据库

...

3分钟短文 | Laravel SQL筛选两个日期之间的记录,怎么写?

引言今天说一个细分的需求,在模型中,或者使用laravel提供的EloquentORM功能,构造查询语句时,返回位于两个指定的日期之间的条目。应该怎么写?本文通过几个例子,为大家梳理一下。学习时...

一文由浅入深带你完全掌握MySQL的锁机制原理与应用

本文将跟大家聊聊InnoDB的锁。本文比较长,包括一条SQL是如何加锁的,一些加锁规则、如何分析和解决死锁问题等内容,建议耐心读完,肯定对大家有帮助的。为什么需要加锁呢?...

验证Mysql中联合索引的最左匹配原则

后端面试中一定是必问mysql的,在以往的面试中好几个面试官都反馈我Mysql基础不行,今天来着重复习一下自己的弱点知识。在Mysql调优中索引优化又是非常重要的方法,不管公司的大小只要后端项目中用到...

MySQL索引解析(联合索引/最左前缀/覆盖索引/索引下推)

目录1.索引基础...

你会看 MySQL 的执行计划(EXPLAIN)吗?

SQL执行太慢怎么办?我们通常会使用EXPLAIN命令来查看SQL的执行计划,然后根据执行计划找出问题所在并进行优化。用法简介...

MySQL 从入门到精通(四)之索引结构

索引概述索引(index),是帮助MySQL高效获取数据的数据结构(有序),在数据之外,数据库系统还维护者满足特定查询算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构...

mysql总结——面试中最常问到的知识点

mysql作为开源数据库中的榜一大哥,一直是面试官们考察的重中之重。今天,我们来总结一下mysql的知识点,供大家复习参照,看完这些知识点,再加上一些边角细节,基本上能够应付大多mysql相关面试了(...

mysql总结——面试中最常问到的知识点(2)

首先我们回顾一下上篇内容,主要复习了索引,事务,锁,以及SQL优化的工具。本篇文章接着写后面的内容。性能优化索引优化,SQL中索引的相关优化主要有以下几个方面:最好是全匹配。如果是联合索引的话,遵循最...

MySQL基础全知全解!超详细无废话!轻松上手~

本期内容提醒:全篇2300+字,篇幅较长,可搭配饭菜一同“食”用,全篇无废话(除了这句),干货满满,可收藏供后期反复观看。注:MySQL中语法不区分大小写,本篇中...

深入剖析 MySQL 中的锁机制原理_mysql 锁详解

在互联网软件开发领域,MySQL作为一款广泛应用的关系型数据库管理系统,其锁机制在保障数据一致性和实现并发控制方面扮演着举足轻重的角色。对于互联网软件开发人员而言,深入理解MySQL的锁机制原理...

Java 与 MySQL 性能优化:MySQL分区表设计与性能优化全解析

引言在数据库管理领域,随着数据量的不断增长,如何高效地管理和操作数据成为了一个关键问题。MySQL分区表作为一种有效的数据管理技术,能够将大型表划分为多个更小、更易管理的分区,从而提升数据库的性能和可...

MySQL基础篇:DQL数据查询操作_mysql 查

一、基础查询DQL基础查询语法SELECT字段列表FROM表名列表WHERE条件列表GROUPBY分组字段列表HAVING分组后条件列表ORDERBY排序字段列表LIMIT...

MySql:索引的基本使用_mysql索引的使用和原理

一、索引基础概念1.什么是索引?索引是数据库表的特殊数据结构(通常是B+树),用于...