Flutter Widget的安排和事件 [英] Flutter Widgets arrangement and events
问题描述
我正在尝试在Flutter中构建表单,请看一下到目前为止我制作的屏幕图像:
I am trying to build a form in Flutter, please take a look in the screen image I've made so far:
我对此页面有几个疑问:
I have few questions regarding this page:
-
如何对齐
DropDown
按钮(项目"和任务"),使它们以相同的宽度拉伸,并且其图标位于相同的位置?另外,如何用某种边框装饰它们?
How can I align the
DropDown
buttons (Project and Task) so they will be stretched with the same width and that their icons will be at the same position? Also, How can I decorate them with some kind of border?
输入字段窗口小部件,例如开始/结束"和日期",当您单击该图标时,它将打开时间"或日期选择器".我也希望当用户点击文本输入字段时,它将打开选择器,因此用户将无法手动编辑内容.
The input fields Widgets, like Start/Finish and Date, when you click the icon, it will open a Time or Date Picker. I would like that also when user taps on the text input field, it will open the picker, so the user wont be able to edit the content manually.
当我点击持续时间并注意TextFields
时,软键盘会越过这些字段.键盘打开时如何使视图向上滚动,键盘关闭时如何使视图返回?
When I tap the Duration and Note TextFields
, the soft keyboard go over these fields. How can I make the view scrolled up when the keyboard opens and go back when the keyboard is closed?
这是整个页面的代码:
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}");
}
请使用小部件上方的键盘查看屏幕截图:
Please see a screen shot with the keyboard that go over the widgets:
感谢您的回答.
推荐答案
1)简单,只需为其文本设置相同的宽度:
1) Easy, Just set an equal width their texts :
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置于更高的优先级.但这不是正确的方法.
2) GestureDecorator should wrap the whole row, The problem is with the textField. TextFields has their own gestureDecorator. I think there is a way to put the outer GestureDecorator in higher priority. But this is not a proper way.
TextFields仅应用于编辑文本.请改用文字小部件.看这个例子:
TextFields should be used only to edit a text. use text widgets instead. look at this example:
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上.更新问题,并在可能的情况下为此屏幕截图添加截图.
有关屏幕上的键盘问题,请查看以下文章: 问题和
For keyboard over screen problem look at these articles: Issue and the Solution
这篇关于Flutter Widget的安排和事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!