設計給的效果如下:
拿到設計後,先把整體拆分成幾個部分:
- “獲取相冊圖片”,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,
),
],
);
}