flutter 父子组件互相调用方法,值更新

文章目录

    • 父组件向子组件传值
    • [子组件调用父组件(传值 / 调方法)](#子组件调用父组件(传值 / 调方法))
    • [子组件是 StatefulWidget,父组件调用子组件方法](#子组件是 StatefulWidget,父组件调用子组件方法)
    • [通过回调主动监听(有状态子组件) 子组件监听父组件值更新](#通过回调主动监听(有状态子组件) 子组件监听父组件值更新)
    • 注意事项

父组件向子组件传值

父传子是最基础的传值方式,核心是子组件定义接收参数的变量,父组件在创建子组件时传入数据。

实现步骤

  • 子组件:通过构造函数定义需要接收的参数(推荐用 final 保证不可变);
  • 父组件:创建子组件时,通过构造函数传递数据
dart 复制代码
import 'package:flutter/material.dart';

// 子组件(接收父组件传递的值)
class ChildWidget extends StatelessWidget {
  // 1. 定义接收的参数(final + 构造函数必填)
  final String title;
  final int count;
  final Color bgColor;

  // 2. 构造函数(推荐用 required 标记必填参数)
  const ChildWidget({
    super.key,
    required this.title,
    required this.count,
    this.bgColor = Colors.blue, // 可选参数,设置默认值
  });

  @override
  Widget build(BuildContext context) {
    return Container(
      color: bgColor,
      padding: const EdgeInsets.all(16),
      child: Column(
        children: [
          Text("父组件传递的标题:$title"),
          Text("父组件传递的数字:$count"),
        ],
      ),
    );
  }
}

// 父组件(传递值给子组件)
class ParentWidget extends StatelessWidget {
  const ParentWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("父传子示例")),
      body: Center(
        // 3. 创建子组件时传递参数
        child: ChildWidget(
          title: "我是父组件给的标题",
          count: 99,
          bgColor: Colors.lightBlue, // 覆盖默认值
        ),
      ),
    );
  }
}

子组件调用父组件(传值 / 调方法)

子组件不能直接访问父组件的属性 / 方法,核心实现方式是父组件将函数(回调)传递给子组件,子组件调用该函数(本质是 "子触发父的逻辑")。

dart 复制代码
import 'package:flutter/material.dart';

// 子组件(调用父组件的回调函数传值)
class ChildWidget extends StatelessWidget {
  // 1. 接收父组件传递的回调函数
  final Function(String) onValueChanged;
  final Function() onButtonClick;

  const ChildWidget({
    super.key,
    required this.onValueChanged,
    required this.onButtonClick,
  });

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 点击按钮:调用父组件的回调,传递子组件的数值
        ElevatedButton(
          onPressed: () {
            // 2. 子组件主动调用父组件的函数,传递数据
            onValueChanged("我是子组件传递给父的值");
          },
          child: const Text("子组件传值给父"),
        ),
        ElevatedButton(
          onPressed: () {
            // 3. 子组件调用父组件的无参方法
            onButtonClick();
          },
          child: const Text("子组件调用父组件方法"),
        ),
      ],
    );
  }
}

// 父组件(定义回调函数,接收子组件的值/响应调用)
class ParentWidget extends StatefulWidget {
  const ParentWidget({super.key});

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

class _ParentWidgetState extends State<ParentWidget> {
  String _childData = "暂无数据";

  // 4. 父组件定义回调函数(处理子组件传递的值)
  void _handleChildValue(String value) {
    setState(() {
      _childData = value; // 更新父组件的状态
    });
    print("父组件收到子组件的值:$value");
  }

  // 5. 父组件定义被调用的方法
  void _parentMethod() {
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text("父组件的方法被子组件调用了!")),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("子调用父示例")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text("子组件传递的值:$_childData"),
            const SizedBox(height: 20),
            // 6. 将父组件的函数传递给子组件
            ChildWidget(
              onValueChanged: _handleChildValue,
              onButtonClick: _parentMethod,
            ),
          ],
        ),
      ),
    );
  }
}

子组件是 StatefulWidget,父组件调用子组件方法

如果需要父组件主动调用子组件的方法(比如子组件有一个 "重置" 方法,父组件按钮触发),需要用 GlobalKey 或 Callback 实现,这里讲最常用的 GlobalKey 方式:

dart 复制代码
import 'package:flutter/material.dart';

// 子组件(提供可被父组件调用的方法)
class ChildWidget extends StatefulWidget {
  // 1. 定义 GlobalKey,关联子组件的 State
  static final GlobalKey<_ChildWidgetState> childKey = GlobalKey<_ChildWidgetState>();

  const ChildWidget({super.key = childKey}); // 绑定Key

  @override
  State<ChildWidget> createState() => _ChildWidgetState();
}

class _ChildWidgetState extends State<ChildWidget> {
  int _childCount = 0;

  // 2. 子组件的公开方法(供父组件调用)
  void resetCount() {
    setState(() {
      _childCount = 0;
    });
    print("子组件的count被父组件重置了");
  }

  // 子组件的自增方法
  void incrementCount() {
    setState(() {
      _childCount++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text("子组件的count:$_childCount"),
        ElevatedButton(
          onPressed: incrementCount,
          child: const Text("子组件自增"),
        ),
      ],
    );
  }
}

// 父组件(调用子组件的方法)
class ParentWidget extends StatelessWidget {
  const ParentWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("父调用子示例")),
      body: Center(
        child: ColumnChildWidget
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const ChildWidget(), // 3. 创建子组件(已绑定Key)
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                // 4. 父组件通过 GlobalKey 调用子组件的方法
                ChildWidget.childKey.currentState?.resetCount();
              },
              child: const Text("父组件调用子组件的重置方法"),
            ),
          ],
        ),
      ),
    );
  }
}

void main() => runApp(const MaterialApp(home: ParentWidget()));

通过回调主动监听(有状态子组件) 子组件监听父组件值更新

dart 复制代码
import 'package:flutter/material.dart';

// 子组件:有状态,主动监听父组件参数变化
class ChildWidget extends StatefulWidget {
  final int parentCount;

  const ChildWidget({
    super.key,
    required this.parentCount,
  });

  @override
  State<ChildWidget> createState() => _ChildWidgetState();
}

class _ChildWidgetState extends State<ChildWidget> {
  int _localCount = 0; // 子组件本地状态

  // 核心生命周期:当父组件传递的参数变化时触发
  @override
  void didUpdateWidget(covariant ChildWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    // 对比新旧参数,确认值真的变化了(避免无意义的逻辑执行)
    if (widget.parentCount != oldWidget.parentCount) {
      print("子组件监听到父组件count变化:${oldWidget.parentCount} → ${widget.parentCount}");
      
      // 执行额外逻辑:比如更新本地状态、发起请求、播放动画等
      setState(() {
        _localCount = widget.parentCount * 2; // 子组件根据父值更新本地数据
      });
      
      // 其他逻辑:比如弹提示、调用方法等
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text("父组件count更新为:${widget.parentCount}")),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(16),
      color: Colors.lightGreen,
      child: Column(
        children: [
          Text("父组件count:${widget.parentCount}"),
          Text("子组件本地计算值:$_localCount"),
        ],
      ),
    );
  }
}

// 父组件:和基础示例一致,维护动态count
class ParentWidget extends StatefulWidget {
  const ParentWidget({super.key});

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

class _ParentWidgetState extends State<ParentWidget> {
  int _count = 0;

  void _incrementCount() {
    setState(() {
      _count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("主动监听父组件值更新")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ChildWidget(parentCount: _count),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: _incrementCount,
              child: const Text("增加父组件count"),
            ),
          ],
        ),
      ),
    );
  }
}

void main() => runApp(const MaterialApp(home: ParentWidget()));

核心逻辑

  • didUpdateWidget 是 StatefulWidget 的生命周期方法,仅在父组件传递的参数变化时触发;
  • 通过对比 oldWidget(旧参数)和 widget(新参数),可以精准判断哪些值发生了变化;
  • 在方法内可以执行任意自定义逻辑(更新本地状态、弹提示、调用方法等),实现 "主动监听"。

注意事项

  • 子调用父的核心逻辑:父组件传递 "函数" 给子组件,子组件执行该函数(可传参),本质是 "回调",而非子组件直接访问父组件;
  • GlobalKey 注意点:
    • 仅用于父调用子的场景,不要滥用(可能影响性能);
    • currentState 可能为 null,需加 ? 空安全判断;
  • 状态管理:如果组件层级较深(超过父子),不建议用回调 / GlobalKey,推荐用 Provider/GetX/Riverpod 等状态管理库;
  • 不可变原则:父传子的参数推荐用 final,如果需要修改,通过回调通知父组件更新状态,再重新传值给子组件(单向数据流)。
相关推荐
jay神1 小时前
基于 YOLOv8 的手势识别系统(Web 版)
前端·人工智能·python·深度学习·yolo·计算机视觉·计算机毕业设计
屏什么录1 小时前
长时间录屏中途崩溃:MP4 打不开的 6 种救回方案与参数设置
前端·音视频开发
code_Bo2 小时前
不只命令行:我把跨 Agent 会话同步做成了桌面应用
前端·javascript·后端
kyriewen2 小时前
AI 能把页面写出来,为什么改个需求就开始崩?
前端·javascript·人工智能
OpsEye2 小时前
同一套用例跑两个大模型接口,差异到底出在哪
javascript·ai编程
程序员老刘2 小时前
Flutter 3.50更新,开发者应该关注什么?
flutter·ai编程·客户端
X1A0RAN2 小时前
PPT 图片批量处理教程:导出全部幻灯片 · 提取全部素材
开发语言·javascript·powerpoint
涛涛ing3 小时前
2026年最值得关注的前端变化,藏在“默认值”里
前端
量化分析码农3 小时前
股票历史数据的复权参数对照:多级别返回的字段与数值差异
java·javascript·后端
亿元程序员5 小时前
太牛了!这AI做出来的游戏,居然还能变现!
前端