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探索如何关联

相关推荐
RichardLai887 小时前
[Flutter 进阶] - 掌握StatefulWidget的艺术
android·前端·flutter
依旧风轻9 小时前
ChangeNotifierProvider 本质上也是 Widget
flutter·ios·dart·widget·changenotifier·provider·sqi
马拉萨的春天21 小时前
flutter的widget的执行顺序,单个组建的执行顺序
flutter
怀君1 天前
Flutter——数据库Drift开发详细教程(七)
数据库·flutter
月伤591 天前
Flutter中将bytes转换成XFile对象上传
flutter
柿蒂1 天前
Flutter 拖动会比原生更省资源?分析 GPU呈现模式条形图不动的秘密
android·flutter
杉木笙1 天前
Flutter 代码雨实现(矩阵雨)DLC 多图层
前端·flutter
耳東陈1 天前
Flutter开箱即用一站式解决方案2.0-支持任意Widget的跑马灯
flutter
耳東陈1 天前
Flutter开箱即用一站式解决方案2.0-智能刷新列表
flutter