如何实现NotificationInput与NotificationController的双向绑定机制?
实现NotificationInput与NotificationController的双向绑定
背景
TextField和TextEditingController天然支持双向绑定:修改输入框内容会同步更新控制器,修改控制器也会同步更新输入框。现在需要基于给定的NotificationController和NotificationInput代码,实现同样的双向绑定效果,同时避免UI与控制器互相通知导致的循环死锁。
现有基础代码
class NotificationValue { final String subject; final String message; const NotificationValue({ required this.subject, required this.message, }); NotificationValue copyWith({ String? subject, String? message, }) { return NotificationValue( subject: subject ?? this.subject, message: message ?? this.message, ); } } class NotificationController extends ValueNotifier<NotificationValue> { NotificationController({ String subject = '', String message = '', }) : super(NotificationValue( subject: subject, message: message, )); String get subject => value.subject; String get message => value.message; set subject(String nextValue) { value = value.copyWith( subject: nextValue, ); } set message(String nextValue) { value = value.copyWith( message: nextValue, ); } } class NotificationInput extends StatefulWidget { const NotificationInput({ super.key, required this.controller, }); final NotificationController controller; @override State<NotificationInput> createState() => _NotificationInputState(); } class _NotificationInputState extends State<NotificationInput> { final subjectController = TextEditingController(); final messageController = TextEditingController(); @override void initState() { subjectController.text = widget.controller.subject; messageController.text = widget.controller.message; super.initState(); } @override void dispose() { subjectController.dispose(); messageController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Column( children: [ TextField(controller: subjectController), TextField(controller: messageController), ], ); } }
解决方案
要实现双向绑定,需要完成两个方向的同步,并通过值校验避免循环死锁,以下是修改后的完整代码:
class NotificationValue { final String subject; final String message; const NotificationValue({ required this.subject, required this.message, }); NotificationValue copyWith({ String? subject, String? message, }) { return NotificationValue( subject: subject ?? this.subject, message: message ?? this.message, ); } } class NotificationController extends ValueNotifier<NotificationValue> { NotificationController({ String subject = '', String message = '', }) : super(NotificationValue( subject: subject, message: message, )); String get subject => value.subject; String get message => value.message; set subject(String nextValue) { // 仅当值变化时才更新,避免无意义的通知触发 if (nextValue != value.subject) { value = value.copyWith(subject: nextValue); } } set message(String nextValue) { if (nextValue != value.message) { value = value.copyWith(message: nextValue); } } } class NotificationInput extends StatefulWidget { const NotificationInput({ super.key, required this.controller, }); final NotificationController controller; @override State<NotificationInput> createState() => _NotificationInputState(); } class _NotificationInputState extends State<NotificationInput> { final subjectController = TextEditingController(); final messageController = TextEditingController(); late VoidCallback _controllerListener; @override void initState() { super.initState(); // 初始化输入框与控制器同步 _syncControllerToInput(); // 注册控制器值变化监听 _controllerListener = () => _syncControllerToInput(); widget.controller.addListener(_controllerListener); } @override void dispose() { // 移除监听,防止内存泄漏 widget.controller.removeListener(_controllerListener); subjectController.dispose(); messageController.dispose(); super.dispose(); } // 将控制器的值同步到输入框 void _syncControllerToInput() { // 仅当值不同时才更新输入框,避免循环触发 if (subjectController.text != widget.controller.subject) { subjectController.text = widget.controller.subject; } if (messageController.text != widget.controller.message) { messageController.text = widget.controller.message; } } @override Widget build(BuildContext context) { return Column( children: [ TextField( controller: subjectController, // 输入框变化时同步到控制器 onChanged: (value) => widget.controller.subject = value, ), TextField( controller: messageController, onChanged: (value) => widget.controller.message = value, ), ], ); } }
核心逻辑说明
- 输入框→控制器同步:通过
TextField的onChanged回调,将输入内容同步到NotificationController,控制器内部会先判断值是否变化,只有不同时才更新并触发通知。 - 控制器→输入框同步:在
initState中注册控制器的监听,当控制器值变化时,调用_syncControllerToInput方法同步到输入框,同样会先做值校验,避免无意义的更新。 - 避免循环死锁:双向同步的每一步都添加了值判断,确保只有实际值变化时才触发操作,彻底切断了UI与控制器互相通知的循环链路。
内容的提问来源于stack exchange,提问作者XUNIT
相关产品推荐
相关产品推荐

