目標
實現抽屜樣式的下拉式選單,或許 row
和 column
也能夠實現,但是會比較麻煩。這次實現步驟是透過 ListTile
和 ListView
實現。
ListTle 和 ListView
ListTile
官方說 A single fixed-height row that typically contains some text as well as a leading or trailing icon.
是一個高度固定,但單一個列,通常包含一些文字和前導或尾隨的圖示。
原始碼
class ListTile extends StatelessWidget {
/// Creates a list tile.
///
/// If [isThreeLine] is true, then [subtitle] must not be null.
///
/// Requires one of its ancestors to be a [Material] widget.
const ListTile({
Key key,
this.leading, /// 前導,通常都用 Icon Widget
this.title, /// 標題,通常都用 Text widget
this.subtitle, /// 副標題
this.trailing, /// 尾隨(末端),通常都用 Icon Widget
this.isThreeLine = false, /// 是否顯示三行的文字
this.dense, /// 把 ListTitle 高度變小
this.contentPadding, /// 調整邊距,預設是 `EdgeInsets.symmetric(horizontal: 16.0)`
this.enabled = true, /// 點擊動作,預設為可點擊
this.onTap, /// 點擊後的行為
this.onLongPress, /// 長按
this.selected = false, /// 選擇列表時,該列表被選重時會以 `primary color` 作為顏色,`ListTileTheme` 可覆寫
}) : assert(isThreeLine != null),
assert(enabled != null),
assert(selected != null),
assert(!isThreeLine || subtitle != null),
super(key: key);
isThreeLine
為true
時,subtitle
不能為null
,因為需要第二行或以上
官方示意圖
ListView
讓 widget 能夠滾動列表。
官方說明,構造 ListView 有四個選項:
- 默認構造函數採用
children
顯式List <Widget>
。適用於少量的children
列表。 ListView.builder
構造函數採用IndexedWidgetBuilder
,它建立在children
的需求。適用於大量children
或無限多個。ListView.separated
構造函數有兩個IndexedWidgetBuilders:itemBuilder
按需求建立多個子項目,separatorBuilder
建立在出現在子項之間的分隔符的children
。適用於具有固定數量的children
ListView.custom
構造需要SliverChildDelegate
,它提供了自定義child
模型的其他方面的能力。
原始碼,這邊從官網查看參數即可得知定義。
ListView({
...
/// 可滾動 widget 共同參數
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController controller,
bool primary,
ScrollPhysics physics,
EdgeInsetsGeometry padding,
///各個構造函數共同參數
double itemExtent,
bool shrinkWrap = false,
bool addAutomaticKeepAlives = true,
bool addRepaintBoundaries = true,
double cacheExtent,
/// 子 widget 列表
List<Widget> children = const <Widget>[],
})
實作
下面是部分程式碼,無法完整運行。需要自行定義 List<NavigationModel> navigationItem
。這邊付上在 Codelabs
的完整實作鏈接。
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'navigation_model.dart';
class NavDrawer extends StatefulWidget {
List<NavigationModel> navigationItem;
NavDrawer({Key key, @required this.navigationItem }): super(key:key);
@override
NavDrawerState createState() => NavDrawerState();
}
class NavDrawerState extends State<NavDrawer> {
@override
Widget build(BuildContext context) {
Map<String, String> routeValue = ModalRoute.of(context).settings.arguments; // 不同路由傳值接值
// TODO: implement build
return new Material(
child: new Container(
width: 200,
child: new Drawer(
child: new Column(
children: <Widget>[
new NavListTitle(
title: (routeValue == null) ? "F1" : routeValue['title'],
icon: Icons.person,
),
new SizedBox(
width: 50,
),
new Divider(
color: Colors.grey,
height: 10.0,
),
new Expanded(
child: new ListView.separated( /// child 之間的分割符
separatorBuilder: (context, index) {
return new Divider(
color: Colors.grey,
height: 10.0,
);
},
itemBuilder: (context, index) {
return new NavListTitle(
title: widget.navigationItem[index].title,
icon: widget.navigationItem[index].icon,
onTap: () {
setState(() {
return null;
});
},
);
},
itemCount: widget.navigationItem.length,
),
),
],
),
),
),
);
}
}
class NavListTitle extends StatefulWidget {
final String title;
final IconData icon;
final Function onTap;
NavListTitle({@required this.title, @required this.icon, this.onTap});
@override
_NavListTitleState createState() => _NavListTitleState();
}
class _NavListTitleState extends State<NavListTitle> {
@override
Widget build(BuildContext context) {
// TODO: implement build
return new ListTile(
leading: Icon(
widget.icon,
size: 20,
),
title: new Text(
widget.title,
style: TextStyle(fontSize: 15),
),
onTap: widget.onTap,
);
}
}