likes
comments
collection
share

Flutter 组件通信

作者站长头像
站长
· 阅读数 57

前言

flutter的状态管理有provider,riverpod,getx等方案,但是在开发中,存在着大量父子组件通信并不需要进行状态管理的情况。这里介绍下组件传值常用情况

1. 父传子

父组件通过传递参数,将值给子组件

//---------------------------- ParentWidget ----------------------------

class ParentWidget extends StatefulWidget {
  const ParentWidget({super.key});

  @override
  State<ParentWidget> createState() => _ParentWidgetCState();
}

class _ParentWidgetCState extends State<ParentWidget> {
  bool _active = false; //定义状态

  void _handleTapboxChanged() {
    setState(() {
      _active = !_active;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          InkWell(
            child: const Text('点击'),
            onTap: () {
              _handleTapboxChanged();
            },
          ),
          Tapbox(active: _active),//传递状态
        ],
      ),
    );
  }
}

//----------------------------- Tapbox ------------------------------

class Tapbox extends StatefulWidget {
  final bool active;
  const Tapbox({super.key, required this.active});

  @override
  State<Tapbox> createState() => _TapboxCState();
}

class _TapboxCState extends State<Tapbox> {
  @override
  Widget build(BuildContext context) {
    return Text(
      widget.active ? 'Active' : 'Inactive',
    );
  }
}

2. 子传父

父组件定义状态和改变状态的方法,并传递给子组件;子组件通过给方法传值来改变父组件的状态

//---------------------------- ParentWidget ----------------------------

class ParentWidget extends StatefulWidget {
  const ParentWidget({super.key});

  @override
  State<ParentWidget> createState() => _ParentWidgetCState();
}

class _ParentWidgetCState extends State<ParentWidget> {
  bool _active = false; //定义状态

  void _handleTapboxChanged(bool newValue) {
    //定义改变状态的方法
    setState(() {
      _active = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Tapbox( //传递状态和改变状态的方法
        active: _active,
        onChanged: _handleTapboxChanged,
      ),
    );
  }
}

//----------------------------- Tapbox ------------------------------

class Tapbox extends StatefulWidget {
  const Tapbox({super.key, required this.active, required this.onChanged});

  final bool active;
  final void Function(bool newValue) onChanged;

  @override
  State<Tapbox> createState() => _TapboxCState();
}

class _TapboxCState extends State<Tapbox> {
  void _handleTap() {
    widget.onChanged(!widget.active); //给方法传值
  }

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: _handleTap,
      child: Text(
        widget.active ? 'Active' : 'Inactive',
      ),
    );
  }
}

3. 父调用子

借助GlobalKey获取到子组件

//---------------------------- ParentWidget ----------------------------

GlobalKey<TapboxCState> childKey = GlobalKey(); //1. 创建 globalKey

class ParentWidget extends StatefulWidget {
  const ParentWidget({super.key});

  @override
  State<ParentWidget> createState() => _ParentWidgetCState();
}

class _ParentWidgetCState extends State<ParentWidget> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          Tapbox(
            key: childKey, // 2. 给子组件传递一个 globalKey
          ),
          InkWell(
            onTap: () {
              // 3. 通过 globalKey 的 currentState 获取方法  
              childKey.currentState
                  ?.handleTap(); 
            },
            child: const Text('点击'),
          )
        ],
      ),
    );
  }
}

//----------------------------- Tapbox ------------------------------

class Tapbox extends StatefulWidget {
  const Tapbox({super.key});

  @override
  State<Tapbox> createState() => TapboxCState();
}

class TapboxCState extends State<Tapbox> {
  void handleTap() {
    print('被调用');
  }

  @override
  Widget build(BuildContext context) {
    return const Text(
      '子组件',
    );
  }
}

4. 上下文传值(InheritedWidget)

  1. 新建persionShareDataWidget.dart
import 'package:flutter/material.dart';

class PersionShareDataWidget extends InheritedWidget {
  const PersionShareDataWidget({
    super.key,
    required this.data,
    required super.child,
  });

  final Person data; //需要在子树中共享的数据

  //提供一个 “of” 静态方法来获取其对象
  static PersionShareDataWidget? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<PersionShareDataWidget>();
  }

  //当data发生变化时,是否通知子树中依赖data的Widget重新build
  @override
  bool updateShouldNotify(PersionShareDataWidget oldWidget) {
    return oldWidget.data != data;
  }
}

class Person{
  String name;
  int age;
  Person({required this.name, required this.age});
}
  1. 使用
import 'package:flutter/material.dart';
import 'persionShareDataWidget.dart';

void main() {
  runApp(const MyApp());
}

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const GrandFather(),
    );
  }
}

class GrandFather extends StatefulWidget {
  const GrandFather({super.key});

  @override
  State<GrandFather> createState() => _GrandFatherState();
}

class _GrandFatherState extends State<GrandFather> {
  Person person = Person(name: '张三', age: 18);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: PersionShareDataWidget(
          data: person,
          child: Column(
            children: [
              const Text('祖先组件'),
              const Child(),
              ElevatedButton(
                child: const Text("Increment"),
                //更新数据
                onPressed: () => setState(() {
                  person = Person(name: '李四', age: 19);
                }),
              )
            ],
          )),
    );
  }
}

class Child extends StatefulWidget {
  const Child({super.key});

  @override
  State<Child> createState() => _ChildState();
}

class _ChildState extends State<Child> {
  @override
  Widget build(BuildContext context) {
    Person person = PersionShareDataWidget.of(context)!.data; //获取到上下文中的值
    return Text('${person.name.toString()}${person.age.toString()}');
  }
}

5. 事件总线

  1. 封装一个事件总线的方法,新建bus.dart
//订阅者回调签名
typedef void EventCallback(arg);

class EventBus {

  EventBus._internal();

  //保存单例
  static final EventBus _singleton = EventBus._internal();

  //工厂构造函数
  factory EventBus()=> _singleton;

  //保存事件订阅者队列,key:事件名(id),value: 对应事件的订阅者队列
  final _emap = <Object, List<EventCallback>?>{};

  //添加订阅者
  void on(eventName, EventCallback f) {
    _emap[eventName] ??=  <EventCallback>[];
    _emap[eventName]!.add(f);
  }

  //移除订阅者
  void off(eventName, [EventCallback? f]) {
    var list = _emap[eventName];
    if (eventName == null || list == null) return;
    if (f == null) {
      _emap[eventName] = null;
    } else {
      list.remove(f);
    }
  }

  //触发事件,事件触发后该事件所有订阅者会被调用
  void emit(eventName, [arg]) {
    var list = _emap[eventName];
    if (list == null) return;
    int len = list.length - 1;
    //反向遍历,防止订阅者在回调中移除自身带来的下标错位
    for (var i = len; i > -1; --i) {
      list[i](arg);
    }
  }
}

var bus = EventBus();

enum Event {
  login,
}
  1. 使用
import 'bus.dart';

//....

class Father extends StatefulWidget {
  const Father({super.key});

  @override
  State<Father> createState() => _FatherState();
}

class _FatherState extends State<Father> {
  int count = 0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          const Text('父组件'),
          ElevatedButton(
            child: const Text("Increment"),
            onPressed: () {
              setState(() => ++count);
              bus.emit(Event.login, count); //发布
            },
          ),
          const Child(),
        ],
      ),
    );
  }
}

class Child extends StatefulWidget {
  const Child({super.key});

  @override
  State<Child> createState() => _ChildState();
}

class _ChildState extends State<Child> {
  int count = 0;
  void onChange(value) {
    setState(() {
      count = value;
    });
  }

  @override
  void initState() {
    //订阅登录状态改变事件
    bus.on(Event.login, onChange);
    super.initState();
  }

  @override
  void dispose() {
    //取消订阅
    bus.off(Event.login, onChange);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Text('子组件$count');
  }
}

结尾

如果有不对或者需要补充的,欢迎在评论区留言

转载自:https://juejin.cn/post/7185070762343006264
评论
请登录