You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter中不使用GetX/Provider,通过父StatefulWidget更新子组件值

解决方案:不使用状态管理库实现父组件更新子组件TextFormField

在Flutter中,不依赖GetX、Provider等状态管理库时,可通过GlobalKey直接获取子组件的State实例,调用其内部方法更新TextFormField的值,具体实现步骤如下:

1. 修改子组件,暴露更新方法

调整子组件的State类为公共可见(去掉类名前的下划线),并添加用于更新工资输入框的公共方法:

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:site_management/supervisors/supervisor_services/supervisor_attendance_services.dart';

class LabourAttendanceWidget extends StatefulWidget {
  const LabourAttendanceWidget({
    Key? key,
    required this.name,
    required this.wages,
    required this.isPrensent,
    required this.workerId,
    required this.masterAttendanceId,
  }) : super(key: key);
  final int workerId;
  final int masterAttendanceId;
  final String name;
  final String wages;
  final String isPrensent;

  @override
  LabourAttendanceWidgetState createState() => LabourAttendanceWidgetState();
}

// 去掉下划线,让State类可被父组件通过GlobalKey访问
class LabourAttendanceWidgetState extends State<LabourAttendanceWidget> {
  final TextEditingController _wagesController = TextEditingController();
  String _character = "";
  Timer? searchOnStoppedTyping;

  _onChangeHandler(value) {
    const duration = Duration(milliseconds: 800);
    if (searchOnStoppedTyping != null) {
      setState(() => searchOnStoppedTyping?.cancel());
    }
    setState(() =>
        searchOnStoppedTyping = Timer(duration, () => submitWages(value)));
  }

  submitWages(value) {
    SupervisorAttendanceServices.storeWorkerWages(
        widget.workerId, value, widget.masterAttendanceId);
  }

  // 公共方法:供父组件调用更新输入框值
  void updateWages(String newWages) {
    setState(() {
      _wagesController.text = newWages;
      _onChangeHandler(newWages); // 需要自动触发提交逻辑则保留,否则可移除
    });
  }

  @override
  void initState() {
    super.initState();
    _character = widget.isPrensent;
    _wagesController.text = widget.wages;
  }

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Column(children: [
        Row(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.start,
          children: [
            const SizedBox(width: 10, height: 50),
            const Icon(FeatherIcons.user),
            const SizedBox(width: 20),
            Text(widget.name, style: const TextStyle(fontSize: 18)),
          ],
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            SizedBox(
                width: 150,
                height: 60,
                child: TextFormField(
                  controller: _wagesController,
                  onChanged: _onChangeHandler,
                  decoration: const InputDecoration(
                      hintText: "Wages",
                      prefixIcon: Icon(Icons.wallet, color: Colors.blue)),
                )),
            Row(
              children: [
                Radio(
                  value: "P",
                  groupValue: _character,
                  fillColor: MaterialStateColor.resolveWith((states) => Colors.green),
                  onChanged: (selectedValue) {
                    setState(() {
                      _character = selectedValue.toString();
                      SupervisorAttendanceServices.changeAttendance(
                              widget.workerId, _character, widget.masterAttendanceId)
                          .then((response) {
                        if (response != 1) {
                          ScaffoldMessenger.of(context).showSnackBar(
                            SnackBar(
                              behavior: SnackBarBehavior.floating,
                              content: Row(
                                children: const [
                                  Icon(FeatherIcons.home),
                                  SizedBox(width: 10),
                                  Text("Something went wrong !"),
                                ],
                              ),
                            ),
                          );
                        }
                      });
                    });
                  },
                ),
                const Text("P"),
                Radio(
                    value: "A",
                    fillColor: MaterialStateColor.resolveWith((states) => Colors.red),
                    groupValue: _character,
                    onChanged: (selectedValue) {
                      setState(() {
                        _wagesController.text = "0";
                        _onChangeHandler("0");
                        _character = selectedValue.toString();
                        SupervisorAttendanceServices.changeAttendance(
                            widget.workerId, _character, widget.masterAttendanceId);
                      });
                    }),
                const Text("A"),
              ],
            )
          ],
        )
      ]),
    );
  }
}

2. 修改父组件,使用GlobalKey管理子组件

在父组件中创建GlobalKey列表绑定每个子组件,并添加触发更新的按钮:

// 父组件的State类中添加成员变量
List<GlobalKey<LabourAttendanceWidgetState>> _childKeys = [];

// 修改FutureBuilder部分
FutureBuilder(
  future: SupervisorAttendanceServices.getAttendancesDetailsList(widget.attendanceId),
  builder: (BuildContext context, AsyncSnapshot snapshot) {
    if (snapshot.hasData) {
      var data = snapshot.data['labour'];
      // 初始化子组件的GlobalKey列表
      _childKeys = List.generate(data.length, (index) => GlobalKey<LabourAttendanceWidgetState>());
      return Column(
        children: [
          // 添加触发更新的按钮
          ElevatedButton(
            onPressed: () {
              // 示例1:更新所有子组件的工资为"100"
              for (var key in _childKeys) {
                key.currentState?.updateWages("100");
              }

              // 示例2:更新特定worker的工资(根据workerId定位)
              // int targetWorkerId = 123; // 替换为目标workerId
              // int targetIndex = data.indexWhere((item) => item['worker_id'] == targetWorkerId);
              // if (targetIndex != -1) {
              //   _childKeys[targetIndex].currentState?.updateWages("150");
              // }
            },
            child: const Text("更新子组件工资"),
          ),
          Expanded(
            child: ListView.builder(
                itemCount: data.length,
                physics: const NeverScrollableScrollPhysics(),
                shrinkWrap: true,
                itemBuilder: (BuildContext context, int index) {
                  return LabourAttendanceWidget(
                    key: _childKeys[index], // 绑定GlobalKey
                    workerId: data[index]['worker_id'],
                    masterAttendanceId: widget.attendanceId,
                    name: data[index]['worker_name'],
                    wages: data[index]['attendance_worker_wages'],
                    isPrensent: data[index]['attendance_worker_presense'].toString(),
                  );
                }),
          ),
        ],
      );
    } else if (snapshot.hasError) {
      return const Center(child: Text("Something went wrong !"));
    } else {
      return const Center(child: LinearProgressIndicator());
    }
  },
),

核心原理说明

  • GlobalKey:跨Widget层级获取子组件的State实例,直接调用其内部公共方法。
  • 子组件暴露updateWages方法:父组件可直接控制TextFormField内容,同时保留原有输入提交逻辑。
  • 移除GetX依赖:删除原GetX控制器相关代码,避免依赖第三方状态管理库。

内容的提问来源于stack exchange,提问作者swapnil mane

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 16:45:44