2. Scaffold
2.1 AppBar
| 名稱 | 功能 |
|---|---|
| leading | 標(biāo)題前置控件。在首頁通常顯示應(yīng)用程序的Logo,其它頁面通常顯示為返回按鈕 |
| title | 頁面標(biāo)題。通常顯示當(dāng)前頁面的標(biāo)題文字,可以放組件 |
| actions | 標(biāo)題后置控件。通常使用IconButton來表示,可以放按鈕組 |
| bottom | 底部控件。通常用tabBar來表示放置Tab標(biāo)簽欄 |
| backgroundColor | 導(dǎo)航背景顏色 |
| iconTheme | 圖標(biāo)樣式 |
| textTheme | 文字樣式 |
| centerTitle | 標(biāo)題是否居中顯示 |
| elevation | 值是0.0 就是沒有陰影 |
import 'package:flutter/material.dart';
import './pages/tabs.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Flutter Demo",
theme: ThemeData(primarySwatch: Colors.blue),
home: Scaffold(
appBar: AppBar(
// 背景顏色
backgroundColor: Colors.red,
// 左側(cè)按鈕
leading: IconButton(
onPressed: () {
print("左側(cè)按鈕");
},
icon: const Icon(Icons.menu)),
// 右邊按鈕
actions: [
IconButton(
onPressed: () {
print("setting");
},
icon: const Icon(Icons.settings)),
IconButton(
onPressed: () {
print("more");
},
icon: const Icon(Icons.more))
],
title: const Text("Demo1"),
),
body: const Text("sss"),
),
);
}
}
2.2 TabBar
TabBar是作為AppBar的一部分存在。參考文檔 中提供了兩種實(shí)現(xiàn)頂部TabLayout的方法,兩者效果是一樣的,但第二種功能更為豐富。具體為 TabBar + TabBarView兩個(gè)weight結(jié)合。
| 名稱 | 功能 |
|---|---|
| tabs | 標(biāo)簽組 |
| controller | 標(biāo)簽控制器 |
| isScrollable | 標(biāo)簽組是否可以滾動(dòng) |
| indicatorColor | 指示器的顏色 |
| indicatorWeight | 指示器高度 |
| indicatorPadding | 指示器的內(nèi)邊距 |
| indicator | 指標(biāo)器裝飾 |
| indicatorSize | 指示器的大小 |
| labelColor | 標(biāo)簽的顏色 |
| labelStyle | 標(biāo)簽的樣式 |
| labelPadding | 標(biāo)簽的內(nèi)邊距 |
| unselectedLabelColor | 選中標(biāo)簽的顏色 |
| unselectedLabelStyle | 選中標(biāo)簽的樣式 |
2.2.1 DefaultTabController
使用 DefaultTabController作為最外層控制器,聯(lián)調(diào) TabBar + TabBarView,這種方式簡單方便,但致命缺點(diǎn)是拿不到當(dāng)前選中tab的index。
class FirstPage extends StatefulWidget {
FirstPage({Key key, this.title}) : super(key: key);
final String title;
@override
State<StatefulWidget> createState() => _FirstPageState();
}
/// DefaultTabController (TabBar + TabBarView) 使用 (內(nèi)部還是TabController實(shí)現(xiàn))
class _FirstPageState extends State<FirstPage> {
final List<Tab> myTabs = <Tab>[
Tab(text: '全部訂單'),
Tab(text: '已完成'),
Tab(text: '未完成')
];
@override
Widget build(BuildContext context) {
// 1. 使用 DefaultTabController 作為外層控制器
return DefaultTabController(
length: myTabs.length,// 定義tab數(shù)量
child: Scaffold(
appBar: AppBar(
title: Text('FirstPage'),
// 2. 使用 TabBar
bottom: TabBar(
tabs: myTabs // 定義TabWeight,若數(shù)量和定義不一致會(huì)報(bào)錯(cuò)
),
),
// 3. 使用 TabBarView
body: TabBarView(
children: <Widget>[
/// 全部訂單
Center(child: Text('全部訂單')),
/// 已完成訂單
Center(child: Text('已完成')),
/// 未完成訂單
Center(child: Text('未完成'))
]),
),
);
}
}
2.2.2 TabController
實(shí)現(xiàn)SingleTickerProviderStateMixin,重寫初始化和注銷方法,利用控制器TabController管理和更多操作,其實(shí)第一種方法內(nèi)部也是由TabController實(shí)現(xiàn)的。
// 1. 實(shí)現(xiàn) SingleTickerProviderStateMixin
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
TabController _tabController;
static const List<Tab> _homeTopTabList = <Tab>[
Tab(text: '音樂', icon: Icon(Icons.music_note)),
Tab(text: '體育', icon: Icon(Icons.directions_run)),
Tab(text: '天氣', icon: Icon(Icons.cloud_queue)),
Tab(text: '科技', icon: Icon(Icons.toys))
];
// 2. 初始化狀態(tài)
@override
void initState() {
super.initState();
// TabController的滾動(dòng)事件會(huì)觸發(fā)一次監(jiān)聽, 點(diǎn)擊事件會(huì)觸發(fā)兩次監(jiān)聽(一次是正常觸發(fā),一次是tab的動(dòng)畫觸發(fā))
_tabController = TabController(length: _homeTopTabList.length, vsync: this);
// 添加監(jiān)聽獲取tab選中下標(biāo)
_tabController.addListener((){
_currentTopTabIndex = _tabController.index;
});
}
// 3. 注銷狀態(tài)
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
// 4. 添加TabBar
bottom: TabBar(
tabs: _homeTopTabList,
controller: _tabController
),
),
// 5. 添加TabBarView
body: TabBarView(
controller: _tabController,
children: <Widget>[
_tabMusic(context),
_tabSport(context),
_tabSport(context),
_tabSport(context)
],
),
);
}
注:關(guān)于TabController 的監(jiān)聽有個(gè)坑 踩坑TabBar之TabController.addListener
2.1 bottomNavigationBar
BottomNavigationBar 和 BottomNavigationBarItem 配合來共同展示Flutter里面的底部狀態(tài)欄,底部狀態(tài)欄是在移動(dòng)端很重要的控件。
| 名稱 | 功能 |
|---|---|
| items | 底部導(dǎo)航按鈕集合 |
| iconSize | icon |
| currentIndex | 默認(rèn)選中第幾個(gè) |
| onTap | 選中變化回調(diào)函數(shù) |
| fixedColor | 選中的顏色 |
| type | BottomNavigationBarType.fixed 或 BottomNavigationBarType.shifting |
下面代碼實(shí)現(xiàn):點(diǎn)擊底部的導(dǎo)航按鈕,顯示不同的頁面
main.dart
import 'package:flutter/material.dart';
import './pages/tabs.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Flutter Demo",
theme: ThemeData(primarySwatch: Colors.blue),
home: const Tabs(),
);
}
}
tabs.dart
import 'package:flutter/material.dart';
import './tabs/home.dart';
import './tabs/category.dart';
import './tabs/me.dart';
class Tabs extends StatefulWidget {
const Tabs({super.key});
@override
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
final List<Widget> _pages = const [Home(), Category(), Me()];
int _currentIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Demo1"),
),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: "首頁"),
BottomNavigationBarItem(icon: Icon(Icons.category), label: "分類"),
BottomNavigationBarItem(icon: Icon(Icons.person), label: "我"),
],
// 底部有4個(gè),或者4個(gè)以上的時(shí)候才生效
type: BottomNavigationBarType.fixed,
),
);
}
}
category.dart 、home.dart 、 me.dart
import 'package:flutter/material.dart';
class Category extends StatefulWidget {
const Category({super.key});
@override
State<Category> createState() => _CategoryState();
}
class _CategoryState extends State<Category> {
@override
Widget build(BuildContext context) {
return const Center(
child: Text("分類"),
);
}
}
2.2 floatingActionButton
用來實(shí)現(xiàn)懸浮按鈕效果的
class ScffoldHomePage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return ScffoldHomePageState();
}
}
class ScffoldHomePageState extends State<ScffoldHomePage> {
///當(dāng)前選中的頁面
num index =0;
@override
Widget build(BuildContext context) {
///使用 Scaffold 組件來構(gòu)建應(yīng)用的基本頁面
/// 頁面的架構(gòu)
return Scaffold(
///定義頁面的標(biāo)題
appBar: AppBar(
title: Text("這里是首頁"),
),
///定義的頁面的主體內(nèi)容
body:pageWidgetList[index],
///定義的懸浮按鈕
floatingActionButton: FloatingActionButton(
child: Text("++"),
///點(diǎn)擊響應(yīng)事
onPressed: () {
print("點(diǎn)擊了 FloatingActionButton");
},
///長按提示
tooltip: "點(diǎn)擊了 tooltip s ",
///設(shè)置懸浮按鈕的背景
backgroundColor: Colors.red,
///獲取焦點(diǎn)時(shí)顯示的顏色
focusColor: Colors.green,
///鼠標(biāo)懸浮在按鈕上時(shí)顯示的顏色
hoverColor: Colors.yellow,
///水波紋顏色
splashColor: Colors.deepPurple,
///定義前景色 主要影響文字的顏色
foregroundColor: Colors.black,
///配制陰影高度 未點(diǎn)擊時(shí)
elevation: 0.0,
///配制陰影高度 點(diǎn)擊時(shí)
highlightElevation: 20.0,
),
///用來控制 FloatingActionButton 的位置
///FloatingActionButtonLocation.endFloat 默認(rèn)使用 浮動(dòng)右下角
///FloatingActionButtonLocation.endDocked 右下角
///FloatingActionButtonLocation.endTop 右上角
///FloatingActionButtonLocation.startTop 左上角
///FloatingActionButtonLocation.centerFloat 底部中間浮動(dòng)
///FloatingActionButtonLocation.centerDocked 底部中間不浮動(dòng)
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
);
}
}
2.3 drawer
抽屜組件
import "package:flutter/material.dart";
class CategoryPage extends StatefulWidget {
CategoryPage({Key key}) : super(key: key);
_CategoryPageState createState() => _CategoryPageState();
}
class _CategoryPageState extends State<CategoryPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Flutter App"),
),
// 左側(cè)抽屜
drawer: Drawer(
child: Text('左側(cè)抽屜'),
),
// 右側(cè)抽屜
endDrawer: Drawer(
child: Text('右側(cè)抽屜'),
),
);
}
}