前言
上篇關於Flutter的文章總結了下標簽+導航的項目模式的搭建,具體的有需要的可以去看看Flutter分類的文章,這篇文章我們簡單的總結一下關於Flutter本地文件引用以及簡單的自定義List的使用,我們先總結本地圖片的引用。今天這篇文章的具體的UI效果如下:

引用本地圖片
我們沒有使用到的我們暫時先不提,等后面慢慢補充進去,比如說網絡圖片的顯示等等,我們現總結一下關於本地圖片的使用,具體的我們需要下面幾步:
1、創建文件導入資源
Flutter的文件資源需要我們創建一個文件去管理,我們可以定義一個images的文件,當然這個名字不是固定的但需要留意下它的文件等級,它里面還可以裝別的其他資源文件,你要叫Resource也可以的。然后不管是做Android的還是iOS的都知道我們的圖片資源是分2x和3x的,所以我們在你創建的文件下面再創建一個2.0x和3.0x的文件夾分別存放相應倍數的圖片資源。如下圖:

2、不是說直接導入就能直接使用的,還需要處理一下 pubspec.yaml文件,具體的改動如下面所示:
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages
#### 圖片資源
assets:
- images/icon_header.png
- images/icon_heath.png
- images/icon_village.png
- images/mine_train.png
前面我們說的文件夾名字不是固定的,綁定使用的的就是assets,這里其實你理解成圖片在項目中的層級位置就可以了。看上面Flutter給的注釋信息,我們完全可以在導入別的信息,如 fonts等等。
3、現在可以直接使用了,它的使用還是相對比較簡單的,我們直接上代碼,需要注意的點是使用的時候需要的是圖片全程,記得帶上后綴。
Image.asset(
"images/icon_header.png",
width: 20,
height: 20,
)
實現上面我的頁面
上面的實現我們需要簡單的了解幾個相應的控件,我們一個一個的介紹一下先,最后就可以出來我們前面的我的頁面的UI了。
1、InkWell 它是一個效果控件 ,點擊有潑墨的水波的效果 ,經常和Material+顏色透明一起使用。
2、Row 水平組件,首先一點是不管是我們現在說的Row還是我們后面說的 Column只能在繼承與StatelessWidget或者StatefullWidget的Widget中使用。
Row({
Key key,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
MainAxisSize mainAxisSize = MainAxisSize.max,
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
TextDirection textDirection,
VerticalDirection verticalDirection = VerticalDirection.down,
TextBaseline textBaseline = TextBaseline.alphabetic,
List<Widget> children = const <Widget>[],
}) : super(
children: children,
key: key,
direction: Axis.horizontal,
mainAxisAlignment: mainAxisAlignment,
mainAxisSize: mainAxisSize,
crossAxisAlignment: crossAxisAlignment,
textDirection: textDirection,
verticalDirection: verticalDirection,
textBaseline: textBaseline,
);
它就這么一個構造方法,還是相對比較簡單的,首先它是不能滾動的,但是它可以靈活布局,如果要讓某個子組件填充滿剩余剩余空間,可以在 children 中使用 Expanded 組件,children 這個屬性看上面的源碼我們知道,它是一個 Widget 數組,這個我們在后面會使用到。具體的它里面的 Expanded 組件我們下面總結,現不在這里細說。
它里面的屬性還是相對比較簡單的,可以自己了解學習一下。我們接着看下面的組件。
3、Column
上面說的是水平的,那這個肯定就是豎直的了,其實它倆挺像的,也都是最基礎的。我們把它初始化方法放出來對比一下,對比一下前面的Row:
Column({
Key key,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
MainAxisSize mainAxisSize = MainAxisSize.max,
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
TextDirection textDirection,
VerticalDirection verticalDirection = VerticalDirection.down,
TextBaseline textBaseline,
List<Widget> children = const <Widget>[],
}) : super(
children: children,
key: key,
direction: Axis.vertical,
mainAxisAlignment: mainAxisAlignment,
mainAxisSize: mainAxisSize,
crossAxisAlignment: crossAxisAlignment,
textDirection: textDirection,
verticalDirection: verticalDirection,
textBaseline: textBaseline,
);
}
4、Expanded
這個我們也需要說說的,因為我們的Row和Column都需要Expanded,其實它也是比較簡單的:
class Expanded extends Flexible {
/// Creates a widget that expands a child of a [Row], [Column], or [Flex]
/// so that the child fills the available space along the flex widget's
/// main axis.
const Expanded({
Key key,
int flex = 1,
@required Widget child,
}) : super(key: key, flex: flex, fit: FlexFit.tight, child: child);
}
我們就需要了解兩個參數一個 flex 和一個 child,child我們就不多說了,比較簡單,flex 按照一個比例理解,比如兩個Expanded , 並排需要控制它們的寬度比例,就可以使用這個屬性。這些我們在下面的代碼使用中都有體現的。我們代碼中細說。
我們看具體的代碼,理解了上面大概說的,這個代碼就相對比較簡單了,我們先看看頂部的頭部我們是怎樣定義的:
class MineHeader extends StatelessWidget {
String userHeaderImage;
String userName;
/// 這里定義了就可以在外面使用這個方法進行初始化
MineHeader(this.userHeaderImage, this.userName);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.orange,
height: 100,
/// 水平布局
/// 在Row中使用Expanded的時候,無法指定Expanded中的子組件的寬度width,但可以指定其高度height。
/// 同理,在Column中使用Expanded的時候,
/// 無法指定Expanded中的子組件的高度height,可以指定寬度width。
child: Row(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
padding: EdgeInsets.only(left: 15),
child: Image.asset(
userHeaderImage,
width: 50,
height: 50,
),
),
),
Expanded(
flex: 5,
child: Container(
padding: EdgeInsets.only(left: 15),
child: Text(
userName,
/// 18號 藍色 加粗
style: TextStyle(
fontSize: 18,
color: Colors.white,
fontWeight: FontWeight.bold),
),
),
),
],
),
);
}
}
需要我們注意的點我們都在上面代碼注釋中基本上都說了,接下來我們我們看看下面列表的代碼:
class MineItemWidget extends StatelessWidget {
String imageName;
String title;
@required
VoidCallback onTap;
MineItemWidget(this.imageName, this.title, {this.onTap});
@override
Widget build(BuildContext context) {
return Container(
/// 子視圖 顏色容器
child: Column(
children: <Widget>[
///
Container(
height: 53,
child: _mineItem(imageName, title),
),
Container(
color: Color(0xffeaeaea),
constraints: BoxConstraints.expand(height: 1.0),
),
],
),
);
}
Widget _mineItem(String imageName, String title) {
return InkWell(
onTap: () {
this.onTap();
},
child: Row(
///
children: <Widget>[
Expanded(
flex: 1,
child: Container(
padding: EdgeInsets.only(left: 15),
child: Image.asset(
imageName,
width: 20,
height: 20,
),
),
),
Expanded(
flex: 6,
child: Container(
padding: EdgeInsets.only(left: 15),
child: Text(
title,
style: TextStyle(fontSize: 16),
),
),
),
],
),
);
}
}
最后就是把它倆寫到我們的 MinePage ,MinePage的代碼如下:
class MinePage extends StatefulWidget {
MinePage({Key key}) : super(key: key);
@override
_MinePageState createState() => _MinePageState();
}
class _MinePageState extends State<MinePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
title: Text("我的"),
backgroundColor: Colors.orange,
elevation: 0, // 去掉Appbar底部陰影
),
body: ListView(
children: <Widget>[
/// 頭部的View
MineHeader("images/icon_header.png", "張旭旭旭旭啊"),
/// 線
_listViewLine,
MineItemWidget("images/icon_heath.png", "我的健康", onTap: () {
/// 跳轉到查看按鈕內容界面
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MineCustomButton()),
);
}),
MineItemWidget("images/icon_village.png", "我的旅行", onTap: () {
print('我的旅行');
}),
_listViewLine,
MineItemWidget("images/mine_train.png", "我的家鄉", onTap: () {
print('我的家鄉');
}),
],
),
);
}
/// 分割線
Widget get _listViewLine {
return Container(
color: Color(0xffeaeaea),
height: 6,
);
}
}
這樣整個一個基本的效果就出來了,如最開始我們給的那張圖效果一樣。打算在后面一篇文章中再說說我們常見到一些組件,按鈕,輸入框,進度條等等的。
參考文章:
