我想在Flutter中构建一个表单,请查看我到目前为止制作的屏幕图像:
关于此页面我几乎没有问题:
-
如何对齐
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}");
}
请看一下使用键盘的屏幕截图:
谢谢你的回答 .
1 回答
1)简单,只需设置相同宽度的文本:
2)GestureDecorator应该包装整行,问题在于textField . TextFields有自己的gestureDecorator . 我认为有一种方法可以将外部GestureDecorator置于更高优先级 . 但这不是一个正确的方法 .
TextFields仅用于编辑文本 . 改为使用文本小部件 . 看看这个例子:
3)我没有遇到运行代码的问题 . 每当弹出软键盘时,内容应向上滚动以关注当前的textField . 如果可能的话,更新问题并为此设置截图 .
对于键盘屏幕问题,请查看以下文章:Issue和Solution