我正在尝试在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}");
}
请使用小部件上方的键盘查看屏幕截图:
谢谢您的回答。
答案 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上。更新问题并在可能的情况下为此屏幕截图添加截图。