本文首發於公眾號「劉望舒」
關聯絡列 ReactNative入門系列 React Native元件 Flutter基礎系列
前言
在上一篇文章Flutter基礎(四)開發Flutter應用前需要掌握的Basics Widget,我們學習了Basics Widget,除了Basics Widget,我們還需要了解Material Components,也就是Material元件。它提供了實現Material Design準則的視覺、行為和動作的Widget。 官方將Material元件分為為幾個型別:
- 應用程式結構和導航
- Button
- 輸入和選擇
- 對話方塊,警告彈框和皮膚
- 資訊顯示
- 佈局
主要介紹應用程式結構和導航,會分為兩篇文章進行介紹,這一篇介紹應用程式結構和導航分類中的MaterialApp、Scaffold、AppBar。
1.MaterialApp
說到Material元件,不得不提到MaterialApp,它包含了許多Widget,這些Widget通常是實現Material Design的應用程式所必需的。 MaterialApp在此前的文章都用過,簡單的使用這裡就不介紹了,這裡簡單介紹下路由。 在Android開發中我們使用Intent來進行介面跳轉,也稱之為原生路由,後來出現了一些路由框架,比如ARouter。 在Flutter中進行介面跳轉的就是路由,路由用Route類來進行表示,Navigator是對Route進行管理的Widget。Navigator不僅管理了一堆route,還提供管理堆疊的方法 Navigator.push 和 Navigator.pop,通過路由物件的進出棧來控制頁面的跳轉。
flutter路由的使用方式主要有兩種,一種是新建路由,一種是註冊路由。我們分別用這兩種方式寫例子: 首屏是第一個介面,通過第一個介面的按鈕跳轉到第二頁,點選第二頁的按鈕回到第一頁。
1.1 新建路由
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Material Components',
home: FirstPage(),
);
}
}
class FirstPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('第一頁'),
),
body: Padding(
padding: EdgeInsets.all(30.0),
child: RaisedButton(
child: Text('跳轉到第二頁'),
onPressed: () {
Navigator.push(//1
context,
MaterialPageRoute(builder: (context) => SecondPage()),
);
},
),
),
);
}
}
class SecondPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('第二頁'),
),
body: Padding(
padding: EdgeInsets.all(30.0),
child: RaisedButton(
child: Text('回到上一頁'),
onPressed: () {
Navigator.pop(context);//2
}),
),
);
}
}
複製程式碼
註釋1處呼叫了Navigator.push,將新建的路由新增到Navigator管理的route堆疊的棧頂,這個路由我們可以自定義,但是建議使用MaterialPageRoute,它是一個模態路由,可以自適應各個平臺進行頁面替換,並提供了相應的頁面切換動畫。在Android平臺時,頁面進入動畫是向上滑動並淡出,退出是相反的動畫,如果是在iOS平臺 ,頁面進入動畫是從右側滑入,退出是相反的動畫。 點選'跳轉到第二頁'按鈕時會跳轉到SecondPage。註釋2處的Navigator.pop用於彈出route堆疊最頂層的Route。效果如下兩個圖所示。
1.2 註冊路由
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Material Components',
home: FirstPage(),
routes: <String, WidgetBuilder>{//1
'/first': (BuildContext context) => FirstPage(),
'/second': (BuildContext context) => SecondPage(),
},
initialRoute: '/first' ,
);
}
}
class FirstPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('第一頁'),
),
body: Padding(
padding: EdgeInsets.all(30.0),
child: RaisedButton(
child: Text('跳轉到第二頁'),
onPressed: () {
Navigator.pushNamed(context, '/second');//2
},
),
),
);
}
}
class SecondPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('第二頁'),
),
body: Padding(
padding: EdgeInsets.all(30.0),
child: RaisedButton(
child: Text('回到上一頁'),
onPressed: () {
Navigator.of(context).pop();
}),
),
);
}
}
複製程式碼
通過註釋1處的routes用於初始化一個路由列表,當推送路由時,將在routes中查詢路徑名稱,如果名稱存在,則關聯的WidgetBuilder用於構造MaterialPageRoute。註釋2處的Navigator.pushNamed和Navigator.push作用類似,只不過pushNamed的引數為路由的名稱。
2. Scaffold
Scaffold同樣屬於Material元件,它實現了Material Design的基本佈局結構,因此它經常會作為MaterialApp的子Widget, Scaffold會自動填充可用的空間,這通常意味著它將佔據整個視窗或螢幕,並且Scaffold會自動適配螢幕。我們的佈局就是在Scaffold中進行編寫的。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Scaffold示例'),
),
body: Padding(
padding: EdgeInsets.all(30.0),
child: Text('Scaffold'),
),
bottomNavigationBar: BottomAppBar(
child: Container(height: 50),
),
drawer: Drawer(
child: Center(
child: Text('抽屜'),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
),
),
);
}
}
複製程式碼
Scaffold的屬性有很多,例子中用了幾個屬性:
- appBar:用於設定頂部的標題欄。
- body:顯示Scaffold的主要內容。
- bottomNavigationBar:用於設定Scaffold的底部導航欄,
- drawer:用於設定抽屜效果。
- floatingActionButton:用於設定位於右下角的按鈕。
效果如下所示:
可以看到在AppBar上有個抽屜的按鈕,點選按鈕就會滑出抽屜。
3. AppBar
AppBar由toolbar和其他的可選Widget組成,比如TabBar和FlexibleSpaceBar。 AppBar會在頂部顯示leading、title、actions等內容,底部bottom通常顯示TabBar,下圖展示了這些內容的位置分佈。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyScaffld(),
);
}
}
class MyScaffld extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('AppBar示例'),
leading: FlutterLogo(colors: Colors.lightGreen),
actions: <Widget>[
IconButton(
icon: Icon(Icons.share),
onPressed: () {
print('新增按鈕');
},
),
],
),
);
}
}
複製程式碼
這次沒有把所有程式碼寫在MyApp類中,而是將Scaffld的定義放在了MyScaffld類中。
上面程式碼的Widget樹如下所示,遵守Material Design準則的flutter應用的Widget樹大致也是如此。
總結
本文總結了Material元件中的三種Widget,可以說它們是使用Material元件時最常使用的Widget,常用到我們可能會忽略它們。由於篇幅原因,會在下一篇介紹Material元件的其他Widget。