一个欲儿的博客

一个欲儿的博客

Flutter 数据共享
2025-11-25

一、引言

在现代前端应用开发中,组件间的数据交互与状态管理是核心关注点。以用户登录场景为例,通常需要从输入控件中提取账号密码等表单数据。在传统 Web 前端技术栈中,这种数据绑定通常通过以下方式实现:

  • JavaScript:通过 document.getElementById() 或 document.getElementsByClassName() 等方法进行 DOM 元素绑定和数据操作

  • CSS:通过类选择器、标签选择器等方式对特定组件进行样式规则绑定
    然而,Flutter 作为现代化的跨平台 UI 框架,采用了一套截然不同的设计范式。其核心特性之一便是 InheritedWidget——一种基于 Widget 树的高效数据共享机制。该机制不仅实现了数据的跨组件传递,更在性能优化和状态管理方面提供了独特的解决方案。

与传统的选择器绑定模式不同,InheritedWidget 通过构建响应式的数据依赖关系,实现了数据的自动向下传播和局部更新,这为构建复杂交互界面提供了更加优雅和高效的实现路径。

二、简单实例

这边有个简单例子,可以体现,大概就是用户可以一直更新消息,按钮和文本都同时共享了消息属性

import 'package:flutter/material.dart';

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

// 1. 定义 InheritedWidget
class DataContainer extends InheritedWidget {
  final String message;
  final VoidCallback updateMessage;

  DataContainer({
    required this.message,
    required this.updateMessage,
    required Widget child,
  }) : super(child: child);

  static DataContainer of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<DataContainer>()!;
  }

  @override
  bool updateShouldNotify(DataContainer oldWidget) => message != oldWidget.message;
}

// 2. 主应用
class MyApp extends StatefulWidget {
  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String _message = 'Hello InheritedWidget!';

  void _updateMessage() {
    setState(() {
      _message = '消息已更新: ${DateTime.now().second}';
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: DataContainer(
        message: _message,
        updateMessage: _updateMessage,
        child: Scaffold(
          appBar: AppBar(title: Text('最简单的 InheritedWidget')),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                MessageDisplay(),  // 显示消息的组件
                SizedBox(height: 20),
                UpdateButton(),    // 更新按钮
              ],
            ),
          ),
        ),
      ),
    );
  }
}

// 3. 显示消息的组件
class MessageDisplay extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final message = DataContainer.of(context).message;
    return Text(message, style: TextStyle(fontSize: 20));
  }
}

// 4. 更新按钮
class UpdateButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final updateMessage = DataContainer.of(context).updateMessage;
    return ElevatedButton(
      onPressed: updateMessage,
      child: Text('更新消息'),
    );
  }
}

说一下原理,DataContainer通过继承InheritedWidget实现数据共享,共享的不仅仅是message这个变量,还有一个回调函数updateMessage,然后重写了一下构造器

企业微信截图_91765e34-b3a7-44ef-a97b-4efe35f6ab69.png

好,然后是封装两个需要和数据进行绑定的组件
首先是文本,通过DataContainer.of(context).message找到DataContainer中的message进行数据绑定,然后再把数据show出来
企业微信截图_c8ed6e10-3392-4985-9f40-c3d9b9dd875d.png

更新数据的按钮也是如此
企业微信截图_0224fc04-9b16-4d30-905a-26156e815485.png

最后是前端渲染的时候的书写方式
首先把需要共享的组件都放在DataContainer里面,例如这里就是刚才封装的文本和按钮,然后再把构造器需要的东西传过去,这里传了共享变量的初始值,以及更新变量初始值的方法
企业微信截图_cfb07267-a250-4c52-a63b-5b05df11c477.png

三、个人看法

Flutter数据共享的底层实现是树,所以最后要用DataContainer包裹住的组件才有资格访问共享的变量和回调这种,但是之前在组件封装的时候也绑定过一次,这里貌似是两次操作,其实并不是,这里只是因为我封装了,如果我不封装组件,那么我只需要将需要访问共享变量的放到DataContainer下面去就好了,Flutter的方式就是先获取树的根节点,树的根节点以及子节点都可以共享数据,这是一种方案,但是我个人感觉设计有几点不好
1.有些时候我也不知道哪些组件要数据共享
2.如果是隐式自动填充其实我觉得也可以
3.如果用观察者设计模式是不是更好就可以在某个组件的时候通过订阅从而获取数据
4.其实我觉得也可以有一套id体系,通过id进行数据绑定
hhhh都是个人看法,也许我的解决方案会遇到各种各样的问题,Flutter的官方开发者应该也发现了这个问题,所以这种方案应该是目前来看最好的


发表评论: