Flutter自定义组件状态问题:父/内部更新异常、TouchPoint交互故障排查
本篇内容较长,我暂未定位到问题根源,也不清楚该如何处理。
更新说明:若改用StatelessWidget,数值可以正常同步,因此……
设计目标
我设计的TouchPoint核心是一个按钮,点击拖拽时会将事件向上传递给可滚动容器;长按则进入编辑模式,可处理多类数据;点击弹窗确认按钮会调用父组件的OnPressed方法回传新值,点击取消则不触发onPressed也不更新标签。
遇到的问题
打开对话框后无法正常交互:我在控件上添加了打印日志,能捕获到点击事件,但对话框内的UI控件未同步最新状态。
无法实现按钮的点击拖拽功能
实现代码
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'statusword_t.dart'; enum InputType { boolean, text, numeric, outconfig, sword } class TouchPoint extends StatelessWidget { TouchPoint({ Key? key, required this.title, required this.icon, required this.value, required this.enabled, required this.type, this.onlyButtons = false, this.warning, this.setter, }) : super(key: key); final String title; final String value; final Icon icon; final bool enabled; final InputType type; final bool onlyButtons; final Function(String)? setter; final String? warning; /*@override _TouchPointState createState() => _TouchPointState(); */ @override Widget build(BuildContext context) { return ElevatedButton( // 这是页面显示的按钮 style: ButtonStyle( backgroundColor: MaterialStateProperty.all<Color>( enabled ? Colors.blue : Colors.grey), foregroundColor: MaterialStateProperty.all(Colors.white)), child: Row( mainAxisAlignment: MainAxisAlignment.start, // TODO: 要不要居中? crossAxisAlignment: CrossAxisAlignment.center, children: [ icon, Column( // TODO: 这里的对齐设置好像不生效 mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( title, textAlign: TextAlign.center, // TODO: 文本对齐也不生效 ), _uiControl(inEdit: false, type: type, value: value), ]) ], ), onPressed: null, // TODO: 把点击拖拽事件传给父组件 onLongPress: !enabled ? null : () { // TODO: 这里弹出设置组件 print( "按下:" + title + " [" + type.toString() + ": $value ]"); _inputDialog(context: context, touchPoint: this).then((retval) { if (retval != null) { // 点击了确认按钮 print("设置值:" + retval); if (setter != null) setter!(retval); } }); }, ); } } TextEditingController _textFieldController = TextEditingController(); Widget _uiControl( {required bool inEdit, required InputType type, required String value, Function(String)? onPressed, bool? printme}) { if (printme != null) print("编辑状态 $inEdit, 值: $value, " + ((onPressed == null) ? "无回调" : "有回调")); switch (type) { case InputType.numeric: case InputType.text: if (!inEdit) return Text(value); else { Widget textf = TextField( autofocus: true, keyboardType: // TODO: Flutter官方bug,数字键盘相关问题 (type == InputType.numeric) ? TextInputType.number : TextInputType.text, // TODO: 限制最大长度 // TODO: 要不要限制数值范围? controller: _textFieldController, decoration: InputDecoration(hintText: "zzBogusHint"), ); // 设置初始值(只能通过控制器设置) switch (type) { case InputType.numeric: case InputType.text: _textFieldController.text = value; _textFieldController.selection = TextSelection.fromPosition( TextPosition(offset: _textFieldController.text.length)); break; default: // 这几个类型不需要键盘 break; } return textf; } case InputType.boolean: return SizedBox( height: 16, width: 16, child: Checkbox( value: value == "1", onChanged: onPressed == null ? null : (val) { onPressed(value); }, )); case InputType.outconfig: return !inEdit ? ToggleButtons( // 仅做样式展示用,不可交互 disabledColor: Colors.white, color: Colors.white, disabledBorderColor: Colors.white, children: [ int.parse(value) % 2 == 0 ? Text("NO") : Text("NC"), ], constraints: BoxConstraints(minHeight: 16, minWidth: 32), borderRadius: BorderRadius.circular(8.0), borderWidth: 1, isSelected: [true]) : Row( children: [ ToggleButtons( children: [Text("NO"), Text("NC")], constraints: BoxConstraints(minHeight: 16, minWidth: 32), borderRadius: BorderRadius.circular(8.0), borderWidth: 1, isSelected: [ int.parse(value) % 2 == 0, int.parse(value) % 2 == 1 ], onPressed: onPressed == null ? null : (index) => onPressed(index.toString())), ], ); case InputType.sword: return StatusWordWidget(int.parse(value), short: !inEdit, direction: !inEdit ? Axis.horizontal : Axis.vertical); } } // 用于修改值的弹窗 Future<String?> _inputDialog( {required BuildContext context, required TouchPoint touchPoint}) async { String myval = touchPoint.value; final dlg = showDialog<String>( context: context, builder: (context) { return AlertDialog( title: Text(touchPoint.title), content: Column(mainAxisSize: MainAxisSize.min, // 弹窗大小自适应内容 children: [ if (touchPoint.warning != null) Text(touchPoint.warning!), if (!touchPoint.onlyButtons) _uiControl( printme: true, inEdit: true, type: touchPoint.type, value: myval, onPressed: (str) { switch (touchPoint.type) { case InputType.boolean: myval = myval == "0" ? "1" : "0"; print("布尔值更新: $myval"); break; case InputType.outconfig: int ival = int.parse(myval); print("NO/NC切换前: $str - $ival"); ival = ((ival & 0xFF00) + ((ival & 0xFF) == 0 ? 1 : 0)); myval = ival.toString(); print("NO/NC切换后: $ival"); break; default: } }, ) ]), actions: <Widget>[ FlatButton( color: Colors.red, textColor: Colors.white, child: Text('取消'), onPressed: () { Navigator.pop(context); }, ), FlatButton( color: Colors.green, textColor: Colors.white, child: Text('确认'), onPressed: () { // 需要从控制器获取TextField的输入值 String retval; switch (touchPoint.type) { case InputType.numeric: case InputType.text: retval = _textFieldController.text; break; default: retval = myval; } Navigator.pop(context, retval); }, ), ], ); }); return dlg; }
内容的提问来源于stack exchange,提问作者ThorMJ
相关产品推荐
相关产品推荐

