小部件的状态问题——从父级更新并从内部更新

如何解决小部件的状态问题——从父级更新并从内部更新

这会太长,但我不知道问题出在哪里/该怎么做。

更新:如果我使用 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);
      });
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?