问题描述
这会太长,但我不知道问题出在哪里/该怎么做。
更新:如果我使用 StateLessWidget 来正确跟踪值;所以...
目的
在其核心,我设想我的 TouchPoint 是一个按钮,如果点击和拖动它会将拖动传递到可滚动容器。如果 Longpressed,它将进入编辑模式并处理几种不同类型的数据;如果在对话框上按下 OK,它将调用 parent() 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(
//This is the screen buton
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(
enabled ? Colors.blue : Colors.grey),foregroundColor: MaterialStateProperty.all(Colors.white)),child: Row(
mainAxisAlignment: MainAxisAlignment.start,//Todo: center?
crossAxisAlignment: CrossAxisAlignment.center,children: [
icon,Column(
//Todo: Alignment here doesn't seem to do anything...
mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [
Text(
title,textAlign:
TextAlign.center,//Todo: TextAlign doesn't work either...
),_uiControl(inEdit: false,type: type,value: value),])
],),onpressed: null,//Todo: Pass click-drag to parent...
onLongPress: !enabled
? null
: () {
//Todo: Flyout setter widget here.
print(
"pressed: " + title + " [" + type.toString() + ": $value ]");
_inputDialog(context: context,touchPoint: this).then((retval) {
if (retval != null) {
//OK was pressed
print("Setter:" + 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("IE $inEdit,val: $value," +
((onpressed == null) ? "nocall" : "wcall"));
switch (type) {
case InputType.numeric:
case InputType.text:
if (!inEdit)
return Text(value);
else {
Widget textf = TextField(
autofocus: true,keyboardType: //Todo: Flutter Bug https://github.com/Flutter/Flutter/issues/58510
(type == InputType.numeric)
? TextInputType.number
: TextInputType.text,//Todo: maxLength: maxlen,//Todo: Min/Max value?
controller: _textFieldController,decoration: Inputdecoration(hintText: "zzBogusHint"),);
//Set Initial Values (since it has to be done via the Controller)
switch (type) {
case InputType.numeric:
case InputType.text:
_textFieldController.text = value;
_textFieldController.selection = TextSelection.fromPosition(
TextPosition(offset: _textFieldController.text.length));
break;
default: //These aren't keyboards
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(
//We're gonna look like toggle buttons,but it's just a statement of what we are.
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")],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);
}
}
//A flyout dialog for changing values
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,//Keep it small
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("BoolUpdate: $myval");
break;
case InputType.outconfig:
int ival = int.parse(myval);
print("NoncB4: $str - $ival");
ival =
((ival & 0xFF00) + ((ival & 0xFF) == 0 ? 1 : 0));
myval = ival.toString();
print("NoncAfter: $ival");
break;
default:
}
},)
]),actions: <Widget>[
FlatButton(
color: Colors.red,textColor: Colors.white,child: Text('CANCEL'),onpressed: () {
Navigator.pop(context);
},FlatButton(
color: Colors.green,child: Text('OK'),onpressed: () {
//You have to get the value of the TextField from the Controller
String retval;
switch (touchPoint.type) {
case InputType.numeric:
case InputType.text:
retval = _textFieldController.text;
break;
default:
retval = myval;
}
Navigator.pop(context,retval);
},);
});
return dlg;
}
解决方法
真正的答案是我需要拆分这个: 主小部件是无状态的 - 因此它会自动更新。
“编辑对话框”是一个单独的 StateFulWidget - 因此您可以与其交互,但值是在创建时设置的。
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(
//This is the screen buton
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(
enabled ? Colors.blue : Colors.grey),foregroundColor: MaterialStateProperty.all(Colors.white)),child: Row(
mainAxisAlignment: MainAxisAlignment.start,//ToDo: center?
crossAxisAlignment: CrossAxisAlignment.center,children: [
icon,Column(
//ToDo: Alignment here doesn't seem to do anything...
mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [
Text(
title,textAlign:
TextAlign.center,//ToDo: TextAlign doesn't work either...
),_uiControl(inEdit: false,type: type,value: value),])
],),onPressed: null,//ToDo: Pass click-drag to parent...
onLongPress: !enabled
? null
: () {
//ToDo: Flyout setter widget here.
_inputDialog(context: context,touchPoint: this).then((retval) {
if (retval != null) {
//OK was pressed
if (setter != null) setter!(retval);
}
});
},);
}
}
TextEditingController _textFieldController = TextEditingController();
Widget _uiControl(
{required bool inEdit,required InputType type,required String value,Function(String)? onPressed}) {
switch (type) {
case InputType.numeric:
case InputType.text:
if (!inEdit)
return Text(value);
else {
Widget textf = TextField(
autofocus: true,keyboardType: //ToDo: Flutter Bug https://github.com/flutter/flutter/issues/58510
(type == InputType.numeric)
? TextInputType.number
: TextInputType.text,//ToDo: maxLength: maxlen,//ToDo: Min/Max value?
controller: _textFieldController,decoration: InputDecoration(hintText: "zzBogusHint"),);
//Set Initial Values (since it has to be done via the Controller)
switch (type) {
case InputType.numeric:
case InputType.text:
_textFieldController.text = value;
_textFieldController.selection = TextSelection.fromPosition(
TextPosition(offset: _textFieldController.text.length));
break;
default: //These aren't keyboards
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(
//We're gonna look like toggle buttons,but it's just a statement of what we are.
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")],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);
}
}
/* The dialog,since it changes the vaues,needs to be a stateful widget */
class _TouchPointDialog extends StatefulWidget {
//ToDo: Pass in things here
_TouchPointDialog({Key? key,required this.touchPoint}) : super(key: key);
final TouchPoint touchPoint;
@override
_TouchPointDialogState createState() =>
_TouchPointDialogState(touchPoint.value);
}
class _TouchPointDialogState extends State<_TouchPointDialog> {
_TouchPointDialogState(this.myval);
String myval;
@override
Widget build(BuildContext context) {
final dlg = AlertDialog(
title: Text(widget.touchPoint.title),content: Column(mainAxisSize: MainAxisSize.min,//Keep it small
children: [
if (widget.touchPoint.warning != null)
Text(widget.touchPoint.warning!),if (!widget.touchPoint.onlyButtons)
_uiControl(
inEdit: true,type: widget.touchPoint.type,value: myval,onPressed: (str) {
setState(() {
switch (widget.touchPoint.type) {
case InputType.boolean:
myval = myval == "0" ? "1" : "0";
break;
case InputType.outconfig:
int ival = int.parse(myval);
ival = ((ival & 0xFF00) + ((ival & 0xFF) == 0 ? 1 : 0));
myval = ival.toString();
break;
default:
}
});
},)
]),actions: <Widget>[
FlatButton(
color: Colors.red,textColor: Colors.white,child: Text('CANCEL'),onPressed: () {
Navigator.pop(context);
},FlatButton(
color: Colors.green,child: Text('OK'),onPressed: () {
//You have to get the value of the TextField from the Controller
String retval;
switch (widget.touchPoint.type) {
case InputType.numeric:
case InputType.text:
retval = _textFieldController.text;
break;
default:
retval = myval;
}
Navigator.pop(context,retval);
},);
return dlg;
}
}
//A flyout dialog for changing values
Future<String?> _inputDialog(
{required BuildContext context,required TouchPoint touchPoint}) async {
return showDialog(
barrierDismissible: false,context: context,builder: (BuildContext context) {
return _TouchPointDialog(touchPoint: touchPoint);
});
}