动态生成TextField,并通过软键盘上的回车跳转到下一个TextField,最后一个TextField关闭软键盘

项目背景:

        近期Flutter项目有个需求,根据后端传过来的数据,动态生成检查项目卡片,卡片中动态生成5个检查结果录入的TextField和一个结果判定的TextField,结果判定的TextField中的内容根据5个检查结果自动判定OK和NG,并将OK背景色设置为绿色,NG背景色设置为红色。通过控制软键盘的回车键自动跳转到下一个TextField,最后一个TextField则关闭软键盘。

代码:

/// @Author JackMa
/// @Date 2023/7/6 11:56
///
/// @Description 初物检查界面

import 'package:flutter/material.dart';

class PrimerCheckContent extends StatefulWidget {
  static String routeName = "PrimerCheck";

  PrimerCheckContent({Key? key})
      : super(key: key);

  @override
  State<PrimerCheckContent> createState() => _PrimerCheckContentState();
}

class _PrimerCheckContentState extends State<PrimerCheckContent> {

  //初物检查项目
  List<Map<String, dynamic>> checkItem = [];
  //最后一个焦点对象,用以控制软键盘关闭
  FocusNode? lastFocusNode;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    
    // 实际项目从后端获取数据
    checkItem.add(
      {
        "insItem": "项目名称",
        "insCondition": "检查规格",
        "max": "10",
        "min": "0",
        "insUnit": "测量单位",
        //五个检查录入格子控制器
        "textEditControllers": [
          new TextEditingController(),
          new TextEditingController(),
          new TextEditingController(),
          new TextEditingController(),
          new TextEditingController(),
        ],
        //五个检查录入格子的状态,用来推断OK/NG
        "textEditStatus": [
          false,
          false,
          false,
          false,
          false,
        ],
        //五个检查录入格子焦点
        "focusNodes": [
          new FocusNode(),
          new FocusNode(),
          new FocusNode(),
          new FocusNode(),
          new FocusNode(),
        ],
        //判定格子的控制器
        "judgeController": new TextEditingController(),
        //通过来回切换true/false来触发组件刷新,防止键盘弹起时无法即使更新TextField的背景色
        "refresh": ValueNotifier<bool>(true)
      },
    );
    checkItem.add(
      {
        "insItem": "项目名称",
        "insCondition": "检查规格",
        "max": "10",
        "min": "",
        "insUnit": "测量单位",
        "textEditControllers": [
          new TextEditingController(),
          new TextEditingController(),
          new TextEditingController(),
          new TextEditingController(),
          new TextEditingController(),
        ],
        "textEditStatus": [
          false,
          false,
          false,
          false,
          false,
        ],
        "focusNodes": [
          new FocusNode(),
          new FocusNode(),
          new FocusNode(),
          new FocusNode(),
          new FocusNode(),
        ],
        "judgeController": new TextEditingController(),
        "refresh": ValueNotifier<bool>(true)
      },
    );
    checkItem.add(
      {
        "insItem": "项目名称",
        "insCondition": "检查规格",
        "max": "",
        "min": "0",
        "insUnit": "测量单位",
        "textEditControllers": [
          new TextEditingController(),
          new TextEditingController(),
          new TextEditingController(),
          new TextEditingController(),
          new TextEditingController(),
        ],
        "focusNodes": [
          new FocusNode(),
          new FocusNode(),
          new FocusNode(),
          new FocusNode(),
          new FocusNode(),
        ],
        "textEditStatus": [
          false,
          false,
          false,
          false,
          false,
        ],
        "judgeController": new TextEditingController(),
        "refresh": ValueNotifier<bool>(true)
      },
    );

    lastFocusNode = checkItem[2]["focusNodes"][4];
  }

  @override
  void dispose() {
    // 将焦点控制对象释放
    for (var item in checkItem) {
      for (var focusNode in item["focusNodes"]) {
        focusNode.dispose();
      }
    }
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: "动态生成TextField",
      ),
      body: Container(
        child: Column(
          children: [
            Container(
              width: MediaQuery.of(context).size.width,
              padding: EdgeInsets.only(left: ScreenAdapter.width(5)),
              height: ScreenAdapter.height(45),
              child: Row(
                children: [
                  Expanded(
                    child: itemTile(
                      "生产品番:",
                      "123123456456",
                      leadingFontSize: ScreenAdapter.sp(25),
                      titleFontSize: ScreenAdapter.sp(25),
                      strutLeading: 2.0,
                    ),
                  ),
                  SizedBox(
                    width: 120.0,
                  ),
                  Expanded(
                    child: itemTile(
                      "本体型号:",
                      "",
                      leadingFontSize: ScreenAdapter.sp(25),
                      titleFontSize: ScreenAdapter.sp(25),
                      strutLeading: 2.0,
                    ),
                  ),
                  SizedBox(
                    width: 120.0,
                  ),
                  Expanded(
                    child: itemTile(
                      "额定电压:",
                      "",
                      leadingFontSize: ScreenAdapter.sp(25),
                      titleFontSize: ScreenAdapter.sp(25),
                      strutLeading: 2.0,
                    ),
                  ),
                ],
              ),
            ),
            Expanded(
              child: ListView.builder(
                itemCount: checkItem.length,
                itemBuilder: (ctx, index) {
                  return checkItemCard(checkItem[index]);
                },
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget checkItemCard(var item) {
    /// description 检查项目卡片
    /// author JackMa
    /// Date 2023/7/7 18:18
    ///
    /// [item:检查项目对象]

    var fontSize = ScreenAdapter.sp(25);
    return Card(
      // 阴影颜色
      shadowColor: Colors.black,
      // 阴影高度
      elevation: 3,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(10),
        side: BorderSide(
          color: Colors.grey,
          width: 2,
        ),
      ),
      child: Container(
        width: ScreenAdapter.width(1025),
        height: ScreenAdapter.height(280),
        padding: EdgeInsets.symmetric(horizontal: 10),
        child: Column(
          children: [
            Row(
              children: [
                Expanded(
                  child: itemTile(
                    "项目名称:",
                    item["insItem"],
                    leadingFontSize: fontSize,
                    titleFontSize: fontSize,
                    strutLeading: 2.0,
                  ),
                ),
                SizedBox(
                  width: 120.0,
                ),
                Expanded(
                  child: itemTile(
                    "检查规格:",
                    item["insCondition"],
                    leadingFontSize: fontSize,
                    titleFontSize: fontSize,
                    strutLeading: 2.0,
                  ),
                ),
              ],
            ),
            Row(
              children: [
                Expanded(
                  child: itemTile(
                    "规格上限:",
                    item["max"],
                    leadingFontSize: fontSize,
                    titleFontSize: fontSize,
                    strutLeading: 2.0,
                  ),
                ),
                SizedBox(
                  width: 120.0,
                ),
                Expanded(
                  child: itemTile(
                    "规格下限:",
                    item["min"],
                    leadingFontSize: fontSize,
                    titleFontSize: fontSize,
                    strutLeading: 2.0,
                  ),
                ),
                SizedBox(
                  width: 120.0,
                ),
                Expanded(
                  child: itemTile(
                    "单位:",
                    item["insUnit"],
                    leadingFontSize: fontSize,
                    titleFontSize: fontSize,
                    strutLeading: 2.0,
                  ),
                ),
              ],
            ),
            judgeWidget(item),
            SizedBox(
              height: 10,
            ),
            ValueListenableBuilder(
                valueListenable: item["refresh"],
                builder: (context, value, widget) {
                  return Row(
                    children: [
                      Text(
                        "判定:",
                        style: TextStyle(
                          fontSize: fontSize,
                        ),
                      ),
                      Expanded(
                        child: TextField(
                          textAlign: TextAlign.center,
                          decoration: InputDecoration(
                            border: OutlineInputBorder(
                              borderRadius: BorderRadius.circular(15.0),
                            ),
                            fillColor: item["judgeController"].text == "NG"
                                ? Colors.red
                                : item["judgeController"].text == "OK"
                                    ? Color.fromRGBO(103, 194, 58, 1)
                                    : Colors.white,
                            filled: true,
                          ),
                          style: TextStyle(
                            fontSize: fontSize,
                          ),
                          controller: item["judgeController"],
                          enabled: false,
                        ),
                      ),
                      Expanded(
                        child: Container(),
                        flex: 4,
                      )
                    ],
                  );
                })
          ],
        ),
      ),
    );
  }

  /// description 检查录入单元格组件
  /// author JackMa
  /// Date 2023/7/7 11:03
  /// 动态检查录入输入框
  judgeWidget(var item) {
    List<Widget> textfieldWidgetList = [];
    for (int i = 0; i < item["textEditControllers"].length; i++) {
      textfieldWidgetList.add(
        Expanded(
          child: StatefulBuilder(
            builder: (context, setInnerState) => TextField(
              keyboardType: TextInputType.number,
              textAlign: TextAlign.center,
              decoration: InputDecoration(
                border: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(15.0),
                ),
                hintText: '请填写',
              ),
              focusNode: item["focusNodes"][i],
              //右下角键盘文字:下一项
              textInputAction: TextInputAction.next,
              style: TextStyle(
                  fontSize: ScreenAdapter.sp(25),
                  color: item["textEditStatus"][i] ? Colors.red : Colors.black),
              controller: item["textEditControllers"][i],
              onEditingComplete: () {
                try {
                  double inputNum =
                      double.parse(item["textEditControllers"][i].text);
                  setInnerState(() {
                    if (item["max"] != "" && item["min"] != "") {
                      item["textEditStatus"][i] =
                          inputNum >= double.parse(item["min"]) &&
                                  inputNum <= double.parse(item["max"])
                              ? false
                              : true;
                    } else if (item["max"] != "") {
                      item["textEditStatus"][i] =
                          inputNum <= double.parse(item["max"]) ? false : true;
                    } else if (item["min"] != "") {
                      item["textEditStatus"][i] =
                          inputNum >= double.parse(item["min"]) ? false : true;
                    }
                  });
                  changeStatus(item, setInnerState);
                  if (item["focusNodes"][i] != lastFocusNode) {
                    FocusScope.of(context).nextFocus();
                  } else {
                    item["focusNodes"][i].unfocus();
                  }
                } catch (e) {
                  showToast("只能输入纯数字!");
                  item["textEditControllers"][i].text = "";
                  item["textEditStatus"][i] = false;
                  changeStatus(item, setInnerState);
                }
              },
            ),
          ),
        ),
      );
      textfieldWidgetList.add(
        const SizedBox(
          width: 10,
        ),
      );
    }
    textfieldWidgetList.removeLast();
    return Row(
      children: textfieldWidgetList,
    );
  }

  /// description 根据检查结果修改判定状态
  /// author JackMa
  /// Date 2023/7/10 10:26
  changeStatus(var item, setInnerState) {
    //是否有异常
    bool flag = false;
    for (int i = 0; i < item["textEditStatus"].length; i++) {
      if (item["textEditStatus"][i]) {
        setInnerState(() {
          item["judgeController"].text = "NG";
          item["refresh"].value = !item["refresh"].value;
        });
        flag = true;
        break;
      }
    }
    if (!flag) {
      //是否有空的判定格子
      bool hasEmpty = false;
      for (int i = 0; i < item["textEditControllers"].length; i++) {
        if (item["textEditControllers"][i].text == "") {
          setInnerState(() {
            item["judgeController"].text = "";
            item["refresh"].value = !item["refresh"].value;
          });
          hasEmpty = true;
          break;
        }
      }
      if (!hasEmpty) {
        setInnerState(() {
          item["judgeController"].text = "OK";
          item["refresh"].value = !item["refresh"].value;
        });
      }
    }
  }
}

效果图:

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值