设计给的效果如下:
UI布局图拿到设计后,先把整体拆分成几个部分:
- “获取相册图片”,Flutter团队开发的图片选择器(
image_picker
)插件,从手机相册中获取图片。 - “默认头像图片”,新用户默认的头像图片,右下方通过一个小图片提醒用户可以点击设置头像。
- “圆形头像图片”,经过简单裁剪后的圆形头像图片,上面覆盖一层边框背景图片。
然后就可以开始进行编码了。
第1步:绘制组件树
有背景图的头像选择的组件树第2步:实现“获取相册图片”
使用Flutter团队开发的image_picker插件,你可以轻松的调用Android和iOS系统的相册和相机,它会返回用户所选择的图片文件路径,这样你就可以通过文件路径去访问用户所选图片。
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
/// 自定义的上传头像组件。
class UploadAvatar extends StatefulWidget {
@override
_UploadAvatarState createState() => _UploadAvatarState();
}
/// 与自定义的上传头像组件关联的状态子类。
class _UploadAvatarState extends State<UploadAvatar> {
// 文件(`File`)类,对文件系统上的文件的引用。
/// 从相册选择的图片文件。
File _image;
/// 打开系统相册并选择一张照片。
Future getImage() async {
// Flutter团队开发的图片选择器(`image_picker`)插件。
// 适用于iOS和Android的Flutter插件,用于从图像库中拾取图像,并使用相机拍摄新照片。
// https://pub.dartlang.org/packages/image_picker
var image = await ImagePicker.pickImage(source: ImageSource.gallery);
setState(() {
// 更新用作头像的照片。
_image = image;
});
}
// TODO: 第3步:实现“默认头像图片”
// TODO: 第4步:实现“圆形头像图片”
@override
Widget build(BuildContext context) {
return Row(
// 主轴对齐(`mainAxisAlignment`)属性,如何将子组件放在主轴上。
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
GestureDetector(
onTap: getImage,
child: Container(
width: 130.0,
height: 135.0,
child: _image == null
// 第3步实现的自定义组件。
? defaultImage
// 第4步实现的自定义组件。
: ovalImage(_image),
),
),
],
);
}
}
第3步:实现“默认头像图片”
通过堆栈(Stack
)组件在默认头像图片的上面放提醒操作的图片,作为用户没有选择头像图片时的默认显示组件。
// TODO: 第3步:实现“默认头像图片”
/// 自定义的默认头像组件,用来显示默认图片。
Widget defaultImage = Stack(
children: <Widget>[
Align(
// 默认头像图片放在左上方。
alignment: Alignment.topLeft,
child: Image.asset(
'assets/icon_head_default.png',
fit: BoxFit.contain,
height: 130.0,
width: 135.0,
),
),
Align(
// 编辑头像图片放在右下方。
alignment: Alignment.bottomRight,
child: Image.asset(
'assets/icon_edit.png',
fit: BoxFit.contain,
height: 48.0,
),
),
],
);
第4步:实现“圆形头像图片”
使用剪辑椭圆形(ClipOval
)组件将图片裁剪成圆形图片,放在边框背景图片的下面,再通过容器(Container
)组件来对齐位置,使它们严丝合缝的结合在一起。
// TODO: 第4步:实现“圆形头像图片”
/// 自定义的椭圆形头像组件,用来裁剪显示头像。
Widget ovalImage(File image) {
return Stack(
children: <Widget>[
Container(
// 通过容器(`Container`)组件的填充(`padding`)属性,使头像对齐边框。
padding: EdgeInsets.fromLTRB(9.0, 10.5, 0.0, 0.0),
// 剪辑椭圆形(`ClipOval`)组件,使用椭圆剪辑其子项的组件。
child: ClipOval(
// 图片.文件(`Image.file`)构造函数,创建一个窗口组件,显示从文件获取的图片流。
child: Image.file(
image,
fit: BoxFit.cover,
height: 109.0,
width: 109.0,
),
),
),
// 头像边框图片默认放在左上方。
Image.asset(
'assets/icon_head_default_null.png',
fit: BoxFit.contain,
height: 130.0,
width: 135.0,
),
],
);
}
网友评论