- Flutter和Dart系列文章和程式碼GitHub地址
Flutter
一切皆Widget
的核心思想, 為我們提供了兩種主題風格CupertinoApp
: 一個封裝了很多iOS
風格的小部件,一般作為頂層widget
使用MaterialApp
: 一個封裝了很多安卓風格的小部件,一般作為頂層widget
使用, 下面我們先看下這個Widget
MaterialApp
這裡我們先看看MaterialApp
的建構函式和相關函式
const MaterialApp({
Key key,
// 導航主鍵, GlobalKey<NavigatorState>
this.navigatorKey,
// 主頁, Widget
this.home,
// 路由
this.routes = const <String, WidgetBuilder>{},
// 初始化路由, String
this.initialRoute,
// 構造路由, RouteFactory
this.onGenerateRoute,
// 為止路由, RouteFactory
this.onUnknownRoute,
// 導航觀察器
this.navigatorObservers = const <NavigatorObserver>[],
// widget的構建
this.builder,
// APP的名字
this.title = '',
// GenerateAppTitle, 每次在WidgetsApp構建時都會重新生成
this.onGenerateTitle,
// 背景顏色
this.color,
// 主題, ThemeData
this.theme,
// app語言支援, Locale
this.locale,
// 多語言代理, Iterable<LocalizationsDelegate<dynamic>>
this.localizationsDelegates,
// flutter.widgets.widgetsApp.localeListResolutionCallback
this.localeListResolutionCallback,
// flutter.widgets.widgetsApp.localeResolutionCallback
this.localeResolutionCallback,
// 支援的多語言, Iterable<Locale>
this.supportedLocales = const <Locale>[Locale('en', 'US')],
// 是否顯示網格
this.debugShowMaterialGrid = false,
// 是否開啟效能監控,覆蓋在螢幕最上面
this.showPerformanceOverlay = false,
// 是否開啟柵格快取影像的檢查板
this.checkerboardRasterCacheImages = false,
// 是否開啟顯示到螢幕外點陣圖的圖層的檢查皮膚
this.checkerboardOffscreenLayers = false,
// 是否開啟覆蓋圖,顯示框架報告的可訪問性資訊 顯示邊框
this.showSemanticsDebugger = false,
// 是否顯示右上角的Debug標籤
this.debugShowCheckedModeBanner = true,
})
複製程式碼
需要注意的幾點
- 如果
home
首頁指定了,routes
裡面就不能有'/'
的根路由了,會報錯,/
指定的根路由就多餘了 - 如果沒有
home
指定具體的頁面,那routes
裡面就有/
來指定根路由 - 路由的順序按照下面的規則來:
- 1、如果有
home
,就會從home
進入 - 2、如果沒有
home
,有routes
,並且routes
指定了入口'/'
,就會從routes
的/
進入 - 3、如果上面兩個都沒有,或者路由達不到,如果有
onGenerateRoute
,就會進入生成的路由 - 4、如果連上面的生成路由也沒有,就會走到
onUnknownRoute
,不明所以的路由,比如網路連線失敗,可以進入斷網的頁面
- 1、如果有
routes
- 宣告程式中有哪個通過
Navigation.of(context).pushNamed
跳轉的路由 - 引數以鍵值對的形式傳遞
key
:路由名字value
:對應的Widget
routes: {
'/home': (BuildContext content) => Home(),
'/mine': (BuildContext content) => Mine(),
},
複製程式碼
initialRoute
- 初始化路由, 當使用者進入程式時,自動開啟對應的路由(home還是位於一級)
- 傳入的是上面
routes
的key
, 跳轉的是對應的Widget
(如果該Widget
有Scaffold.AppBar
,並不做任何修改,左上角有返回鍵)
routes: {
'/home': (BuildContext content) => Home(),
'/mine': (BuildContext content) => Mine(),
},
initialRoute: '/mine',
複製程式碼
onGenerateRoute
當通過Navigation.of(context).pushNamed
跳轉路由時,
在routes
查詢不到時,會呼叫該方法
onGenerateRoute: (RouteSettings setting) {
return MaterialPageRoute(
settings: setting,
builder: (BuildContext content) => Text('生成一個路由')
);
},
複製程式碼
onUnknownRoute
未知路由, 效果跟onGenerateRoute
一樣, 在未設定onGenerateRoute
的情況下, 才會去呼叫onUnknownRoute
onUnknownRoute: (RouteSettings setting) {
return MaterialPageRoute(
settings: setting,
builder: (BuildContext content) => Text('這是一個未知路由')
);
},
複製程式碼
navigatorObservers
- 路由觀察器,當呼叫
Navigator
的相關方法時,會回撥相關的操作 - 比如
push
,pop
,remove
,replace
是可以拿到當前路由和後面路由的資訊 - 獲取路由的名字:
route.settings.name
// navigatorObservers: [HomeObserver()],
// 繼承NavigatorObserver
class HomeObserver extends NavigatorObserver {
@override
void didPush(Route route, Route previousRoute) {
super.didPush(route, previousRoute);
// 獲取路由的名字
print('name = ${route.settings.name}');
// 獲取返回的內容
print('reaule = ${route.currentResult}');
}
}
複製程式碼
builder
如果設定了這個引數, 那麼將會優先渲染這個builder
, 而不會在走路由
builder: (BuildContext content, Widget widget) => Text('builder'),
複製程式碼
title
- 裝置用於識別使用者的應用程式的單行描述
- 在
Android
上,標題顯示在工作管理員的應用程式快照上方,當使用者按下“最近的應用程式”按鈕時會顯示這些快照 - 在
iOS
上,無法使用此值。來自應用程式的Info.plist
的CFBundleDisplayName
在任何時候都會被引用,否則就會引用CFBundleName
- 要提供初始化的標題,可以用
onGenerateTitle
CupertinoApp
用於建立iOS
風格應用的頂層元件, 相關屬性和MaterialApp
相比只是少了theme
和debugShowMaterialGrid
, 其他屬性都一樣, 如下所示
const CupertinoApp({
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.locale,
this.localizationsDelegates,
this.localeListResolutionCallback,
this.localeResolutionCallback,
this.supportedLocales = const <Locale>[Locale('en', 'US')],
this.showPerformanceOverlay = false,
this.checkerboardRasterCacheImages = false,
this.checkerboardOffscreenLayers = false,
this.showSemanticsDebugger = false,
this.debugShowCheckedModeBanner = true,
})
複製程式碼
使用示例如下
return CupertinoApp(
title: 'Cupertino App',
color: Colors.red,
home: CupertinoPageScaffold(
backgroundColor: Colors.yellow,
resizeToAvoidBottomInset: true,
navigationBar: CupertinoNavigationBar(
middle: Text('Cupertino App Bar'),
backgroundColor: Colors.blue,
),
child: Center(
child: Container(
child: Text('Hello World'),
),
),
),
);
複製程式碼
CupertinoPageScaffold
一個iOS
風格的頁面的基本佈局結構。包含內容和導航欄
const CupertinoPageScaffold({
Key key,
// 設定導航欄, 後面會詳解
this.navigationBar,
// 設定內容頁面的背景色
this.backgroundColor = CupertinoColors.white,
// 子widget是否應該自動調整自身大小以適應底部安全距離
this.resizeToAvoidBottomInset = true,
@required this.child,
})
複製程式碼
navigationBar
const CupertinoNavigationBar({
Key key,
//導航欄左側元件
this.leading,
//是否顯示左邊元件, 好像無效
this.automaticallyImplyLeading = true,
//是否顯示中間元件, 好像無效
this.automaticallyImplyMiddle = true,
//導航欄左側元件的右邊的文字, 好像無效
this.previousPageTitle,
// 導航欄中間元件
this.middle,
// 導航欄右側元件
this.backgroundColor = _kDefaultNavBarBackgroundColor,
// 設定左右元件的內邊距, EdgeInsetsDirectional
this.padding,
//左側預設元件和左側元件右邊文字的顏色
this.actionsForegroundColor = CupertinoColors.activeBlue,
this.transitionBetweenRoutes = true,
this.heroTag = _defaultHeroTag,
})
複製程式碼
使用示例
return CupertinoApp(
title: 'Cupertino App',
color: Colors.red,
debugShowCheckedModeBanner: false,
home: CupertinoPageScaffold(
backgroundColor: Colors.yellow,
resizeToAvoidBottomInset: true,
navigationBar: CupertinoNavigationBar(
leading: Icon(Icons.person),
automaticallyImplyLeading: false,
automaticallyImplyMiddle: false,
previousPageTitle: '返回',
middle: Text('Cupertino App Bar'),
trailing: Icon(Icons.money_off),
border: Border.all(),
backgroundColor: Colors.white,
padding: EdgeInsetsDirectional.fromSTEB(10, 10, 10, 10),
actionsForegroundColor: Colors.red,
transitionBetweenRoutes: false,
heroTag: Text('data'),
),
child: Center(
child: Container(
child: Text('Hello World'),
),
),
),
);
複製程式碼
Scaffold
Scaffold
通常被用作MaterialApp
的子Widget
(安卓風格),它會填充可用空間,佔據整個視窗或裝置螢幕Scaffold
提供了大多數應用程式都應該具備的功能,例如頂部的appBar
,底部的bottomNavigationBar
,隱藏的側邊欄drawer
等
const Scaffold({
Key key,
// 顯示在介面頂部的一個AppBar
this.appBar,
// 當前介面所顯示的主要內容Widget
this.body,
// 懸浮按鈕, 預設在右下角位置顯示
this.floatingActionButton,
// 設定懸浮按鈕的位置
this.floatingActionButtonLocation,
// 懸浮按鈕出現消失的動畫
this.floatingActionButtonAnimator,
// 在底部呈現一組button,顯示於[bottomNavigationBar]之上,[body]之下
this.persistentFooterButtons,
// 一個垂直皮膚,顯示於左側,初始處於隱藏狀態
this.drawer,
// 一個垂直皮膚,顯示於右側,初始處於隱藏狀態
this.endDrawer,
// 出現於底部的一系列水平按鈕
this.bottomNavigationBar,
// 底部的持久化提示框
this.bottomSheet,
// 背景色
this.backgroundColor,
// 重新計算佈局空間大小
this.resizeToAvoidBottomPadding = true,
// 是否顯示到底部, 預設為true將顯示到頂部狀態列
this.primary = true,
})
複製程式碼
appBar
設定導航欄, 接受一個抽象類PreferredSizeWidget
, 這裡使用其子類AppBar
進行設定, 後面會詳解
floatingActionButton
- 設定一個懸浮按鈕, 預設在右下角位置顯示, 這裡使用
FloatingActionButton
設定 FloatingActionButton
是Material
設計規範中的一種特殊Button
,通常懸浮在頁面的某一個位置作為某種常用動作的快捷入口, 後面會詳解
floatingActionButtonLocation
設定懸浮按鈕的位置, 接受一個抽象類FloatingActionButtonLocation
// 右下角, 距離底部有一點距離, 預設值
static const FloatingActionButtonLocation endFloat = _EndFloatFabLocation();
// 中下方, 距離底部有一點距離
static const FloatingActionButtonLocation centerFloat = _CenterFloatFabLocation();
// 右下角, 距離底部沒有間距
static const FloatingActionButtonLocation endDocked = _EndDockedFloatingActionButtonLocation();
// 中下方, 距離底部沒有間距
static const FloatingActionButtonLocation centerDocked = _CenterDockedFloatingActionButtonLocation();
複製程式碼
FloatingActionButton
在Material Design
中,一般用來處理介面中最常用,最基礎的使用者動作。它一般出現在螢幕內容的前面,通常是一個圓形,中間有一個圖示, 有以下幾種建構函式
const FloatingActionButton({
Key key,
this.child,
// 文字解釋, 按鈕唄長按時顯示
this.tooltip,
// 前景色
this.foregroundColor,
// 背景色
this.backgroundColor,
// hero效果使用的tag,系統預設會給所有FAB使用同一個tag,方便做動畫效果
this.heroTag = const _DefaultHeroTag(),
// 未點選時陰影值,預設6.0
this.elevation = 6.0,
// 點選時陰影值,預設12.0
this.highlightElevation = 12.0,
// 點選事件監聽
@required this.onPressed,
// 是否為“mini”型別,預設為false
this.mini = false,
// 設定陰影, 設定shape時,預設的elevation將會失效,預設為CircleBorder
this.shape = const CircleBorder(),
// 剪下樣式
this.clipBehavior = Clip.none,
// 設定點選區域大小的樣式, MaterialTapTargetSize的列舉值
this.materialTapTargetSize,
// 是否為”extended”型別
this.isExtended = false,
})
複製程式碼
mini
- 是否為
mini
型別,預設為false
FloatingActionButton
分為三種型別:regular
,mini
,extended
regular
和mini
兩種型別通過預設的構造方法實現, 只有圖片- 大小限制如下
const BoxConstraints _kSizeConstraints = const BoxConstraints.tightFor(
width: 56.0,
height: 56.0,
);
const BoxConstraints _kMiniSizeConstraints = const BoxConstraints.tightFor(
width: 40.0,
height: 40.0,
);
const BoxConstraints _kExtendedSizeConstraints = const BoxConstraints(
minHeight: 48.0,
maxHeight: 48.0,
);
複製程式碼
isExtended
- 是否為
extended
型別, 設定為true
即可 - 除此之外, 還可以使用
extended
建構函式建立該型別
FloatingActionButton.extended({
Key key,
this.tooltip,
this.foregroundColor,
this.backgroundColor,
this.heroTag = const _DefaultHeroTag(),
this.elevation = 6.0,
this.highlightElevation = 12.0,
@required this.onPressed,
this.shape = const StadiumBorder(),
this.isExtended = true,
this.materialTapTargetSize,
this.clipBehavior = Clip.none,
// 設定圖片
@required Widget icon,
// 設定文字
@required Widget label,
})
複製程式碼
從引數上看差異並不大,只是把預設構造方法中的child
換成了icon
和label
,不過通過下面的程式碼可以看到,傳入的label
和icon
也是用來構建child
的,不過使用的是Row
來做一層包裝而已
AppBar
AppBar
是一個Material
風格的導航欄,它可以設定標題、導航欄選單、底部Tab
等
AppBar({
Key key,
// 導航欄左側weidget
this.leading,
// 如果leading為null,是否自動實現預設的leading按鈕
this.automaticallyImplyLeading = true,
// 導航欄標題
this.title,
// 導航欄右側按鈕, 接受一個陣列
this.actions,
// 一個顯示在AppBar下方的控制元件,高度和AppBar高度一樣,可以實現一些特殊的效果,該屬性通常在SliverAppBar中使用
this.flexibleSpace,
// 一個AppBarBottomWidget物件, 設定TabBar
this.bottom,
//中控制元件的z座標順序,預設值為4,對於可滾動的SliverAppBar,當 SliverAppBar和內容同級的時候,該值為0,當內容滾動 SliverAppBar 變為 Toolbar 的時候,修改elevation的值
this.elevation = 4.0,
// 背景顏色,預設值為 ThemeData.primaryColor。改值通常和下面的三個屬性一起使用
this.backgroundColor,
// 狀態列的顏色, 黑白兩種, 取值: Brightness.dark
this.brightness,
// 設定導航欄上圖示的顏色、透明度、和尺寸資訊
this.iconTheme,
// 設定導航欄上文字樣式
this.textTheme,
// 導航欄的內容是否顯示在頂部, 狀態列的下面
this.primary = true,
// 標題是否居中顯示,預設值根據不同的作業系統,顯示方式不一樣
this.centerTitle,
// 標題間距,如果希望title佔用所有可用空間,請將此值設定為0.0
this.titleSpacing = NavigationToolbar.kMiddleSpacing,
// 應用欄的工具欄部分透明度
this.toolbarOpacity = 1.0,
// 底部導航欄的透明度設定
this.bottomOpacity = 1.0,
})
複製程式碼
leading
導航欄左側weidget
final Widget leading;
// 示例
leading: Icon(Icons.home),
複製程式碼
actions
導航欄右側按鈕, 接受一個陣列
final List<Widget> actions;
// 示例
actions: <Widget>[
Icon(Icons.add),
Icon(Icons.home),
],
複製程式碼
brightness
狀態列的顏色, 黑白兩種
// 狀態列白色
brightness: Brightness.dark,
// 狀態列黑色
brightness: Brightness.light,
複製程式碼
iconTheme
設定導航欄上圖示的顏色、透明度、和尺寸資訊
const IconThemeData({this.color, double opacity, this.size})
// 示例
iconTheme: IconThemeData(color: Colors.white, opacity: 0.56, size: 30),
複製程式碼
TabBar
- 在
AppBar
中通過bottom
屬性來新增一個導航欄底部tab
按鈕組, 接受一個PreferredSizeWidget
型別 PreferredSizeWidget
是一個抽象類, 這裡我們使用TabBar
class TabBar extends StatefulWidget implements PreferredSizeWidget {
const TabBar({
Key key,
// 陣列,顯示的標籤內容,一般使用Tab物件,當然也可以是其他的Widget
@required this.tabs,
// TabController物件
this.controller,
// 是否可滾動
this.isScrollable = false,
// 指示器顏色
this.indicatorColor,
// 指示器高度
this.indicatorWeight = 2.0,
// 指示器內邊距
this.indicatorPadding = EdgeInsets.zero,
// 設定選中的樣式decoration,例如邊框等
this.indicator,
// 指示器大小, 列舉值TabBarIndicatorSize
this.indicatorSize,
// 選中文字顏色
this.labelColor,
// 選中文字樣式
this.labelStyle,
// 文字內邊距
this.labelPadding,
// 未選中文字顏色
this.unselectedLabelColor,
// 未選中文字樣式
this.unselectedLabelStyle,
})
}
// Tab的建構函式
const Tab({
Key key,
// 文字
this.text,
// 圖示
this.icon,
// 子widget
this.child,
})
複製程式碼
效果如下
相關程式碼如下
void main(List<String> args) => runApp(NewApp());
class NewApp extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return App();
}
}
class App extends State<NewApp> with SingleTickerProviderStateMixin {
List tabs = ['語文', '數學', '英語', '政治', '歷史', '地理', '物理', '化學', '生物'];
TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(initialIndex: 0, length: tabs.length, vsync: this);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('CoderTitan'),
backgroundColor: Colors.blueAccent,
brightness: Brightness.dark,
centerTitle: true,
bottom: TabBar(
controller: _tabController,
tabs: tabs.map((e) => Tab(text: e)).toList(),
isScrollable: true,
indicatorColor: Colors.red,
indicatorWeight: 2,
indicatorSize: TabBarIndicatorSize.label,
labelColor: Colors.orange,
unselectedLabelColor: Colors.white,
labelStyle: TextStyle(fontSize: 18, color: Colors.orange),
unselectedLabelStyle: TextStyle(fontSize: 15, color: Colors.white),
),
),
body: TabBarView(
controller: _tabController,
children: tabs.map((e) {
return Container(
alignment: Alignment.center,
child: Text(e, style:TextStyle(fontSize: 50)),
);
}).toList(),
),
),
debugShowCheckedModeBanner: false,
);
}
}
複製程式碼
BottomNavigationBar
- 在
Scaffold
中有一個屬性bottomNavigationBar
用於設定最底部的tabbar
導航欄 - 使用
Material
元件庫提供的BottomNavigationBar
和BottomNavigationBarItem
兩個Widget
來實現Material風格的底部導航欄
BottomNavigationBar({
Key key,
// 子widget陣列
@required this.items,
// 每一個item的點選事件
this.onTap,
// 當前選中的索引
this.currentIndex = 0,
// 型別
BottomNavigationBarType type,
// 文字顏色
this.fixedColor,
// 圖片大小
this.iconSize = 24.0,
})
複製程式碼
items
包含所有子Widget
的陣列
final List<BottomNavigationBarItem> items;
const BottomNavigationBarItem({
// 未選中圖片
@required this.icon,
// 標題
this.title,
// 選中的圖片
Widget activeIcon,
// 背景色
this.backgroundColor,
})
複製程式碼