Flutter 構(gòu)建一個(gè)常用的頁(yè)面框架

最終實(shí)現(xiàn)的結(jié)果如上圖所示,頂部共用一個(gè)導(dǎo)航欄,底部有四個(gè)圖標(biāo)導(dǎo)航,點(diǎn)擊對(duì)應(yīng)的圖標(biāo)跳轉(zhuǎn)到對(duì)應(yīng)的頁(yè)面。
圖標(biāo)準(zhǔn)備
本次例程需要4個(gè)圖標(biāo),2種顏色,可以從 iconfont 中找到自己需要的圖標(biāo)下載不同的顏色使用。然后在 pubspec.yaml 中的 assets 指定素材所在目錄。需要注意的是如果是 png 文件直接指定整個(gè)目錄即可,但如果是 jpg 格式,則需要同時(shí)指定文件名及后綴。
BottomNavigationBar 簡(jiǎn)介
BottomNavigationBar的構(gòu)造函數(shù)如下:
BottomNavigationBar({
Key? key,
required this.items,
this.onTap,
this.currentIndex = 0,
this.elevation,
this.type,
Color? fixedColor,
this.backgroundColor,
this.iconSize = 24.0,
Color? selectedItemColor,
this.unselectedItemColor,
this.selectedIconTheme,
this.unselectedIconTheme,
this.selectedFontSize = 14.0,
this.unselectedFontSize = 12.0,
this.selectedLabelStyle,
this.unselectedLabelStyle,
this.showSelectedLabels,
this.showUnselectedLabels,
this.mouseCursor,
})
其中常用的屬性為:
- items:及對(duì)應(yīng)的頁(yè)面組件數(shù)組
- currentIndex:默認(rèn)顯示第幾個(gè)頁(yè)面
- type:組件類(lèi)型,使用BottomNavigationBarType枚舉,有 fixed 和 shifting 兩種。fixed 是圖標(biāo)固定位置,而 shifting 的圖標(biāo)點(diǎn)擊后會(huì)有一個(gè)漂移效果,可以實(shí)際試一下,一般用fixed 比較多。
- onTap:點(diǎn)擊后的事件,一般用這個(gè)更新?tīng)顟B(tài)數(shù)據(jù),以便更新頁(yè)面。
其他屬性用于控制樣式的,可以根據(jù)實(shí)際需要設(shè)置圖標(biāo)大小,主題色,字體等參數(shù)。
構(gòu)建項(xiàng)目頁(yè)面結(jié)構(gòu)
首先,新建四個(gè)業(yè)務(wù)頁(yè)面,分別是 dynamic.dart,message.dart,category.dart 和 mine.dart,分別對(duì)應(yīng)動(dòng)態(tài)、消息、分類(lèi)瀏覽和個(gè)人中心四個(gè)頁(yè)面。目前這四個(gè)頁(yè)面很簡(jiǎn)單,只是在頁(yè)面中間依次顯示“島上碼農(nóng)”四個(gè)字。代碼都是類(lèi)似的,以 dynamic 為例:
import 'package:flutter/material.dart';
class DynamicPage extends StatelessWidget {
const DynamicPage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text('島'),
),
);
}
}
注意的是這里的 Scaffold 沒(méi)有 AppBar 了,這是因?yàn)樵谑醉?yè)已經(jīng)有了,如果再有 AppBar 就會(huì)出現(xiàn)兩個(gè)。
其次,新建首頁(yè),用于管理四個(gè)業(yè)務(wù)頁(yè)面,命名為 app.dart。app.dart 使用了 BottomNavigationBar 管理四個(gè)業(yè)務(wù)頁(yè)面的切換。
import 'package:flutter/material.dart';
import 'dynamic.dart';
import 'message.dart';
import 'category.dart';
import 'mine.dart';
class AppHomePage extends StatefulWidget {
AppHomePage({Key key}) : super(key: key);
@override
_AppHomePageState createState() => _AppHomePageState();
}
class _AppHomePageState extends State<AppHomePage> {
int _index = 0;
List<Widget> _homeWidgets = [
DynamicPage(),
MessagePage(),
CategoryPage(),
MinePage(),
];
void _onBottomNagigationBarTapped(index) {
setState(() {
_index = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('島上碼農(nóng)'),
),
body: IndexedStack(
index: _index,
children: _homeWidgets,
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: _index,
onTap: _onBottomNagigationBarTapped,
items: [
_getBottomNavItem(
'動(dòng)態(tài)', 'images/dynamic.png', 'images/dynamic-hover.png', 0),
_getBottomNavItem(
' 消息', 'images/message.png', 'images/message-hover.png', 1),
_getBottomNavItem(
'分類(lèi)瀏覽', 'images/category.png', 'images/category-hover.png', 2),
_getBottomNavItem(
'個(gè)人中心', 'images/mine.png', 'images/mine-hover.png', 3),
],
),
);
}
BottomNavigationBarItem _getBottomNavItem(
String title, String normalIcon, String pressedIcon, int index) {
return BottomNavigationBarItem(
icon: _index == index
? Image.asset(
pressedIcon,
width: 32,
height: 28,
)
: Image.asset(
normalIcon,
width: 32,
height: 28,
),
label: title,
);
}
}
這里關(guān)鍵的地方有兩個(gè),一是使用的 IndexedStack,這是一個(gè)管理頁(yè)面顯示層級(jí)的容器。使用 index 屬性確定當(dāng)前容器里那個(gè)頁(yè)面在最頂上,容器里的頁(yè)面通過(guò) children 屬性設(shè)置,要求是一個(gè) Widget 數(shù)組。因此,邏輯就是當(dāng) BottomNavigationBar 中的圖標(biāo)被點(diǎn)擊后,對(duì)應(yīng)點(diǎn)擊事件會(huì)回調(diào) onTap屬性指定的方法,將當(dāng)前的點(diǎn)擊索引值傳遞回調(diào)函數(shù),因此可以利用這個(gè)方式控制 IndexedStack 的頁(yè)面層級(jí)切換。
最后,使用了狀態(tài)變量_index 存儲(chǔ)IndexedStatck當(dāng)前顯示頁(yè)面的索引值,然后當(dāng) BottomNavigationBar的圖標(biāo)點(diǎn)擊事件發(fā)生后,在回調(diào)函數(shù)中使用 setState 更新?tīng)顟B(tài)變量_index 來(lái)刷新當(dāng)前界面。
簡(jiǎn)化入口
main.dart 是入口文件,應(yīng)當(dāng)做最基礎(chǔ)的配置和全局初始化配置,而不應(yīng)該有業(yè)務(wù)代碼,因此可以簡(jiǎn)化為從main 方法加載首頁(yè)即可。通過(guò)這種方式可以讓 main.dart 文件即為簡(jiǎn)潔。這也是在開(kāi)發(fā)的時(shí)候需要注意的地方,將不相關(guān)的代碼剝離,相關(guān)的代碼聚合,即所謂的“高內(nèi)聚,低耦合”原則。
import 'package:flutter/material.dart';
import 'app.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'App 框架',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: AppHomePage(),
);
}
}
代碼復(fù)用
寫(xiě)代碼的時(shí)候要注意復(fù)用,在這里將構(gòu)建 BottomNavigationBar 元素抽離出了一個(gè)構(gòu)建方法_getBottomNavItem,從而提高代碼的復(fù)用性和維護(hù)性,也可以避免 Flutter 的組件構(gòu)建的 build 方法中出現(xiàn)過(guò)多的元素和嵌套,影響代碼的可讀性。
以上就是Flutter 構(gòu)建一個(gè)常用的頁(yè)面框架的詳細(xì)內(nèi)容,更多關(guān)于Flutter 構(gòu)建頁(yè)面框架的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android通知欄微技巧一些需要注意的小細(xì)節(jié)
這篇文章主要介紹了Android通知欄微技巧,那些你所沒(méi)關(guān)注過(guò)的小細(xì)節(jié),小編把此文分享到腳本之家平臺(tái),需要的朋友可以參考下2018-04-04
kotlin實(shí)現(xiàn)快遞與號(hào)碼歸屬地查詢案例詳解
時(shí)間軸時(shí)一個(gè)很炫酷的效果,一般作用在物流信息上,我們同樣也可以作為一個(gè)學(xué)習(xí)對(duì)象去學(xué)習(xí)他的使用方法,同時(shí)呢,我們可以在線查詢我們的電話號(hào)碼歸屬地,巧用鍵盤(pán)的邏輯提升我們用戶體驗(yàn)2023-02-02
Android AlertDialog六種創(chuàng)建方式案例詳解
這篇文章主要介紹了Android AlertDialog六種創(chuàng)建方式案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
ListView的View回收引起的checkbox狀態(tài)改變監(jiān)聽(tīng)等問(wèn)題解決方案
之前講到了自定義Adapter傳遞給ListView時(shí),因?yàn)長(zhǎng)istView的View回收,需要注意當(dāng)ListView列表項(xiàng)中包含有帶有狀態(tài)標(biāo)識(shí)控件的問(wèn)題,感興趣的朋友可以祥看本文,或許會(huì)有意外的收獲哦2013-01-01
Android字體大小自適應(yīng)不同分辨率的解決辦法
這篇文章主要介紹了Android字體大小自適應(yīng)不同分辨率的解決辦法的相關(guān)資料,需要的朋友可以參考下2017-06-06

