Flutter Widgets 排列和事件

2024-02-12

我正在尝试在 Flutter 中构建一个表单,请看一下我到目前为止制作的屏幕图像:

我对这个页面有几个问题:

  1. 我怎样才能对齐DropDown按钮(项目和任务),以便它们将以相同的宽度拉伸并且它们的图标将位于相同的位置?另外,我怎样才能用某种边框装饰它们?

  2. 输入字段小部件,例如开始/完成和日期,当您单击该图标时,它将打开一个时间或日期选择器。我还希望当用户点击文本输入字段时,它将打开选择器,因此用户将无法手动编辑内容。

  3. 当我点击持续时间和注释时TextFields,软键盘会覆盖这些字段。如何使视图在键盘打开时向上滚动并在键盘关闭时返回?

这是整个页面的代码:

import 'package:flutter/material.dart';
import 'package:tikal_time_tracker/ui/new_record_title.dart';
import '../data/models.dart';
import 'dart:async';

class NewRecordPage extends StatefulWidget {
List<Project> projects;

NewRecordPage({this.projects});

  @override
  State<StatefulWidget> createState() {
    return new NewRecordPageState();
  }
}

class NewRecordPageState extends State<NewRecordPage> {
  Project _selectedProject;
  TimeOfDay _startTime;
  TimeOfDay _finishTime;
  DateTime _date;
  TextEditingController startTimeController;
  TextEditingController finishTimeController;
  TextEditingController dateInputController;
  bool isButtonEnabled = false;

  List<Project> _projects = new List<Project>();

  JobTask _selectedTask;
  List<JobTask> _tasks = new List<JobTask>();

  @override
  void initState() {
    super.initState();
    _projects.addAll(widget.projects);
    startTimeController = new TextEditingController(
      text: "",
    );
    finishTimeController = new TextEditingController(text: "");
    dateInputController = new TextEditingController(text: "");
  }

  void _onProjectSelected(Project value) {
    setState(() {
      _selectedProject = value;
      _tasks.clear();
      _tasks.addAll(value.tasks);
      _selectedTask = null;
    });
  }

  void _onTaskSelected(JobTask value) {
    setState(() {
      _selectedTask = value;
    });
  }

  void _onPickedStartTime(TimeOfDay startTime) {
    setState(() {
      _startTime = startTime;
    });
  }

  void _onPickedFinishTime(TimeOfDay finishTime) {
    setState(() {
      _startTime = finishTime;
    });
  }

  void _setButtonState() {
    setState(() {
      if (_date != null && _startTime != null) {
        print("setButtonState: Button enabled");
        isButtonEnabled = true;
      } else {
        print("setButtonState: Button diabled");
        isButtonEnabled = false;
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    final projectsDropDown = Container(
         padding: EdgeInsets.symmetric(horizontal: 25.0),
        child: Row(
            mainAxisSize: MainAxisSize.max,
            mainAxisAlignment: MainAxisAlignment.start,
            children: <Widget>[
              DropdownButton(
                  hint: new Text(
                    "Select a Project",
                    style: TextStyle(fontSize: 30.0),
                  ),
                  value: _selectedProject,
                  isDense: false,
                  iconSize: 50.0,
                  items: _projects.map((Project value) {
                return new DropdownMenuItem<Project>(
                  value: value,
                  child: new Text(
                    value.name,
                    style: TextStyle(fontSize: 25.0),
                  ),
                );
              }).toList(),
              onChanged: (Project value) {
                _onProjectSelected(value);
              })
        ]));

final tasksDropDown = Container(
  padding: EdgeInsets.symmetric(horizontal: 25.0),
  child: Row(
    mainAxisSize: MainAxisSize.max,
    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
    crossAxisAlignment: CrossAxisAlignment.center,
    children: <Widget>[
      new Expanded(
          child: new DropdownButton(
              iconSize: 50.0,
              hint: Text(
                "Select a Task",
                style: TextStyle(fontSize: 30.0),
              ),
              value: _selectedTask,
              items: _tasks.map((JobTask value) {
                return new DropdownMenuItem<JobTask>(
                  value: value,
                  child: new Text(
                    value
                        .toString()
                        .substring(value.toString().indexOf('.') + 1),
                    style: TextStyle(fontSize: 25.0),
                  ),
                );
              }).toList(),
              onChanged: (JobTask value) {
                _onTaskSelected(value);
              })),
    ],
  ),
);

final srartTimePicker = Container(
  padding: EdgeInsets.only(left: 32.0, right: 32.0, top: 8.0),
  child: new Row(
    children: <Widget>[
      Padding(
        padding: const EdgeInsets.all(8.0),
        child: GestureDetector(
          onTap: () {
            print("onTap start");
            _showStartTimeDialog();
          },
          child: Icon(Icons.access_time),
        ),
      ),
      Container(
        child: new Flexible(
            child: new TextField(
                decoration: InputDecoration(hintText: "Start",
                    contentPadding: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0),
                    border: OutlineInputBorder(borderRadius: BorderRadius.circular(20.0))),
                maxLines: 1,
                controller: startTimeController)),
      ),
    ],
  ),
);

final finishTimePicker = Container(
  padding: EdgeInsets.only(left: 32.0, right: 32.0, top: 8.0),
  child: new Row(
    children: <Widget>[
      Padding(
        padding: const EdgeInsets.all(8.0),
        child: GestureDetector(
          onTap: () {
            print("onTap finish");
            _showFinishTimeDialog();
          },
          child: Icon(Icons.access_time),
        ),
      ),
      Container(
        child: new Flexible(
            child: new TextField(
                decoration: InputDecoration(hintText: "Finish",
                    contentPadding: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0),
                    border: OutlineInputBorder(borderRadius: BorderRadius.circular(20.0))),
                maxLines: 1,
                controller: finishTimeController)),
      ),
    ],
  ),
);

final durationInput = Container(
  padding: EdgeInsets.only(left: 32.0, right: 32.0, top: 8.0),
  child: new Row(
    children: <Widget>[
      Container(
        child: new Flexible(
            child: new TextField(
                decoration: InputDecoration(hintText: "Duration",
                    contentPadding: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0),
                    border: OutlineInputBorder(borderRadius: BorderRadius.circular(20.0))),
                maxLines: 1)),
      ),
    ],
  ),
);

final dateInput = Container(
  padding: EdgeInsets.only(left: 32.0, right: 32.0, top: 8.0),
  child: new Row(
    children: <Widget>[
      Padding(
        padding: const EdgeInsets.all(8.0),
        child: GestureDetector(
          onTap: () {
            print("onTap dateInput");
            _showDatePicker();
          },
          child: Icon(Icons.date_range),
        ),
      ),
      Container(
        child: new Flexible(
            child: new TextField(
                decoration: InputDecoration(hintText: "Date",
                    contentPadding: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0),
                    border: OutlineInputBorder(borderRadius: BorderRadius.circular(20.0))),
                maxLines: 1,
                controller: dateInputController)),
      ),
    ],
  ),
);

return Scaffold(
  appBar: new AppBar(
    title: new Text("Edit New Record"),
  ),
  body: Center(
    child: ListView(
      shrinkWrap: true,
      children: <Widget>[
        new NewRecordTitle(),
        projectsDropDown,
        tasksDropDown,
        dateInput,
        srartTimePicker,
        finishTimePicker,
        durationInput,
        Container(
            padding: EdgeInsets.only(left: 32.0, right: 32.0, top: 8.0),
            child: Column(
              mainAxisSize: MainAxisSize.max,
              children: <Widget>[
                TextField(maxLines: 3,
                    decoration: InputDecoration(
                  border: OutlineInputBorder(borderRadius: BorderRadius.circular(20.0)),
                    contentPadding: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0),
                    hintText: "Note:"))
              ],
            )),
        Column(
          mainAxisAlignment: MainAxisAlignment.end,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Padding(
              padding: EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0),
              child: Material(
                borderRadius: BorderRadius.circular(10.0),
                shadowColor: isButtonEnabled
                    ? Colors.orangeAccent.shade100
                    : Colors.grey.shade100,
                elevation: 2.0,
                child: MaterialButton(
                  minWidth: 200.0,
                  height: 42.0,
                  onPressed: () {
                    if (isButtonEnabled) {
                      print("Button Clicked");
                    }
                  },
                  color: isButtonEnabled ? Colors.orangeAccent : Colors.grey,
                  child: Text("Save", style: TextStyle(color: Colors.white)),
                ),
              ),
            )
          ],
        )
      ],
    ),
  ),
);
}

  Future<Null> _showStartTimeDialog() async {
    final TimeOfDay picked =
    await showTimePicker(context: context, initialTime: TimeOfDay.now());

    if (picked != null) {
      setState(() {
         _startTime = picked;
        startTimeController =
        new TextEditingController(text: 
"${picked.hour}:${picked.minute}");
      });
      _setButtonState();
    }
  }

  Future<Null> _showDatePicker() async {
    final DateTime picked = await showDatePicker(
        context: context,
        initialDate: DateTime.now(),
        firstDate: DateTime(DateTime.now()
            .year - 1, 1),
        lastDate: DateTime(DateTime
            .now()
            .year, 12));

    if (picked != null) {
      setState(() {
        _date = picked;
        dateInputController =
        new TextEditingController(text: 
"${picked.month}:${picked.year}");
      });
    }
  }

  Future<Null> _showFinishTimeDialog() async {
    final TimeOfDay picked =
    await showTimePicker(context: context, initialTime: TimeOfDay.now());

    if (picked != null) {
      setState(() {
        _finishTime = picked;
        finishTimeController =
        new TextEditingController(text: 
"${picked.hour}:${picked.minute}");
        calculateDuration(
            date: DateTime.now(),
            startTime: _startTime,
            finishTime: _finishTime);
      });
    }
  }
}

TimeOfDay calculateDuration(
    {DateTime date, TimeOfDay startTime, TimeOfDay finishTime}) {
  DateTime s = new DateTime(date.year, date.month, date.day, startTime.hour, startTime.minute);
  DateTime f = new DateTime(date.year, date.month, date.day, finishTime.hour, finishTime.minute);
  int milisecs = f.millisecond - s.millisecond;
  print("$milisecs");
  DateTime total = new DateTime.fromMillisecondsSinceEpoch(milisecs);
  print("${total.hour}:${total.minute}");
}

请查看小部件上的键盘屏幕截图:

感谢您的任何答复。


1)简单,只需将文本设置为等宽即可:

DropdownButton(
              hint: Container(
                width: MediaQuery.of(context).size.width * 0.7, //You can set width here.
                child: new Text(
                  "Select a Project",
                  style: TextStyle(fontSize: 30.0),
                ),
              ),
              value: _selectedProject,
              isDense: false,
              iconSize: 50.0,
              items: _projects.map((value) {
                return new DropdownMenuItem(
                  value: value,
                  child: new Text(
                    value.name,
                    style: TextStyle(fontSize: 25.0),
                  ),
                );
              }).toList(),
              onChanged: (value) {
                _onProjectSelected(value);
              })

2)GestureDecorator应该包裹整行,问题出在textField上。 TextFields 有自己的gestureDecorator。我认为有一种方法可以将外部 GestureDecorator 置于更高的优先级。但这不是一个正确的方法。

文本字段只能用于编辑文本。请改用文本小部件。看这个例子:

final srartTimePicker = Container(
    padding: EdgeInsets.only(left: 32.0, right: 32.0, top: 8.0),
    child: GestureDetector(  //Thid widgets wraps every other widget
      onTap: () {
        print("onTap start");
        _showStartTimeDialog();
      },
      child: new Row(
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Icon(Icons.access_time),
          ),
          Container(
            child: new Flexible(
              fit: FlexFit.tight,
              child: Container(
                decoration: BoxDecoration(
                    border: Border.all(
                      color: Theme.of(context).disabledColor,
                      width: 2.0,
                      style: BorderStyle.solid,
                    ),
                    borderRadius: BorderRadius.circular(20.0)),
                child: Padding(
                  padding: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0),
                  child: new Text(
                    _startTime != null
                        ? "${_startTime.hour}:${_startTime
                        .minute}"
                        : "Start",
                    style: TextStyle(),
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    ));

3) I didn't face this issue running your code. Whenever the soft keyboard pops up, the content should scroll up to focus on current textField. update the question and put a screenshot for this one if it's possible.

对于键盘超出屏幕的问题,请参阅以下文章:Issue https://github.com/flutter/flutter/issues/10826Solution https://www.didierboelens.com/2018/04/hint-4-ensure-a-textfield-or-textformfield-is-visible-in-the-viewport-when-has-the-focus/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Flutter Widgets 排列和事件 的相关文章

随机推荐

  • 如何制作类型提示前向引用[重复]

    这个问题在这里已经有答案了 我正在研究 python 的 3 5 类型提示 我想知道如何键入提示类方法的返回类型 这就是我的想法 gt gt gt class A classmethod def a cls gt A pass Traceb
  • 引用分配是线程安全的吗?

    我正在 C 中构建一个多线程缓存 它将保存 Car 对象的列表 public static IList
  • 将 apply 的管道输出传送到 centos 中的记录

    我正在尝试自动化 websocket 客户端的一些测试 该客户端根据命令连接到服务器 该服务器基本上是一个语音到文本引擎 客户端支持来自麦克风的音频流 这样人们就可以实时录制自己的声音并将其传输到引擎 我在 centos 虚拟机中运行客户端
  • 如何改变drawAtPoint中NSString的颜色

    我这里有一段代码 它绘制了一个带有单字符字符串的块 CGContextDrawImage context CGRectMake blok getLocation x xunit blok getLocation y yunit 40 40
  • 在 python setup.py data_files 中包含整个目录

    设置的 data files 参数采用以下格式的输入 setup data files target directory list of files to be put there 有没有办法让我指定整个数据目录 这样我就不必单独命名每个文
  • 模糊测试 XML 解析器

    我想对 XML 解析器进行模糊测试 并想知道是否有一些合适的模糊器 如果不仅能生成随机垃圾 还能利用 XSD 或 DTD 等现有模式规范 那就太好了 以下是我在几个月前的搜索过程中偶然发现的一些 XML 模糊器 untidy http so
  • 在 JEditorPane 中突出显示一个单词

    我必须突出显示某个单词在JEditorPane 为此 我使用以下代码 try javax swing text DefaultHighlighter DefaultHighlightPainter highlightPainter new
  • 是否可以通过用户操作或手动管理的触发器来解决 Promise?是的,所以我们能够形成可编辑的承诺

    我希望程序在完成某些用户操作后运行一系列操作 然而 链的一部分将需要等待先前 Promise 的解决或用户已执行某些操作的事实 Promise 可以这样工作吗 我想象理想的程序脚本是这样的 var coreTrigger Promise a
  • C# 中的日期和时间转换 - DateTime.ParseExact() 未按预期工作

    我有日期 时间格式 例如 2013 年 3 月 1 日 92230 根据这个文件 http msdn microsoft com en us library 8kb3ddd4 aspx and 这个链接 http www geekzilla
  • 在Web-Worker中同步等待消息

    是否有某种方法可以同步等待或检查网络工作人员中的新消息 我有一大堆复杂的代码 从 emscripten 编译的 LLVM 我无法围绕回调进行重构 我需要确保在收到并处理来自 UI 线程的消息之前 特定行之后的代码不会执行 如果我用 whil
  • 通过 TableExport.js 将表格上传到 Excel 时保持样式格式

    我在用着TableExport js导出我的html表到Excel文件 我可以导出表格 但在 Excel 工作表中css正在失败 帮助我解决这个问题 我在用着inline仅CSS 我在下面附上了我的代码
  • 通用枚举到可迭代转换器[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 HttpServletRequest 使用了大量 java util Enumeration 我想在
  • com.eed3si9n#sbt- assembly 的库依赖项;0.13.0:未找到

    我正在构建一个 sbt 插件 并希望在 sbt assemble 插件中引用程序集任务 取决于我的任务 为此 我需要将其作为库引用 而不是插件 但不知何故 sbt 无法将其解析为库依赖关系 这就是我的 sbt 的样子 sbtPlugin t
  • 如何使用CMake检测目标架构?

    我已经做了很多研究 但无法找到答案 我如何使用 CMake 可靠地找到我正在编译的目标架构 基本上相当于qmake中的QMAKE TARGET arch 大多数来源似乎建议使用 CMAKE SYSTEM PROCESSOR 但这是一个糟糕的
  • 如何在 Android 中实现 Webhook?

    例如我想要一个网址www example com status在我的 Android 应用程序中 我将观察该应用程序并将从服务器接收多个 POST 请求 对于每个 POST 请求 我都会触发一个功能 我不明白如何在 URL 上实现 24x7
  • 使用照片框架删除相机胶卷资源

    我正在编写一个测试应用程序 看看是否可以使用照片框架删除 iOS 8 中的 照片库 资源 尽管我怀疑这是不可能的 但我发现文档不清楚 并且该网站上的帖子似乎表明这是可能的 看here https stackoverflow com ques
  • 如何解决ssh:/usr/lib64/libcrypto.so.10:没有可用的版本信息[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我正在尝试执行 ssh 并收到以下消息 ssh ssh usr lib64 libcrypto so 10 no version info
  • 如何在具有源图像的 Flutter 中覆盖资源图像?

    我对 Dart 和 Flutter 相当陌生 并且在从源图像覆盖现有资产图像时遇到困难 我的尝试 try File localFile File assets images myImage png localFile writeAsByte
  • 如何通过 Composer 为 PHP CLI 提供脚本(作为独立的和作为依赖项)

    我正在尝试编写一个我想从命令行运行的 PHP 脚本 我想使用 Composer 来管理其依赖项 并使其可以作为其他项目的依赖项进行安装 我还想保持单独使用它 及其依赖项 的能力 现在 main php是我的 入口点 我将从命令行执行的内容
  • Flutter Widgets 排列和事件

    我正在尝试在 Flutter 中构建一个表单 请看一下我到目前为止制作的屏幕图像 我对这个页面有几个问题 我怎样才能对齐DropDown按钮 项目和任务 以便它们将以相同的宽度拉伸并且它们的图标将位于相同的位置 另外 我怎样才能用某种边框装