首頁實現(xiàn)總結
目錄
- 根布局
- Banner圖
- AppBar
- 卡片布局
根布局
根布局使用Scaffold
掉瞳,這個是material包下的組件,它是一個路由頁的骨架浪漠,可以非常容易的拼裝出一個完整的頁面陕习。進入首頁先顯示加載,通過網(wǎng)絡訪問得到數(shù)據(jù)后關閉址愿,還要支持下拉刷新的功能该镣。
技術點:
MediaQuery.removePadding移除系統(tǒng)欄Padding
RefreshIndicator控制下拉刷新。ListView滾動的時候响谓,并且檢查滾動的深度為0损合,防止子View的滾動造成性能損耗。
ListView利用顯示的自列表來構造 List<Widget>
娘纷。此構造函數(shù)適合于具有少量子元素的列表視圖嫁审,因為構造列表需要為可能顯示在列表視圖中的每個子元素執(zhí)行工作,而不僅僅是那些實際可見的子元素赖晶。
_handleRefresh 是Flutter中的異步函數(shù)土居,在未來某個時刻執(zhí)行。主要用來獲取網(wǎng)絡數(shù)據(jù)嬉探。
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xfff2f2f2),
body: LoadingContainer(// 加載頁面
isLoading: _loading,// 控制加載頁面
child: Stack(// ListView與AppBar 類似Stack結構擺放
children: <Widget>[
MediaQuery.removePadding(// 去掉系統(tǒng)欄的Padding
// 去掉頂部屏幕適配
removeTop: true,
context: context,
child: RefreshIndicator(// 下拉刷新
child: NotificationListener(
onNotification: (scrollNotification) {// 監(jiān)聽下拉的狀態(tài)
if (scrollNotification is ScrollUpdateNotification &&
scrollNotification.depth == 0) {
// 優(yōu)化, 防止scrollNotification=0的時候也監(jiān)聽擦耀,只有在ScrollUpdateNotification更新的時候才監(jiān)聽并且只監(jiān)聽ListView的滾動滾動且是列表滾動的時候
_onScroll(scrollNotification.metrics.pixels);
}
},
child: _listView,
),
onRefresh: _handleRefresh),
),
_appBar,
],
)
)
);
}
Future<Null> _handleRefresh() async {// Fullter中的異步,返回一個Future
try {
HomeModel mode = await HomeDao.fetch();
setState(() {// 更新Model
localNavList = mode.localNavList;
subNavList = mode.subNavList;
gridNavModel = mode.gridNav;
salesBoxModel = mode.salesBox;
bannerList = mode.bannerList;
_loading = false;// 顯示視圖
});
} catch (e) {
print(e);
_loading = false;
}
return null;
}
載入布局LoadingContainer:
此Widget由外部isLoading控制顯示隱藏涩堤,內(nèi)部持有子Widget的引用眷蜓,cover控制是否覆蓋整個頁面@override Widget build(BuildContext context) { return !cover // // 是否覆蓋整個頁面 ? isLoading ? _loadingView : child : Stack( children: <Widget>[child, isLoading ? _loadingView : null], ); }
1. Banner圖
引入flutter_swiper
庫。進入到pubspec.yaml文件里添加胎围,同時吁系,引入三方庫也是一樣。圖片通過網(wǎng)絡加載白魂。
pagination: SwiperController
用于控制 Swiper的index屬性, 停止和開始自動播放. 通過 new SwiperController()
創(chuàng)建一個SwiperController實例汽纤,并保存,以便將來能使用福荸。
Widget get _banner {
return Container(// Container設置固定高度
height: 160,
child: Swiper(//第三方控件
itemCount: bannerList.length,// 長度
autoplay: true,// 開啟自動播放
itemBuilder: (BuildContext context, int index) {
return GestureDetector(// 返回一個可以點擊的圖片
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
CommonModel model = bannerList[index];
return WebView(
url: model.url,
title: model.title,
hideAppBar: model.hideAppBar,
);
}),
);
},
child: Image.network(bannerList[index].icon, fit: BoxFit.fill),
);
},
pagination: SwiperPagination(), // 指示器
),
);
}
2.AppBar
流程分析:AppBar 在下滑的過程中透明度發(fā)生改變蕴坪,AppBar可以選擇城市,進入搜索頁面。
decoration(裝飾器)DecoratedBox可以在其子widget繪制前(或后)繪制一個裝飾Decoration(如背景背传、邊框呆瞻、漸變等)。主要由外部的appBarAlpha值控制透明度径玖。
控制透明度邏輯:監(jiān)聽ListView滑動的距離痴脾,滑動過程中不斷改變透明值,通過setState()更新視圖梳星。
Widget get _appBar {
return Column(
// 上面輸入赞赖,下面陰影
children: <Widget>[
Container(
decoration: BoxDecoration(
gradient: LinearGradient(//線性漸變
// AppBar 漸變遮罩背景, 由透明到
colors: [Color(0x66000000), Colors.transparent],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
)),
child: Container(
padding: EdgeInsets.fromLTRB(0, 30, 0, 0),
height: 80.0,
decoration: BoxDecoration(
color:
Color.fromARGB((appBarAlpha * 255).toInt(), 255, 255, 255)
),
child: SearchBar(
searchBarType: appBarAlpha > 0.2
? SearchBarType.homeLight
: SearchBarType.home,
inputBoxClick: _jumpToSearch,
speakClick: _jumpToSpeak,
defaultText: SEARCH_BAR_DEFAULT_TEXT,
leftButtonClick: () {},
),
),
),
Container(
// 陰影設置
height: appBarAlpha > 0.2 ? 0.5 : 0,
decoration: BoxDecoration(
boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 0.5)]),// 設置陰影
)
],
);
}
// 滾動的邏輯
_onScroll(offset) {
var alpha = offset / APPBAR_SCROLL_OFFSET;
if (alpha < 0) {
// alpha 值的保護
alpha = 0;
} else if (alpha > 1) {
alpha = 1;
}
setState(() {
appBarAlpha = alpha;
});
print(appBarAlpha);
}
SeacherBar
對InputDecoration進行了封裝,擁有多種成員函數(shù)冤灾,是否禁止搜索薯定、按鈕隱藏、背景顏色瞳购、函數(shù)回調(diào)等多種功能。
final void Function() leftButtonClick;
將函數(shù)從其他地方傳入亏推,加大擴展性学赛。
_wrapTap()
封裝函數(shù)對可點擊的按鈕進行封裝
ValueChanged<String>
文本內(nèi)容變換監(jiān)聽,用于改變按鈕的樣式
優(yōu)化:原因:由于每次文本變化需要從網(wǎng)絡拉取數(shù)據(jù)吞杭,可能造成用戶輸入的內(nèi)容不是真正需要顯示的內(nèi)容盏浇。
解決:在網(wǎng)絡請求方法保存keyword
。檢查請求來的keyword是否一樣芽狗。檢查Widget重繪的次數(shù)绢掰,并且解決BUG。_wrapTap(Widget child, void Function() callback) { return GestureDetector( onTap: () { if (callback != null) callback(); }, child: child, ); } SearchDao.fetch(url, text).then((SearchModel model) { // 只有當當前輸入的內(nèi)容與服務端返回的內(nèi)容一致才渲染 if (model.keyword == keyword) {// 檢驗keyword setState(() { searchModel = model; }); } }).catchError((e) { print(e); });
3. 卡片布局
布局分成三部分童擎,再將其中一部分分為滴劲,一個大卡片兩個小卡片。
PhysicalModel 實現(xiàn)卡片圓角
FractionallySizedBox:撐滿父布局顾复,F(xiàn)ractionallySizedBox控件會根據(jù)現(xiàn)有空間班挖,來調(diào)整child的尺寸,所以說child就算設置了具體的尺寸數(shù)值芯砸,也不起作用萧芙。
@override
Widget build(BuildContext context) {
return PhysicalModel(//
color: Colors.transparent,
borderRadius: BorderRadius.circular(6),// 圓角
clipBehavior: Clip.antiAlias, // 裁切
child: Column(
children: _gridNavItems(context),
),
);
}
4. 底部卡片
頂部是可點擊的圖片與文本,下面是三組卡片假丧,第一組高度較大
Image: BoxFit.fill:會拉伸填充滿顯示空間双揪,圖片本身長寬比會發(fā)生變化,圖片會變形包帚。
width: MediaQuery.of(context).size.width:獲得手機屏幕寬度渔期,需要計算padding