Flutter 之 InheritedWidget

InheritedWidget 是 Flutter 框架中的一个重要类,用于在 Widget 树中共享数据。它是 Flutter 中数据传递和状态管理的基础之一。通过 InheritedWidget,你可以让子 Widget 在不需要显式传递数据的情况下,访问祖先 Widget 中的数据。这种机制对于跨多个层次的 Widget 共享状态非常有效。

目录

    • 一、 为什么会有 `InheritedWidget`?
    • 二、`InheritedWidget` 的基本工作原理
    • 三、`InheritedWidget` 的解决问题
    • 四、 `InheritedWidget` 的使用例子
    • 五、 `InheritedWidget` 和性能
    • 六、疑问
      • 1、InheritedWidget只能是父组件状态传递到子组件不能子组件传递到父组件吗
        • 1、父组件到子组件的数据传递
        • 2、为什么不支持子组件传递到父组件?
      • 2、子组件的数据如何传递给父组件
        • 1. 回调函数(Callback)
        • 2. 状态提升(Lift State Up)
        • 总结
      • 3、兄弟组件如何共享数据
        • 1. 通过父组件传递数据
        • 示例代码:
        • 2. 使用 `InheritedWidget` 或 `InheritedModel`
        • 使用 `InheritedWidget` 共享数据:
      • 3. 使用 `Provider` 包
    • 七、总结

一、 为什么会有 InheritedWidget

在 Flutter 中,Widget 树是单向的,意味着数据从上层(父 Widget)流向下层(子 Widget)。而 Flutter 中的 Widget 是不可变的,也就是说,你不能直接修改它们的状态。因此,当需要跨多个 Widget 层次传递状态或共享数据时,通常会遇到两类问题:

  • **属性传递:**如果需要共享的数据传递层级过多,父 Widget 必须将数据一层一层地传递给子 Widget,直到最需要这个数据的 Widget。这个过程会导致代码变得冗长,维护困难。
  • 数据共享: 在某些情况下,多个子 Widget 可能需要访问同一个数据源,这种情况下,直接通过构造函数传递数据显得非常不方便,尤其是在较深的 Widget 树中。

InheritedWidget 的出现正是为了解决这两个问题,它让 Widget 树中的任何子 Widget 可以便捷地访问到祖先 Widget 提供的数据,而不必显式地传递给每一个子 Widget。

二、InheritedWidget 的基本工作原理

InheritedWidget 是一个可以在 Widget 树中传播数据的 Widget。它通过实现 updateShouldNotify 方法来通知下层的 Widget,当数据发生变化时,这些 Widget 会重新构建(rebuild),从而获取新的数据。

  • InheritedWidget 发生变化时,它会通过 notifyClients 方法通知所有依赖于它的 Widget。
  • 在 Widget 树中的下层 Widget 可以通过 of 方法获取到祖先 Widget 提供的状态(数据)。

三、InheritedWidget 的解决问题

  • 跨越多层 Widget 树传递数据: 在 Widget 树的上层将数据放入 InheritedWidget 中后,所有的子 Widget 可以访问到该数据,而不需要通过构造函数一层一层地传递。
  • 避免重复构建:InheritedWidget 的数据发生变化时,只有依赖该数据的 Widget 会重新构建。其他不依赖该数据的 Widget 会保持不变,优化了性能。
  • 简化状态管理: InheritedWidget 是许多状态管理解决方案(如 ProviderRiverpod 等)的基础,提供了一个简单且高效的方式来管理和共享状态。

四、 InheritedWidget 的使用例子

import 'package:flutter/material.dart';// 创建一个自定义的 InheritedWidget
class MyInheritedWidget extends InheritedWidget {final int counter;MyInheritedWidget({required Widget child, required this.counter}) : super(child: child);// 判断是否需要通知下层 Widget 更新bool updateShouldNotify(MyInheritedWidget oldWidget) {return counter != oldWidget.counter;}// 提供获取数据的方法static MyInheritedWidget? of(BuildContext context) {return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();}
}// 一个使用 InheritedWidget 的子 Widget
class CounterDisplay extends StatelessWidget {Widget build(BuildContext context) {final inheritedWidget = MyInheritedWidget.of(context);return Text('Counter: ${inheritedWidget?.counter ?? 0}');}
}void main() {runApp(MyApp());
}class MyApp extends StatelessWidget {int counter = 0;Widget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text('InheritedWidget Example')),body: MyInheritedWidget(counter: counter,child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [CounterDisplay(),ElevatedButton(onPressed: () {counter++;// 由于 counter 变化,MyInheritedWidget 会通知子 Widget 更新},child: Text('Increment Counter'),),],),),),);}
}

在上面的代码中:

  • MyInheritedWidget 包含了一个 counter 变量,它可以被下层的子 Widget 访问。
  • CounterDisplay 使用 MyInheritedWidget.of(context) 来获取并显示 counter 的值。
  • 当按钮点击时,counter 值增加并重新构建整个 Widget 树。

五、 InheritedWidget 和性能

虽然 InheritedWidget 可以帮助我们轻松共享数据,但它也有一些限制,尤其是在性能上:

  • 仅依赖 InheritedWidget 的子 Widget 会重新构建,但这个重新构建的范围可能是你未预料到的。因此,要谨慎使用,避免不必要的性能损耗。
  • 对于复杂的应用,可能会更倾向于使用一些更强大的状态管理工具(如 ProviderRiverpod)来简化使用和提升性能。

六、疑问

1、InheritedWidget只能是父组件状态传递到子组件不能子组件传递到父组件吗

是的,InheritedWidget 的设计原则是从父组件向下传递数据,而不是从子组件向父组件传递数据。它主要用于共享 不可变的数据,或者是一些全局状态(如主题、语言、配置等),并使得在 InheritedWidget 树下的子 Widget 可以访问这些数据。

1、父组件到子组件的数据传递

InheritedWidget 允许父组件通过它传递数据到子组件。子组件通过 of 方法获取父组件(或祖先组件)中提供的状态或数据。例如,你可以通过一个 InheritedWidget 在树的上层传递一些数据(如计数器值),然后让树下的任何子组件获取该数据。

2、为什么不支持子组件传递到父组件?

InheritedWidget 是单向数据流的模型,它不支持反向传递(即子组件直接通知父组件更新)。这是因为 InheritedWidget 主要用于 数据共享,而不是 数据修改。其主要目的是提供一种高效的方式来向下共享数据,而不涉及子组件控制父组件的行为。

2、子组件的数据如何传递给父组件

要从子组件传递数据回父组件,通常有两种方式:

1. 回调函数(Callback)

子组件通过调用父组件传入的回调函数将数据或事件传递给父组件。父组件可以在回调函数中处理数据,并更新状态。

import 'package:flutter/material.dart';class ParentWidget extends StatefulWidget {_ParentWidgetState createState() => _ParentWidgetState();
}class _ParentWidgetState extends State<ParentWidget> {int counter = 0;void _incrementCounter() {setState(() {counter++;});}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('Parent to Child Example')),body: Column(children: [Text('Counter: $counter'),ChildWidget(onPressed: _incrementCounter), // 传递回调函数],),);}
}class ChildWidget extends StatelessWidget {final VoidCallback onPressed;  // 回调函数ChildWidget({required this.onPressed});Widget build(BuildContext context) {return ElevatedButton(onPressed: onPressed,  // 触发父组件传递的回调child: Text('Increment Counter'),);}
}void main() {runApp(MaterialApp(home: ParentWidget()));
}

在这个例子中:

  • ParentWidget 通过 onPressed 回调函数传递给 ChildWidget
  • ChildWidget 点击按钮时会调用 onPressed 回调,通知父组件更新 counter
2. 状态提升(Lift State Up)

如果需要共享和修改状态,可以将状态提升到最近的共同祖先组件,父组件和子组件都可以通过该祖先组件来管理和更新状态。通常,这种方式也与回调函数一起使用。

import 'package:flutter/material.dart';class ParentWidget extends StatefulWidget {_ParentWidgetState createState() => _ParentWidgetState();
}class _ParentWidgetState extends State<ParentWidget> {int counter = 0;void _updateCounter(int value) {setState(() {counter = value;});}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('State Lifting Example')),body: Column(children: [Text('Counter: $counter'),ChildWidget(onCounterChanged: _updateCounter),  // 状态提升],),);}
}class ChildWidget extends StatelessWidget {final Function(int) onCounterChanged; // 传递回调ChildWidget({required this.onCounterChanged});Widget build(BuildContext context) {return ElevatedButton(onPressed: () {onCounterChanged(5);  // 修改父组件的状态},child: Text('Update Counter to 5'),);}
}void main() {runApp(MaterialApp(home: ParentWidget()));
}
总结
  • InheritedWidget:主要用于将数据从父组件传递到子组件,子组件无法直接修改父组件的状态。
  • 回调函数状态提升:是从子组件向父组件传递数据或修改父组件状态的常用方法。

3、兄弟组件如何共享数据

在 Flutter 中,兄弟组件(即同一个父组件下的不同子组件)之间共享数据通常有几种方式。以下是几种常见的实现方法:

1. 通过父组件传递数据

最简单的方式是通过父组件来协调兄弟组件之间的通信。父组件将共享数据传递给它的子组件,然后子组件通过回调函数将数据发送回父组件,父组件再将数据传递给另一个子组件。这样可以保持数据流的单向性。

示例代码:
import 'package:flutter/material.dart';class ParentWidget extends StatefulWidget {_ParentWidgetState createState() => _ParentWidgetState();
}class _ParentWidgetState extends State<ParentWidget> {int sharedData = 0;// 更新数据并通知所有子组件void updateSharedData(int newValue) {setState(() {sharedData = newValue;});}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('Shared Data Between Siblings')),body: Column(children: [Text('Shared Data: $sharedData'),// 传递数据到子组件ChildWidget1(onDataChanged: updateSharedData),ChildWidget2(sharedData: sharedData),],),);}
}class ChildWidget1 extends StatelessWidget {final Function(int) onDataChanged;ChildWidget1({required this.onDataChanged});Widget build(BuildContext context) {return ElevatedButton(onPressed: () => onDataChanged(42), // 通过回调通知父组件更新数据child: Text('Update Data to 42'),);}
}class ChildWidget2 extends StatelessWidget {final int sharedData;ChildWidget2({required this.sharedData});Widget build(BuildContext context) {return Text('Child 2 sees Shared Data: $sharedData');}
}void main() {runApp(MaterialApp(home: ParentWidget()));
}

在这个例子中:

  • ParentWidget 维护了 sharedData,并通过 updateSharedData 方法更新它。
  • ChildWidget1 通过回调向父组件发送数据变更请求,父组件接收到数据后更新 sharedData
  • ChildWidget2 获取 sharedData,并显示它。
2. 使用 InheritedWidgetInheritedModel

如果你希望在多个兄弟组件之间共享数据,且不希望通过父组件直接传递数据,可以使用 InheritedWidgetInheritedModel 来共享数据。这两者能够在 Widget 树中向下传递数据,使得同一父级的多个子组件都可以访问这些数据。

使用 InheritedWidget 共享数据:
import 'package:flutter/material.dart';// 创建一个 InheritedWidget
class SharedData extends InheritedWidget {final int data;SharedData({required this.data, required Widget child}) : super(child: child);// 获取数据的静态方法static SharedData? of(BuildContext context) {return context.dependOnInheritedWidgetOfExactType<SharedData>();}bool updateShouldNotify(SharedData oldWidget) {return oldWidget.data != data;}
}class ParentWidget extends StatelessWidget {Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('Shared Data Using InheritedWidget')),body: SharedData(data: 100, // 共享数据child: Column(children: [ChildWidget1(),ChildWidget2(),],),),);}
}class ChildWidget1 extends StatelessWidget {Widget build(BuildContext context) {final sharedData = SharedData.of(context)?.data;return Text('Child 1 sees Shared Data: $sharedData');}
}class ChildWidget2 extends StatelessWidget {Widget build(BuildContext context) {final sharedData = SharedData.of(context)?.data;return Text('Child 2 sees Shared Data: $sharedData');}
}void main() {runApp(MaterialApp(home: ParentWidget()));
}

在这个例子中:

  • SharedData 继承自 InheritedWidget,用于在树下的多个子组件之间共享数据。
  • ChildWidget1ChildWidget2 都可以通过 SharedData.of(context) 获取共享的 data

3. 使用 Provider

如果需要跨多个页面或者复杂的共享数据,可以使用 Flutter 的状态管理框架,如 ProviderProvider 是一种比 InheritedWidget 更加简洁和灵活的解决方案。

七、总结

InheritedWidget 是 Flutter 中处理跨层次数据共享的基础工具。它能有效避免 “prop drilling” 问题,使得子 Widget 可以轻松访问祖先 Widget 的数据。它常常用作状态管理的基础,并且在 Flutter 中的许多第三方库和框架(如 Provider)中都有广泛应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/483600.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java调用ai模型:使用国产通义千问完成基于知识库的问答

整体介绍&#xff1a; 基于RAG&#xff08;Retrieval-Augmented Generation&#xff09;技术&#xff0c;可以实现一个高效的Java智能问答客服机器人。核心思路是将预先准备的问答QA文档&#xff08;例如Word格式文件&#xff09;导入系统&#xff0c;通过数据清洗、向量化处理…

Java 基于Spring AI RAG组件做AI智能问答_rag检索增强_AI智能问答

基于RAG技术构建高效Java智能问答客服机器人 基于RAG&#xff08;Retrieval-Augmented Generation&#xff09;技术&#xff0c;可以构建高效的Java智能问答客服机器人。首先&#xff0c;通过向量化处理将Word格式的问答QA文档转换为机器可理解的形式&#xff0c;并存储于Vect…

顶刊算法 | 鱼鹰算法OOA-BiTCN-BiGRU-Attention多输入单输出回归预测(Maltab)

顶刊算法 | 鱼鹰算法OOA-BiTCN-BiGRU-Attention多输入单输出回归预测&#xff08;Maltab&#xff09; 目录 顶刊算法 | 鱼鹰算法OOA-BiTCN-BiGRU-Attention多输入单输出回归预测&#xff08;Maltab&#xff09;效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实…

Redis+Caffeine 多级缓存数据一致性解决方案

RedisCaffeine 多级缓存数据一致性解决方案 背景 之前写过一篇文章RedisCaffeine 实现两级缓存实战&#xff0c;文章提到了两级缓存RedisCaffeine可以解决缓存雪等问题也可以提高接口的性能&#xff0c;但是可能会出现缓存一致性问题。如果数据频繁的变更&#xff0c;可能会导…

单片机学习笔记 12. 定时/计数器_定时

更多单片机学习笔记&#xff1a;单片机学习笔记 1. 点亮一个LED灯单片机学习笔记 2. LED灯闪烁单片机学习笔记 3. LED灯流水灯单片机学习笔记 4. 蜂鸣器滴~滴~滴~单片机学习笔记 5. 数码管静态显示单片机学习笔记 6. 数码管动态显示单片机学习笔记 7. 独立键盘单片机学习笔记 8…

6.824/6.5840(2024)环境配置wsl2+vscode

本文是经过笔者实践得出的最速の环境配置 首先&#xff0c;安装wsl2和vscode 具体步骤参见Mit6.s081环境配置踩坑之旅WSL2VScode_mit6s081-CSDN博客 接下来开始为Ubuntu(笔者使用的版本依然是20.04)配置go的相关环境 1、更新Ubuntu的软件包 sudo apt-get install build-es…

【排序用法】.NET开源 ORM 框架 SqlSugar 系列

&#x1f4a5; .NET开源 ORM 框架 SqlSugar 系列 &#x1f389;&#x1f389;&#x1f389; 【开篇】.NET开源 ORM 框架 SqlSugar 系列【入门必看】.NET开源 ORM 框架 SqlSugar 系列【实体配置】.NET开源 ORM 框架 SqlSugar 系列【Db First】.NET开源 ORM 框架 SqlSugar 系列…

「Mac畅玩鸿蒙与硬件38」UI互动应用篇15 - 猜数字增强版

本篇将带你实现一个升级版的数字猜谜游戏。相比基础版&#xff0c;新增了计分和历史记录功能&#xff0c;用户可以在每次猜测后查看自己的得分和猜测历史。此功能展示了状态管理的进阶用法以及如何保存和显示历史数据。 关键词 UI互动应用数字猜谜状态管理历史记录用户交互 一…

040集——CAD中放烟花(CAD—C#二次开发入门)

效果如下&#xff1a; 单一颜色的烟花&#xff1a; 渐变色的火花&#xff1a; namespace AcTools {public class HH{public static TransientManager tm TransientManager.CurrentTransientManager;public static Random rand new Random();public static Vector3D G new V…

【机器学习】分类任务: 二分类与多分类

二分类与多分类&#xff1a;概念与区别 二分类和多分类是分类任务的两种类型&#xff0c;区分的核心在于目标变量&#xff08;label&#xff09;的类别数&#xff1a; 二分类&#xff1a;目标变量 y 只有两个类别&#xff0c;通常记为 y∈{0,1} 或 y∈{−1,1}。 示例&#xff…

Python实现网站资源批量下载【可转成exe程序运行】

Python实现网站资源批量下载【可转成exe程序运行】 背景介绍解决方案转为exe可执行程序简单点说详细了解下 声明 背景介绍 发现 宣讲家网 的PPT很好&#xff0c;作为学习资料使用很有价值&#xff0c;所以想下载网站的PPT课件到本地&#xff0c;但是由于网站限制&#xff0c;一…

基于Matlab卡尔曼滤波的GPS/INS集成导航系统研究与实现

随着智能交通和无人驾驶技术的迅猛发展&#xff0c;精确可靠的导航系统已成为提升车辆定位精度与安全性的重要技术。全球定位系统&#xff08;GPS&#xff09;和惯性导航系统&#xff08;INS&#xff09;在导航应用中各具优势&#xff1a;GPS提供全球定位信息&#xff0c;而INS…

【计算机网络】实验6:IPV4地址的构造超网及IP数据报

实验 6&#xff1a;IPV4地址的构造超网及IP数据报 一、 实验目的 加深对IPV4地址的构造超网&#xff08;无分类编制&#xff09;的了解。 加深对IP数据包的发送和转发流程的了解。 二、 实验环境 • Cisco Packet Tracer 模拟器 三、 实验内容 1、了解IPV4地址的构造超网…

使用ESP32通过Arduino IDE点亮1.8寸TFT显示屏

开发板选择 本次使用开发板模块丝印为ESP32-WROOM-32E 开发板库选择 Arduino IDE上型号选择为ESP32-WROOM-DA Module 显示屏选择 使用显示屏为8针SPI接口显示屏 驱动IC为ST7735S 使用库 使用三个Arduino平台库 分别是 Adafruit_GFXAdafruit_ST7735SPI 代码详解 首…

[C++设计模式] 为什么需要设计模式?

文章目录 什么是设计模式&#xff1f;为什么需要设计模式&#xff1f;GOF 设计模式再次理解面向对象软件设计固有的复杂性软件设计复杂性的根本原因如何解决复杂性&#xff1f;分解抽象 结构化 VS 面向对象(封装)结构化设计代码示例&#xff1a;面向对象设计代码示例&#xff1…

机器学习:精确率与召回率的权衡

高精度意味着如果诊断得了那种罕见病的病人&#xff0c;可能病人确实有&#xff0c;这是一个准确的诊断&#xff0c;高召回率意味着如果有一个还有这种罕见疾病的病人&#xff0c;也许算法会正确的识别他们确实患有这种疾病&#xff0c;事实中&#xff0c;在精确与召回之间往往…

03-13、SpringCloud Alibaba第十三章,升级篇,服务降级、熔断和限流Sentinel

SpringCloud Alibaba第十三章&#xff0c;升级篇&#xff0c;服务降级、熔断和限流Sentinel 一、Sentinel概述 1、Sentinel是什么 随着微服务的流行&#xff0c;服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点&#xff0c;从流量控制、熔断降级、系统负载保…

基于vite6+ vue3 + electron@33 实现的 局域网内互传文件的桌面软件

目录 项目介绍项目部分截图介绍下基础项目搭建先搭建一个vite 前端项目 再安装 electron 相关依赖依赖安装失败解决方案修改 vite配置文件和 ts 配置文件修改packjsonts相关配置项目结构介绍 项目介绍 前端 基于 vue3 ts windicss 后端 就是node 层 项目地址&#xff1a; h…

安装MySQL 5.7 亲测有效

前言&#xff1a;本文是笔者在安装MySQL5.7时根据另一位博主大大的安装教程基础上做了一些修改而成 首先在这里表示对博主大大的感谢 下面附博主大大地址 下面的步骤言简意赅 跟着做就不会出错 希望各位读者耐下心来 慢慢解决安装中出现的问题~MySQL 5.7 安装教程&#xff08;全…

CSS函数

目录 一、背景 二、函数的概念 1. var()函数 2、calc()函数 三、总结 一、背景 今天我们就来说一说&#xff0c;常用的两个css自定义属性&#xff0c;也称为css函数。本文中就成为css函数。先来看一下官方对其的定义。 自定义属性&#xff08;有时候也被称作CSS 变量或者级…