版权声明:本文为本人原创文章,未经本人允许不得转载。
上次我们完成了首页项目展示的基本工作,但是展示的内容都是我们预先写好的,今天我们就来说一说如何获取用户的输入来进行数据展示
废话不多说,我们正式开始。还记得首页底部有个添加符号的按钮吧,我们就是通过这个按钮来进入到新增项目信息的页面DiyAddDialog的。
下面我们开始绘制这个页面需要展示的UI,因为是获取用户输入,那么这个页面基本可以得知都是一些与用户进行交互的控件,比如日期选择器、文本输入、照片获取等等。
老规矩先看下基本效果:
分析上图结构,我们可以看出一共需要用到以下几种控件:
1.日期选择器
2.文本输入控件
3.图片选择器
下面我们分别实现这些控件的显示,当然了这次实际写完的效果可能与上图有出入,因为女王大人给了新的要求,要增加一些内容,所以我会重新规划布局,但是只要你掌握了方法,其他都是一样的。
1.时间选择器
因为时间选择器比较独立,同时可能出现在不同的地方都会使用,所以我们单独写这个时间选择器控件。
在model文件夹下新建date_format.dart文件,在文件中编写日期选择器控件。
因为涉及到日期格式化,所以我们这里需要用到第三方插件包,在整个教程中这里是第一次涉及,所以我放一个中文官方文档链接,里面详细说明了如何使用第三方插件包:https://flutterchina.club/using-packages/#%E6%90%9C%E7%B4%A2packages。
我们这里要用到的是国际化支持的 intl 包,代码如下:
date_format.dart
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class DatePicker extends StatelessWidget {
DatePicker({Key key, this.selectedDate, this.selectDate}) : super(key: key);
//已经选择的时间
final DateTime selectedDate;
//泛型是时间的改变回调函数,当选择时间改变后触发
final ValueChanged<DateTime> selectDate;
//选择时间方法
_datePicker(BuildContext context) async {
DateTime picked = await showDatePicker(
context: context,
initialDate: selectedDate,
firstDate: DateTime(2015, 8),
lastDate: DateTime(2050),
);
if (picked != null) {
selectDate(picked);
}
}
@override
Widget build(BuildContext context) {
return new ListTile(
title: new InkWell(
onTap: () => _datePicker(context),
child: new Row(
children: <Widget>[
new Icon(Icons.today),
new SizedBox(
width: 20.0,
),
new Text(DateFormat.yMd("en_US").format(selectedDate)),
],
),
),
);
}
}
以上代码中,showDatePicker是material材质的安卓日期选择器,返回的是一个将来的时间,所以这里需要用到异步操作。如果有不明白异步的同学可以去了解下简单的异步知识,我也只是了解基本的异步使用。
时间选择器我们写完了,要把时间选择器放到页面上,下一步我们在ui文件夹下继续新建diy_add_show.dart,用于存放新增项目页面的ui布局代码。
diy_add_show.dart
import 'package:activity_record/model/date_format.dart';
import 'package:flutter/material.dart';
class DiyAddShow extends StatefulWidget {
@override
State<StatefulWidget> createState() => new DiyAddShowState();
}
class DiyAddShowState extends State<DiyAddShow> {
//实例化对象已选择的时间,并赋予初始值是当前时间
DateTime _selectedDate = DateTime.now();
@override
Widget build(BuildContext context) {
//safeArea是安全区域小控件,通过足够的填充来保护其子控件,以避免显示内容被系统级元素覆盖或出现异常。
return new SafeArea(
top: false,
bottom: false,
child: new ListView(
children: <Widget>[
new DatePicker(
selectedDate: _selectedDate,
selectDate: (DateTime date) {
setState(() {
_selectedDate = date;
});
},
)
],
),
);
}
}
然后我们回到DiyAddDialog.dart,文件,修改body部分内容,把新增项目的ui放进去:
diy_add_dialog.dart
import 'package:activity_record/ui/diy_add_show.dart';
import 'package:flutter/material.dart';
/*
diy活动新增页面
涉及用户输入所以继承自状态可变的StatefulWidget
采用全屏对话框的形式展现
*/
class DiyAddDialog extends StatefulWidget {
@override
DiyAddDialogState createState() => new DiyAddDialogState();
}
class DiyAddDialogState extends State<DiyAddDialog> {
final _title = '新增活动';
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(_title),
),
body: new DiyAddShow(),//新增项目的UI
);
}
}
终于到看成果的时候了,程序跑起来,点击首页底部的添加按钮,看下效果:
顶部出现了我们的日期显示,默认是当前时间,当然显示格式大家可以根据需求进行调整。
当点击日期后,会弹出时间选择器就可以修改时间了:
以上这个是安卓材质的时间选择器,flutter仓库包里还有诸如ios风格的时间选择器,比如 flutter_cupertino_date_picker,大家可以尝试着把上面的时间选择器换成ios风格的。
2.文本输入框
文本输入框的控件相对其实比较简单,主要就是TextField控件来实现用户输入,而TextField里有个获取用户输入信息的控制器,添加他们
在diy_add_show.dart文件里添加各个输入框的控制器用于获取用户输入的值:
diy_add_show.dart
//活动名称输入框控制器
TextEditingController _nameTextEditingController =
new TextEditingController();
//活动地点输入框控制器
TextEditingController _placeTextEditingController =
new TextEditingController();
//活动联系人输入框控制器
TextEditingController _contactTextEditingController =
new TextEditingController();
//活动单价输入框控制器
TextEditingController _singlePriceTextEditingController =
new TextEditingController();
//活动份数输入框控制器
TextEditingController _numsTextEditingController =
new TextEditingController();
//活动物料成本输入框控制器
TextEditingController _itemCostTextEditingController =
new TextEditingController();
//活动人员成本输入框控制器
TextEditingController _laborCostTextEditingController =
new TextEditingController();
然后我们先添加活动基本信息的输入框:
//活动文字信息输入
Widget _infoTextField(
IconData icon, TextEditingController controller, String hint) {
return Padding(
padding: const EdgeInsets.fromLTRB(18.0, 10.0, 18.0, 0.0),
child: new TextField(
autofocus: true,
controller: controller,
//这个文本框的装饰包含了一个图标和提示文字
decoration: new InputDecoration(icon: new Icon(icon), hintText: hint),
),
);
}
然后添加金额数字输入框:
//活动价格份数信息输入框封装
Widget _amountTextField(TextEditingController controller, String labelText,
String prefixText, String suffixText) {
return new TextField(
//键盘类型适用于登录的
keyboardType: TextInputType.numberWithOptions(signed: true),
controller: controller,
/*
输入框装饰:
包含边框、标题、提示文本、后缀文本
*/
decoration: new InputDecoration(
border: OutlineInputBorder(),
labelText: labelText,
prefixText: prefixText,
suffixText: suffixText,
suffixStyle: new TextStyle(color: Colors.green)),
);
}
不同的输入框我们已经封装写完,最后就是根据自己的设计习惯将他们摆放在页面上展示出来,我们继续在DiyAddShow.dart
中日期选择器的下面把输入框组合起来,完成最后的展示:
_infoTextField(Icons.spa, _nameTextEditingController, '活动名称'),
_infoTextField(
Icons.my_location, _placeTextEditingController, '活动地点'),
_infoTextField(
Icons.tag_faces, _contactTextEditingController, '联系人'),
//把两个金额输入框放在一个包含padding的横向布局里
new Padding(
padding: const EdgeInsets.fromLTRB(18.0, 18.0, 18.0, 0.0),
child: new Row(
children: <Widget>[
new Expanded(
child: _amountTextField(_singlePriceTextEditingController,
'活动单价', '\¥', 'CNY'),
),
new SizedBox(
width: 10.0,
),
new Expanded(
child: _amountTextField(
_numsTextEditingController, '活动份数', '\@', '份'),
),
],
),
),
//把两个金额输入框放在一个包含padding的横向布局里
new Padding(
padding: const EdgeInsets.fromLTRB(18.0, 18.0, 18.0, 0.0),
child: new Row(
children: <Widget>[
new Expanded(
child: _amountTextField(
_itemCostTextEditingController, '物料成本', '\¥', 'CNY'),
),
new SizedBox(
width: 10.0,
),
new Expanded(
child: _amountTextField(
_laborCostTextEditingController, '人员成本', '\¥', 'CNY'),
),
],
),
),
到这里基本新增项目的页面UI就基本实现了,赶紧跑起来看看效果吧:
3.图片选择器
flutter支持不同方式显示图片,比如网络、本地、缓存等等,但是默认好像是没有从相册选择图片的,所以这里就需要用到第三方插件包:image_picker: ^0.4.10,使用方法在上面贴过教程链接,这里就不再介绍如何使用。
在diy_add_show.dart
文件中添加如下代码:
String _imagePath;
//从本地相册选择图片
Future _getImagePath() async {
File file = await ImagePicker.pickImage(source: ImageSource.gallery);
if (file != null) {
setState(() {
_imagePath = file.path;
});
}
}
以上代码就是通过图像选择器插件从相册选取,并获得图片的本地路径。以后数据库保存的时候不是直接保存图片,而是保存图片的地址,所以这里我们预先获得图片路径。其中ImageSource.gallery改成ImageSource.camera就可以调用摄像头拍照了。
我们继续在金额输入框下面添加展示照片的控件,代码如下:
new Padding(
padding: const EdgeInsets.all(18.0),
child: new SizedBox(
height: 120.0,
child: new Container(
decoration: new BoxDecoration(
border: new Border.all(color: Colors.grey),
borderRadius: new BorderRadius.circular(5.0)),
child: new InkWell(
onTap: () => _getImagePath(),
child: _image == null
? new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Icon(Icons.photo,size: 40.0,color: Theme.of(context).primaryColor,),
new Text('从相册选取图片')
],
)
//Image.file是根据路径获得图片
: new Image.file(
File(_imagePath),
fit: BoxFit.cover,
),
),
)),
),
以上代码显示一个120高度的容器,如果图片还未选择,那么显示一个图标和选取图片的文字提示,如果选取了那么直接显示图片。
运行程序看下效果,没有选择图片之前:
点击从相册选取后:
本文总结
通过本文,我想你应该掌握了日期选择器、文本输入框、图片选择器的使用,并对上篇文章介绍的控件之间组合布局有了更深的了解,使用起来肯定更加熟练了。
下次我们将开始介绍页面之间的数据传送。
最后附上项目源码地址:https://gitee.com/xusujun33/activity_record_jia.git
项目持续更新中.......