首页 文章

Flutter Widgets安排和活动

提问于
浏览
2

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

enter image description here

关于此页面我几乎没有问题:

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

  • 输入字段小部件,如开始/结束和日期,当您单击图标时,它将打开时间或日期选择器 . 我也希望当用户点击文本输入字段时,它将打开选择器,因此用户将无法手动编辑内容 .

  • 当我点击持续时间并注意 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}");
}

请看一下使用键盘的屏幕截图:

enter image description here

谢谢你的回答 .

1 回答

  • 1

    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置于更高优先级 . 但这不是一个正确的方法 .

    TextFields仅用于编辑文本 . 改为使用文本小部件 . 看看这个例子:

    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)我没有遇到运行代码的问题 . 每当弹出软键盘时,内容应向上滚动以关注当前的textField . 如果可能的话,更新问题并为此设置截图 .

    对于键盘屏幕问题,请查看以下文章:IssueSolution

相关问题