Flutter InheritedWidget 详解

InheritedWidget简介

用于在 widget 树中高效地向下传递数据,允许子组件获取最近Widget树中的InheritedWidget的数据

  1. 数据共享:在 widget 树中向下共享数据
  2. 高效更新:当数据变化时,只重建依赖该数据的子 widget
  3. 自动依赖管理:子 widget 自动"注册"为依赖项(dependOn)

示例

组件结构

子组件TextShowWidget 通过dependOnInheritedWidgetOfExactType()NameInheritedWidget关联,当InputNameWidget组件TextFiled输入变化的时,触发NameInheritedWidget变化,当updateShouldNotify返回true,从而通知和NameInheritedWidget关联的组件TextShowWidget变化

代码示例

TestInheritedWidget/AppChildInheritedWidget

在Flutter中Widget是不可变的,InheritedWidget也是一个Widget,所有要更新widget还是得构造一个StatfulWidget,通过setState()来更新, 在 AppChildInheritedWidget中当name发生变化,重新rebuild,来更新NameInheritedWidget数据, 但是此时的InputShowWidget是通过构造方法传入的,不会改变

scala 复制代码
class TestInheritedWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("TestInheritedWidget111");
    return Scaffold(
        appBar: AppBar(
          title: Text(
            "Inherited",
            style: TextStyle(fontSize: 28, color: Colors.white),
          ),
          backgroundColor: Color(0xFFE51BE5),
        ),
        body: AppChildInheritedWidget(child: InputShowWidget()));
  }
}

class AppChildInheritedWidget extends StatefulWidget {
  final Widget child;

  const AppChildInheritedWidget({required this.child, super.key});

  @override
  State<AppChildInheritedWidget> createState() => _AppChildInheritedWidgetState();
}

class _AppChildInheritedWidgetState extends State<AppChildInheritedWidget> {
  String? name;

  @override
  Widget build(BuildContext context) {
    return NameInheritedWidget(
      child: widget.child,
      name: name,
      onNameChange: (value) {
        setState(() {
          name = value;
        });
      },
    );
  }
}

NameInheritedWidget

dependOnInheritedWidgetOfExactType和 InheritedWidget 产生依赖关系,主动获取数据,当InheritedWidget数据变化时,关联的组件也会自动更新(build),

getInheritedWidgetOfExactType 单纯的获取InheritedWidget的数据,不产生关联,被动获取,

updateShouldNotify比较新旧widget数据是否一致,true触发更新

typescript 复制代码
class NameInheritedWidget extends InheritedWidget {
  final String? name;
  void Function(String? name) onNameChange;
  NameInheritedWidget({required super.child, this.name, required this.onNameChange});

  static String? of(BuildContext context) {
    return context
        .dependOnInheritedWidgetOfExactType<NameInheritedWidget>()!
        .name;
  }
  
  static String? maybe(BuildContext context) {
    return context
        .dependOnInheritedWidgetOfExactType<NameInheritedWidget>()
        ?.name;
  }

  static NameInheritedWidget? getNameInheritedWidget(BuildContext context) {
    return context.getInheritedWidgetOfExactType<NameInheritedWidget>();
  }

  @override
  bool updateShouldNotify(covariant NameInheritedWidget oldWidget) {
    return oldWidget.name != this.name;
  }
}

InputShowWidget

scala 复制代码
class InputShowWidget extends StatelessWidget {
  const InputShowWidget({super.key});

  @override
  Widget build(BuildContext context) {
    print("InputShowWidget222");
    return Column(
      children: [
        Expanded(flex: 2, child: InputNameWidget()),
        Expanded(flex: 1, child: TextShowWidget())
      ],
    );
  }
}

class TextShowWidget extends StatelessWidget {
  const TextShowWidget({super.key});

  @override
  Widget build(BuildContext context) {
    print("TextShowWidget");
    var name = NameInheritedWidget.maybe(context) ?? "未知输入";
    return Padding(
      padding: const EdgeInsets.all(20.0),
      child: Center(
          child: Text(
        name,
        style: TextStyle(
            fontSize: 20,
            fontWeight: FontWeight.w600,
            color: Colors.deepOrangeAccent),
      )),
    );
  }
}

InputNameWidget/TextShowWidget

less 复制代码
class InputNameWidget extends StatefulWidget {
  const InputNameWidget({super.key});

  @override
  State<InputNameWidget> createState() => _InputNameWidgetState();
}

class _InputNameWidgetState extends State<InputNameWidget> {
  TextEditingController _textEditingController = TextEditingController(text: "爱好");
  
  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
    _textEditingController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    print("InputNameWidget333");
    return LayoutBuilder(builder: (context, constraints) {
      return SingleChildScrollView(
        child: ConstrainedBox(
          constraints: BoxConstraints(minHeight: constraints.maxHeight),
          child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 20),
            child: Column(
              spacing: 20,
              crossAxisAlignment: CrossAxisAlignment.stretch,
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                TextField(
                  style: TextStyle(color: Colors.pink),
                  controller: _textEditingController,
                  onChanged: (value){
                    print(value);
                    NameInheritedWidget.getNameInheritedWidget(context)
                        ?.onNameChange(value);
                  },
                  decoration: InputDecoration(
                      focusColor: Colors.red,
                      hintText: "请输入",
                      hintStyle: TextStyle(color: Colors.blueAccent),
                      border: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(12),
                          borderSide: BorderSide.none),
                      focusedBorder: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(12),
                          borderSide: BorderSide(color: Colors.black)),
                      filled: true,
                      fillColor: Color(0xE5BBBBC4)),
                ),
                SizedBox(height: 20,),
                FilledButton(
                    style: ButtonStyle(
                        backgroundColor: WidgetStatePropertyAll(Colors.black),
                        padding: WidgetStatePropertyAll(
                            EdgeInsets.symmetric(vertical: 18)),
                        shape: WidgetStatePropertyAll(RoundedRectangleBorder(
                            borderRadius: BorderRadius.circular(12)))),
                    onPressed: () {
                      NameInheritedWidget.getNameInheritedWidget(context)
                          ?.onNameChange(_textEditingController.text);
                    },
                    child: Text("点击"))
              ],
            ),
          ),
        ),
      );
    });
  }
}
scala 复制代码
class TextShowWidget extends StatelessWidget {
  const TextShowWidget({super.key});

  @override
  Widget build(BuildContext context) {
    print("TextShowWidget");
    var name = NameInheritedWidget.maybe(context) ?? "未知输入";
    return Padding(
      padding: const EdgeInsets.all(20.0),
      child: Center(
          child: Text(
        name,
        style: TextStyle(
            fontSize: 20,
            fontWeight: FontWeight.w600,
            color: Colors.deepOrangeAccent),
      )),
    );
  }
}

日志

首次进入所有Widget的build方法全部执行

TextField发生变化,触发底部文字变化

dependOnInheritedWidgetOfExactType探索如何关联

相关推荐
weixin_411191844 小时前
原生安卓与flutter混编的实现
android·flutter
会煮咖啡的猫12 小时前
编写 Flutter 游戏摇杆组件
flutter
来来走走12 小时前
Flutter dart运算符
android·前端·flutter
风清云淡_A12 小时前
【Flutter3.8x】flutter从入门到实战基础教程(五):Material Icons图标的使用
前端·flutter
阳光明媚sunny14 小时前
Flutter基础知识
flutter
新镜14 小时前
【Flutter】双路视频播放方案
flutter·音视频
GeniuswongAir14 小时前
flutter分享到支付宝
flutter
用户736004375561 天前
【Flutter 必备插件】HTTP 封装 dio
flutter
风清云淡_A2 天前
【Flutter3.8x】flutter从入门到实战基础教程(四):自定义实现一个自增的StatefulWidget组件
前端·flutter
叽哥2 天前
dart学习第1节: 变量与数据类型 —— 程序的 “基本元素”
flutter