往期
上期回顧
上期主要完成環(huán)境的搭建和部署,最終在jetpack.net.cn地址上呈現(xiàn)心例,這期我們就開始搭建主頁,構(gòu)建一個(gè)可以兼容三端(Android、Ios楚午、Web)的主頁。
開始
在lib文件夾下創(chuàng)建home.dart文件尿招,如圖
打開文件矾柜,輸入st出來的提示選擇第一個(gè),如圖
這樣就自動(dòng)生成一個(gè)完整的模版代碼泊业,一個(gè)小小的技巧把沼,請(qǐng)笑納。最后命名PageHome吁伺,這里簡單說下命名規(guī)則饮睬,只是建議,頁面以Page開頭篮奄,非頁面以Widget開頭捆愁,這樣在其他地方引用的時(shí)候容易尋找,提高查找效率窟却。接下來昼丑,我們就用一個(gè)兼容三端的組件來構(gòu)建主頁,不賣關(guān)子夸赫,我們使用Material主題菩帝,因?yàn)槲沂茿ndroid開發(fā),更喜歡這個(gè)主題,我們?cè)谛略鼋M件的時(shí)候總感覺很麻煩呼奢,其實(shí)也有個(gè)小技巧宜雀,如圖
選第一個(gè)就可以生成一個(gè)包裹組件,繼續(xù)看圖
然后將widget改為Material握础。然后添加Padding辐董,Scaffold組件如圖
Padding的用意很簡單,加一個(gè)左右邊距禀综,而在web简烘,和手機(jī)上的邊距是不一樣的,不能固定死定枷,我畫個(gè)圖展示展示下孤澎,如圖
所以使用ResponsiveWidget判斷是否是小屏幕,如果屏幕變小則使用小邊距來適配依鸥,這里注意是小屏幕亥至,不是針對(duì)的Android或者蘋果手機(jī),要注意不能混淆理解贱迟,瀏覽器也可以縮小到手機(jī)屏幕大小對(duì)吧姐扮,要理解ResponsiveWidget請(qǐng)看前期博客:一個(gè)Flutter widget自動(dòng)適配不同UI到Web、Android
還有一個(gè)ScreenUtil衣吠,這個(gè)你可以理解為相當(dāng)于Android中的dp單位茶敏,他負(fù)責(zé)的就是等比適配,不會(huì)讓大小分辨率的屏幕看起來差距很大缚俏。這個(gè)工具有個(gè)初始化的過程
ScreenUtil.instance = ScreenUtil.getInstance()..init(context);
而且初始化必須在MaterialApp包裹中惊搏,為什么呢?因?yàn)槲覀兪褂昧薓ediaQuery動(dòng)態(tài)獲取屏幕的寬高忧换,詳細(xì)請(qǐng)看官方文檔https://api.flutter.dev/flutter/widgets/MediaQuery-class.html里面講的很清楚:
WidgetsApp and MaterialApp, which introduce a MediaQuery and keep it up to date with the current screen metrics as they change.
目前WidgetsApp和MaterialApp實(shí)現(xiàn)了MediaQuery的邏輯恬惯,所以你不在MaterialApp中初始化是會(huì)報(bào)錯(cuò)地,知道了吧亚茬。接下來看下實(shí)際運(yùn)行效果酪耳,我們將內(nèi)容設(shè)置成紅色來看,如圖
屏幕縮小后如圖
這就是我們要的效果刹缝,其實(shí)已經(jīng)有了兩個(gè)框架封裝的很完整碗暗,但前期我們?yōu)槭裁礇]有選擇引用它們呢,而且他們實(shí)現(xiàn)的似乎更合理梢夯,請(qǐng)看他們的代碼風(fēng)格如圖
一個(gè)ScreenTypeLayout這個(gè)組件實(shí)現(xiàn)了四個(gè)屏幕的適配言疗,框架引用:
responsive_builder: ^0.1.5
我為什么沒有選擇直接使用呢?其實(shí)我們?cè)诿靼灼渲性砗蟠_實(shí)可以直接引用颂砸,但我還是建議自己寫一遍噪奄,通過自己的實(shí)現(xiàn)死姚,更清楚其中的道理不是嗎。前期的學(xué)習(xí)過程中勤篮,我們盡量的不去引用知允,選擇自己實(shí)現(xiàn),也是加快提高自己的一個(gè)辦法叙谨。多多學(xué)習(xí),多多練習(xí)保屯。接下來我們實(shí)現(xiàn)Scaffold部分手负,Scaffold這個(gè)組件太好用了,先看下它原本的樣子
姑尺,它就像一個(gè)大的容器竟终,幫住我們組織好了各個(gè)組件的位置,是對(duì)一個(gè)基礎(chǔ)UI的高度抽象切蟋。
簡單分析下它的參數(shù)源碼
其實(shí)這里最主要的三個(gè)常用的部分appBar统捶、body、floatingActionButton柄粹,正好是我們構(gòu)建一個(gè)UI的常用構(gòu)造喘鸟,所以這個(gè)組件基本是我們使用頻率很高的一個(gè)組件,請(qǐng)認(rèn)真學(xué)習(xí)它驻右,更詳細(xì)的學(xué)習(xí)什黑,請(qǐng)看https://api.flutter.dev/flutter/material/Scaffold-class.html,這里面有幾個(gè)例子堪夭,認(rèn)真學(xué)習(xí)哦愕把,我們現(xiàn)在通過它,來構(gòu)建我們的主頁代碼森爽,請(qǐng)看效果圖:
我們先構(gòu)建了AppBar的內(nèi)容恨豁,怎么實(shí)現(xiàn)的呢,請(qǐng)看代碼
@override
Widget build(BuildContext context) {
ScreenUtil.instance = ScreenUtil.getInstance()..init(context);
return Material(
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: !ResponsiveWidget.isSmallScreen(context)
? (ScreenUtil.getInstance().setWidth(108))
: (ScreenUtil.getInstance().setWidth(6))),
child: Scaffold(
appBar: AppBar(
title: _buildTitle(), /// 左邊標(biāo)題
backgroundColor: Color(0xFFf1f3f4),
actions: !ResponsiveWidget.isSmallScreen(context)
? _buildActions(context)
: null, /// 右邊的menu菜單爬迟,這里在小屏幕不顯示橘蜜。
),
body: Center(child: Text('You have pressed the button $_count times.')),
),
));
}
Widget _buildTitle() {
return RichText(
text: TextSpan(
// Note: Styles for TextSpans must be explicitly defined.
// Child text spans will inherit styles from parent
style: TextStyle(
fontSize: 14.0,
color: Colors.black,
),
children: <TextSpan>[
TextSpan(
text: "Jetpack",
style: TextStyles.logo,
),
TextSpan(
text: ".net.cn",
style: TextStyles.logo.copyWith(
color: Color(0xFF50AFC0),
),
),
],
),
);
}
_buildActions(BuildContext context) {
return <Widget>[
MaterialButton(
child: Text(
'Home',
style: TextStyles.menuItem,
),
onPressed: () {
if (ResponsiveWidget.isSmallScreen(context)) Navigator.pop(context);
},
),
MaterialButton(
child: Text(
'About',
style: TextStyles.menuItem,
),
onPressed: () {
if (ResponsiveWidget.isSmallScreen(context)) Navigator.pop(context);
},
),
];
}
分別通過_buildTitle,_buildActions 實(shí)現(xiàn)左邊的標(biāo)題雕旨,右邊的菜單按鈕扮匠,里面有個(gè)細(xì)節(jié):
大屏幕的返回_buildActions,小屏幕直接隱藏凡涩,這里你可能有疑問棒搜,為什么不用drawer實(shí)現(xiàn)菜單?drawer適合小屏幕實(shí)現(xiàn)活箕,而大屏慕我們就要充分利用空間力麸,盡可能的操作簡單,一目了然。下面我們來實(shí)現(xiàn)克蚂,小屏幕的drawer闺鲸,請(qǐng)看代碼
///省略部分代碼
Scaffold(
///省略部分代碼
drawer: _buildDrawer(context),
),
_buildDrawer(BuildContext context) {
return ResponsiveWidget.isSmallScreen(context)
? Drawer(
child: ListView(
padding: const EdgeInsets.all(20),
children: _buildActions(context),
),
)
: null;
}
給Scaffold添加一個(gè)drawer組件,這里用ListView把剛才的_buildActions組件再放進(jìn)來埃叭,原來是以橫向展示摸恍,這次因?yàn)長istView默認(rèn)是縱向,所以就是上下的展示方式赤屋,運(yùn)行項(xiàng)目后立镶,如圖:
未展開的樣子,標(biāo)題右邊的菜單消失
展開的樣子类早,這就是我們要實(shí)現(xiàn)的小屏幕的樣子媚媒。也符合手機(jī)的使用習(xí)慣。
有沒有發(fā)現(xiàn)這個(gè)東東顯示看不清楚涩僻,接下來我們改造它缭召,可我沒做過,我怎么查呢逆日?我這里分享我的經(jīng)驗(yàn)嵌巷,首先你要明白這里的關(guān)鍵字是什么,當(dāng)我鼠標(biāo)停留在上面的時(shí)候提示如圖
是一個(gè)navigation menu室抽,所以我會(huì)在google里搜索晴竞,flutter scaffold drawer navigation menu,這么幾個(gè)關(guān)鍵字狠半,然后搜到的如圖
噩死,第一個(gè)是官方文檔如何添加,第二個(gè)是custom drawer神年,自定義的已维,有可能講到了如何修改,所以我就點(diǎn)進(jìn)去尋找
看到?jīng)]已日,還是很好找的哈垛耳,然后copy過來代碼,發(fā)現(xiàn)它用的圖不對(duì)飘千,我們修改下堂鲜,如圖,這樣是不是看著舒服了护奈。
接下來就是給Scaffold的body填充內(nèi)容了缔莲,先看實(shí)現(xiàn)的效果
Home頁面黃色
About頁藍(lán)色
點(diǎn)擊切換,具體實(shí)現(xiàn)請(qǐng)看下面代碼
int _selectedDrawerIndex = 0;
///定義一個(gè)坐標(biāo)值
/// 添加動(dòng)態(tài)的body _getDrawerItemWidget
child: Scaffold(
body: _getDrawerItemWidget(_selectedDrawerIndex),
),
/// 省略部分代碼
/// 根據(jù)一個(gè)index值霉旗,來確定加載哪個(gè)頁面
_getDrawerItemWidget(int selectedDrawerIndex) {
switch(selectedDrawerIndex){
case 0:
return WidgetMenuHome();
break;
case 1:
return WidgetMenuAbout();
break;
}
}
/// 在_buildActions函數(shù)中的組件onPressed的時(shí)候調(diào)用痴奏,這樣就可以更新UI
setState(() {
_selectedDrawerIndex = 0;
});
好了這期我們就先這樣蛀骇,學(xué)習(xí)到此結(jié)束,下期繼續(xù)读拆。
總結(jié)
這次主要是對(duì)Scaffold的使用擅憔,以及如何構(gòu)建不同平臺(tái)的UI,確切說是不同寬度屏幕的構(gòu)建檐晕,實(shí)現(xiàn)了大屏幕的Menu菜單暑诸,和小屏幕的Drawer菜單顯示,而且沒有考慮分包辟灰,下期將進(jìn)行分包設(shè)計(jì)屠列,并往頁面里面填充內(nèi)容,來構(gòu)建我們的需求UI 實(shí)現(xiàn)一個(gè)Flutter Jetpack伞矩,并把之前做的Android Jetpack也挪過來。
項(xiàng)目開源鏈接
Android Jetpack WebSite
Flutter Jetpack WebSite
Flutter Jetpack Github Source Code
Android Jetpack Github Source Code