Flutter Widget的安排和事件

时间:2018-08-11 07:19:55

标签: flutter flutter-layout

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

enter image description here

我对此页面有几个疑问:

  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}");
}

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

enter image description here

谢谢您的回答。

1 个答案:

答案 0 :(得分: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