版权声明:本文为本人原创文章,未经本人允许不得转载。
上篇文章我们基本介绍了如何分析布局一个应用的整体框架,以及介绍了不带数据传递的不同页面之间的导航。
今天我们开始对页面要展示的内容进行实现,很显然今天涉及的就是Flutter的各类控件了。
数据模型建立
在界面ui之前呢,我们先把活动项目的数据属性先建立起来,在后面的内容我们会用到。
在lib目录下新建文件夹model,用于存放数据模型,新建diy_project.dart文件:
完成数据属性的说明:
diy_project.dart
/*
diy项目类,说明diy项目的对象属性
*/
class DiyProject {
int _id; //项目id
String _name; //项目名字
String _date; //项目时间
String _place; //项目地点
String _contact; //项目联系人
String _imagePath; //项目照片地址
int _singlePrice; //项目单价
int _nums; //项目份数
int _totalAmount; //项目总价
int _itemCost; //物料成本
int _laborCost; //人员成本
int _profit; //项目利润
bool _isCheckOut; //钱款是否结清
//活动项目构造函数
DiyProject(
this._name,
this._date,
this._place,
this._contact,
this._imagePath,
this._singlePrice,
this._nums,
this._totalAmount,
this._itemCost,
this._laborCost,
this._profit,
this._isCheckOut,
);
/*
下面是获取活动项目各属性值得方法
*/
String get name => _name;
String get date => _date;
String get place => _place;
String get contact => _contact;
String get imagePath => _imagePath;
int get id => _id;
int get singlePrcie => _singlePrice;
int get nums => _nums;
int get totalAmount => _totalAmount;
int get itemCost => _itemCost;
int get laborCost => _laborCost;
int get profit => _profit;
bool get isCheckOut => _isCheckOut;
}
然后我们在home_page文件中初始化一组项目数据,用于后面的数据展示,代码如下:
home_page.dart
//初始化三个项目数据
List<DiyProject> _diyProjects = [
new DiyProject('多肉种植', '2018-11-2', '万达广场', '苏苏', 'images/4.jpg', 30, 50,
1500, 500, 300, 700, false),
new DiyProject('彩绘尤克里里', '2018-10-22', '寰宇城', '盼盼', 'images/2.jpg', 20, 30,
600, 500, 500, 1500, false),
new DiyProject('小饼干制作', '2018-9-15', '滨江新城', '磊磊', 'images/5.jpg', 40, 50,
2000, 600, 200, 800, false),
];
UI绘制
数据模型属性我们已经准备完毕,下面我们开始一步步实现首页的展示效果
我们要实现的首页展示效果如下:
简单分析以后我们可以看得出首先这是一个可以滚动的ListView,里面包含的是一个个的Card,Card里对应的是项目活动的属性(展示照片、名称、时间、地点等等)。这些属性又是按照一定的方向进行排列的,其实很多教程和文章里都有说到如何分析一个页面的布局,那要点就是一个字:拆
我们对这个card进行拆解:
1、card里是一个Colum的垂直排列
2、从上往下分别是图片、名称日期组合、联系人地点组合、金额
3、其中名称日期和联系人地点是一个Row控件实现的横向排列
下面我们边撸码边看效果:
首先为了整体代码文件的清晰整洁,我们在lib下新建ui文件夹,在ui文件夹里新建diy_list_show.dart
然后在文件中根据上面拆解的结构绘制ui控件:
diy_list_show.dart
import 'package:activity_record/model/diy_project.dart';
import 'package:flutter/material.dart';
class DiyListShow extends StatefulWidget {
//将项目对象作为参数配置给DiyListShow的构造函数
DiyListShow({Key key, this.diyItem}) : super(key: key);
DiyProject diyItem;
@override
State<StatefulWidget> createState() => new DiyListShowState();
}
class DiyListShowState extends State<DiyListShow> {
@override
Widget build(BuildContext context) {
//card第一行时间和预留按钮菜单
Widget _rowTime() {
return new Container(
//设置距离左边8.0的内间距
padding: const EdgeInsets.only(left: 8.0),
child: new Row(
//表示两个子空间头尾分布
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Text(widget.diyItem.date),
new IconButton(
icon: new Icon(Icons.more_horiz),
onPressed: () {},
)
],
),
);
}
//card第三行名称和地点
Widget _rowNameAndPlace() {
return new Container(
padding: const EdgeInsets.all(8.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Text(widget.diyItem.name,
style:
new TextStyle(fontSize: 17.0, fontWeight: FontWeight.bold)),
new Text(widget.diyItem.place),
],
),
);
}
//进行card里的内容组合
Widget _diyContentShow() {
return Container(
height: 288.0,
child: new Column(
children: <Widget>[
_rowTime(),
//使用expanded将填充控件的剩余空间
new Expanded(
//flex代表这个控件在父控件里的范围比例,默认是1,这里表示在高度288的容器里,图片会填满剩余的所有空间
flex: 3,
child: new Image.asset(
widget.diyItem.imagePath,
fit: BoxFit.cover,
width: 400.0,
)),
_rowNameAndPlace(),
new Padding(
padding: const EdgeInsets.fromLTRB(8.0, 0.0, 8.0, 8.0),
child: new Row(
children: <Widget>[
new Text(
'${widget.diyItem.singlePrcie.toString()}元',
style: new TextStyle(
fontSize: 15.0,
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold),
),
new SizedBox(
width: 20.0,
),
new Text(
'${widget.diyItem.nums.toString()}份',
style: new TextStyle(
fontSize: 15.0,
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold),
)
],
),
)
],
),
);
}
//将整个项目展示内容包裹在card里
return new Card(
margin: const EdgeInsets.fromLTRB(18.0, 18.0, 18.0, 9.0), //设置外边距18
//card形状设置顶部圆形弧度12,底部没有
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(12.0))),
//inkwell是一个带水波纹触摸效果的控件,预留点击回调作为以后点击响应事件
child: new InkWell(
onTap: () {},
child: _diyContentShow(),
),
);
}
}
里面的主要知识点我进行了注释,细心的朋友可能发现了一个知识点——image.asset,下面介绍使用方法:
首先需要在lib同级新建图片文件夹images
- 将想使用的图片拷贝到目录下
- 在
pubsec.yaml
文件里配置图片的位置引用
引用方法如下:
这样我们就能使用image.asset('图片地址')展示图片了。
Card的UI我们写完了,下面我们回到home_page.dart,修改body代码看看card得显示效果如何:
home_page.dart
body: new ListView.builder(
itemCount: 3,
itemBuilder: (context, index) {
return new DiyListShow(
diyItem: _diyProjects[index],
);
},
),
写完后可能发现DiyListShow()下面有警告,因为我们还没引用这个dart文件,所以在开头我们导入文件:
import 'package:activity_record/ui/diy_list_show.dart';
包括以后在引用或使用其他dart文件里的方法内容的时候都要记得先导入需要引用的文件。
以上代码中ListView是列表滚动控件,他有两个构造函数,分别是ListView()和ListView.builder()
其中ListView()适合内容不多的情况使用,因为flutter会全部渲染完成后展现,如果是数据量很多或者无限数据的话就要使用ListView.builder(),这个构造方法是根据用户滚动的情况,实时渲染需要展现的数据,当滑出屏幕后就会回收对应的数据。
写了这么多,是时候看下效果了,UI界面的调整都是边看边改的,对于新人我们不可能一步到位直接写好,边写边热重载边修改非常关键,好了运行后效果如下:
感觉是不是还可以,哈哈 自恋下。可能有人觉得奇怪,这和上面开头的图的布局不一样啊,这是因为女王大人给我提了新的需求,她对之前的效果提出了修改意见,要知道用户的需求才是关键嘛,毕竟我们做完是给他们用的,所以我做了调整和之前的不一样了,不过只要你掌握了方法,无论怎么改我相信你都可以做出来的啦。
最后总结
今天我们主要介绍了flutter里的基础控件,虽然用到的不多,但是都是非常基础和常用的,包括listview、container、row、column、sizedbox、card、text、iconbutton、expanded、padding等等,每个控件里都有很多属性,对于新人来说不要记得那么多,主要记住每个控件常用的属性就可以啦,其他的后面再慢慢学习就好了。
试试看在每个内容前加上小的示意图标(比如在时间前加上代表时间的icon),美化整个card效果,动手试试吧!或者你也可以根据自己想要的样子设计一个展示UI,然后自己实现一下吧。
最后附上项目源码地址:https://gitee.com/xusujun33/activity_record_jia.git
项目持续更新中.......