- 這篇主要介紹下應用界面的結(jié)構(gòu),以及導航相關的小部件,參考王皓的教學視頻,這個教程挺好的,一步一步很詳細適合我這種小白。
先來看看最終效果:

一、MaterialApp和Scaffold
MaterialApp和Scaffold是Flutter提供的兩個Widget
- MaterialApp是一個方便的Widget,它封裝了應用程序?qū)崿F(xiàn)Material Design所需要的一些Widget。
- Scaffold組件是Material Design布局結(jié)構(gòu)的基本實現(xiàn)。此類提供了用于顯示drawer、snackbar和底部sheet的API。
MaterialApp組件中提供了如下屬性:
const MaterialApp({
Key key,
this.navigatorKey,
this.home,
this.routes = const <String, WidgetBuilder>{},
this.initialRoute,
this.onGenerateRoute,
this.onUnknownRoute,
this.navigatorObservers = const <NavigatorObserver>[],
this.builder,
this.title = '',
this.onGenerateTitle,
this.color,
this.theme,
this.locale,
this.localizationsDelegates,
this.localeListResolutionCallback,
this.localeResolutionCallback,
this.supportedLocales = const <Locale>[Locale('en', 'US')],
this.debugShowMaterialGrid = false,
this.showPerformanceOverlay = false,
this.checkerboardRasterCacheImages = false,
this.checkerboardOffscreenLayers = false,
this.showSemanticsDebugger = false,
this.debugShowCheckedModeBanner = true,
})
首先,我們在main.dart中引用import 'package:flutter/material.dart'并返回一個MaterialApp類型app,設置title、主題樣式theme,根視圖home等:
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
debugShowCheckedModeBanner: false, // 不展示右上角"DEBUG"橫幅
title: 'Olive',
theme: ThemeData(
primaryColor: Colors.yellow[300], // 主題顏色
highlightColor: Color.fromRGBO(255, 255, 255, 0.5),
splashColor: Colors.white70,
),
home: MyTabController(), // 根視圖導航控制器
);
}
}
其中MyTabController是自定義的一個dart組件,作為App的根視圖控制器??
二、DefaultTabController
新建一個MyTabController.dart文件,返回一個有狀態(tài)控件DefaultTabController:
import 'package:flutter/material.dart';
import '../pages/HomePage.dart';
import '../pages/LeftDrawerPage.dart';
import 'BottomBarPage.dart';
class MyTabController extends StatefulWidget {
@override
createState() => new MyTabState();
}
class MyTabState extends State<MyTabController> {
int _navIndex = 0;
// 儲存切換bottomNavigationBar時的四個界面
var _body = [
TabBarView(
children: <Widget>[
HomePage(),
Icon(Icons.local_florist, size: 128.0, color: Colors.black12,),
]
),
Icon(Icons.local_airport, size: 128.0, color: Colors.black12,),
Icon(Icons.local_activity, size: 128.0, color: Colors.black12,),
Icon(Icons.local_cafe, size: 128.0, color: Colors.black12,)
];
void navChange(int index) {
setState(() {
_navIndex = index;
});
}
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Scaffold(
backgroundColor: Colors.grey[100],
appBar: _myBar(),
body: _body[_navIndex],
drawer: LeftDrawer(),
bottomNavigationBar: BottomBar(navChange),
),
);
}
}
其中DefaultTabController的child是Scaffold組件。
在該組件中,通過backgroundColor設置界面背景色;_myBar()為自定義的AppBar類型組件,用于配置導航欄相關;drawer為左側(cè)滑抽屜組件,若想從右側(cè)劃出,則使用endDrawer組件。
LeftDrawer()為自定義的Drawer組件,BottomBar()為底部導航欄,在下面都會講到。
- AppBar
上面的_myBar()是一個AppBar類型組件,該組件有五大部分:

提供了如下屬性:
AppBar({
Key key,
this.leading, // 左上角控件,一般放置一個icon
this.automaticallyImplyLeading = true,
this.title, // 標題
this.actions, // 右上角一系列組件
this.flexibleSpace, // 導航欄與bottom間的間隙,見上圖
this.bottom, // 底部控件,位置見上圖
this.elevation = 4.0, // 陰影Z軸
this.backgroundColor, // 背景色
this.brightness, // 狀態(tài)欄亮度
this.iconTheme, // 圖標樣式
this.textTheme, // 文字樣式
this.primary = true,
this.centerTitle, // 標題是否居中顯示,默認值根據(jù)不同的操作系統(tǒng),顯示方式不一樣
this.titleSpacing = NavigationToolbar.kMiddleSpacing,
this.toolbarOpacity = 1.0,
this.bottomOpacity = 1.0,
})
在本示例中,代碼如下:
Widget _myBar () {
return AppBar(
title: Text('Olive'),
elevation: 5.0, // 陰影
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
tooltip: 'Search',
onPressed: () => debugPrint('Search button is pressed')
),
], // 導航欄右側(cè)按鈕
bottom: TabBar(
tabs: <Widget>[
Tab(icon: Icon(Icons.home)),
Tab(icon: Icon(Icons.local_florist)),
],
indicatorColor: Colors.black54,
indicatorSize: TabBarIndicatorSize.label,
unselectedLabelColor: Colors.black38,
), // 導航欄下方的選項卡
);
}
若沒有自定leading左上角控件,且有drawer左側(cè)抽屜,則默認會創(chuàng)建左上角按鈕,且點擊事件為展開左側(cè)抽屜。
若要使用自定義左側(cè)按鈕來打開抽屜,可使用Scaffold.of(context).openDrawer()方法,具體如下:
leading: new Builder(builder: (BuildContext context){
return IconButton(
icon: ClipRRect(
child: Image.asset('images/nav_user.png', fit: BoxFit.contain, width: 28, height: 28),
borderRadius: BorderRadius.circular(28/2),
),
onPressed: (){
Scaffold.of(context).openDrawer();
},
);
}),
- TabBar和TabBarView
TabBar組件為橫向標簽頁,一般結(jié)合TabBarView來使用。
TabBar有如下屬性:
const TabBar({
Key key,
@required this.tabs, // 一系列Tab對象,當然也可以是其他的Widget
this.controller, // TabController對象
this.isScrollable = false, // 是否可滾動
this.indicatorColor, // 指示器顏色
this.indicatorWeight = 2.0, // 指示器厚度
this.indicatorPadding = EdgeInsets.zero, // 底部指示器的內(nèi)間距
this.indicator, // 指示器decoration,例如邊框等
this.indicatorSize, // 指示器大小計算方式
this.labelColor, // 選中Tab文字顏色
this.labelStyle, // 選中Tab文字Style
this.labelPadding, // 文字內(nèi)間距
this.unselectedLabelColor, // 未選中Tab中文字顏色
this.unselectedLabelStyle, // 未選中Tab中文字style
})
本示例中,在上面_myBar()代碼中,給TabBar添加了兩個按鈕:
bottom: TabBar(
tabs: <Widget>[
Tab(icon: Icon(Icons.home)),
Tab(icon: Icon(Icons.local_florist)),
],
indicatorColor: Colors.black54,
indicatorSize: TabBarIndicatorSize.label,
unselectedLabelColor: Colors.black38,
),
在DefaultTabController中設置了TabBarView,控制每個選項卡具體展示的頁面,點擊第一個選項展示HomePage界面,第二個選項卡展示一個Icon,其中HomePage為一個List,這里就不具體說了:
TabBarView(
children: <Widget>[
HomePage(),
Icon(Icons.local_florist, size: 128.0, color: Colors.black12,)
]
),
三、LeftDrawerPage
新建LeftDrawerPage.dart文件,用于側(cè)滑Drawer布局。廢話不多說,直接上代碼:
import 'package:flutter/material.dart';
class LeftDrawer extends StatelessWidget {
final String avatarUrl = 'https://upload.jianshu.io/users/upload_avatars/2650319/becf3e53-9113-43e5-8241-de68bcf8b15f.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/240/h/240';
final String headerBgImgUrl = 'https://images.unsplash.com/photo-1548693316-8ec65a5f2192?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1225&q=80';
List<Icon> _iconItems = <Icon>[
Icon(Icons.message, color: Colors.black12, size: 22,),
Icon(Icons.favorite, color: Colors.black12, size: 22,),
Icon(Icons.settings, color: Colors.black12, size: 22,),
];
List<String> _titleItems = <String>[
'Message', 'Favorite', 'Settings'
];
Widget _listItemBuilder(BuildContext context, int index) {
return new ListTile(
title: Text(
_titleItems[index],
textAlign: TextAlign.right,
),
trailing: _iconItems[index],
onTap: () => Navigator.pop(context),
);
}
Widget _listHeaderBuilder() {
return new UserAccountsDrawerHeader(
accountName: Text(
'Olive',
style: TextStyle(fontWeight: FontWeight.bold),
),
accountEmail: Text('461485900@qq.com'),
currentAccountPicture: CircleAvatar(
backgroundImage: NetworkImage(avatarUrl),
),
decoration: BoxDecoration(
color: Colors.yellow[400],
image: DecorationImage(
image: NetworkImage(headerBgImgUrl),
fit: BoxFit.cover,
colorFilter: ColorFilter.mode(
Colors.yellow[400].withOpacity(0.6),
BlendMode.hardLight
),
)
),
);
}
@override
Widget build(BuildContext context) {
List<Widget> _list = new List();
_list.add(_listHeaderBuilder());
for (int i = 0; i < _titleItems.length; i++) {
_list.add(_listItemBuilder(context, i));
}
return Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: _list,
),
);
}
}
示例中Drawer的child中其實是個ListView,具體用法在上一篇文章中有記錄。這里要提的是,竟然有UserAccountsDrawerHeader這種組件!真的很方便,不知道是不是我孤落寡聞哈哈~
UserAccountsDrawerHeader 能快速設置用戶頭像、用戶名、Email 等信息,顯示一個符合紙墨設計規(guī)范的 drawer header。
四、BottomNavigationBar底部導航欄
創(chuàng)建BottomBarPage.dart文件,代碼如下:
import 'package:flutter/material.dart';
class BottomBar extends StatefulWidget {
ValueChanged<int> _didClickNav;
BottomBar(this._didClickNav);
@override
createState() => new BottomBarState(_didClickNav);
}
class BottomBarState extends State<BottomBar> {
int _currentIndex = 0;
ValueChanged<int> _didClickNav;
BottomBarState(this._didClickNav);
void _onTapHandler (int index) {
setState(() {
_currentIndex = index;
_didClickNav(index);
});
}
@override
Widget build(BuildContext context) {
return BottomNavigationBar(
type: BottomNavigationBarType.fixed,
fixedColor: Colors.black,
currentIndex: _currentIndex,
onTap: _onTapHandler,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.explore),
title: Text('Explore')
),
BottomNavigationBarItem(
icon: Icon(Icons.history),
title: Text('History')
),
BottomNavigationBarItem(
icon: Icon(Icons.list),
title: Text('List')
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
title: Text('My')
),
],
);
}
}