题记
—— 执剑天涯,从你的点滴积累开始,所及之处,必精益求精。
Flutter是谷歌推出的最新的移动开发框架。
本实例运行效果如下 :
//启动函数void main() { runApp(RootApp());}//根目录class RootApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData(primaryColor: Colors.grey[200]), //默认启动的页面 home: HomePage(), ); }}
初始化创建一个 TabController 用来控制 TabBar 与 TabBarView的联动效果:
class HomePage extends StatefulWidget { @override StatecreateState() { return _ScrollHomePageState(); }}class _ScrollHomePageState extends State with SingleTickerProviderStateMixin { //在这里标签页面使用的是TabView所以需要创建一个控制器 TabController tabController; //页面初始化方法 @override void initState() { super.initState(); //初始化 tabController = new TabController(length: 3, vsync: this); } //页面销毁回调生命周期 @override void dispose() { tabController.dispose(); } ...}
对于页面的主体 使用了 Scaffold :
@overrideWidget build(BuildContext context) { //构建页面的主体 return Scaffold( //下拉刷新 body: RefreshIndicator( //可滚动组件在滚动时会发送ScrollNotification类型的通知 notificationPredicate: (ScrollNotification notifation) { //该属性包含当前ViewPort及滚动位置等信息 ScrollMetrics scrollMetrics = notifation.metrics; if (scrollMetrics.minScrollExtent == 0) { return true; } else { return false; } }, //下拉刷新回调方法 onRefresh: () async { //模拟网络刷新 等待2秒 await Future.delayed(Duration(milliseconds: 2000)); //返回值以结束刷新 return Future.value(true); }, child: buildNestedScrollView(), ), );}
RefreshIndicator 是一个下拉刷新组件,用来触发下拉刷新效果,直接嵌套NestedScrollView滑动布局来使用
//NestedScrollView 的基本使用Widget buildNestedScrollView() { //滑动视图 return NestedScrollView( //配置可折叠的头布局 headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { return [buildSliverAppBar()]; }, //页面的主体内容 body: buidChildWidget(), );}
NestedScrollView 中包含两部分,一部分是折叠的头部,使用SliverAppBar来实现,另一部分是滑动切换的页面主体 使用 TabBarView 来实现
//通常在用到 PageView + BottomNavigationBar 或者 TabBarView + TabBar 的时候 //大家会发现当切换到另一页面的时候, 前一个页面就会被销毁, 再返回前一页时, 页面会被重建, //随之数据会重新加载, 控件会重新渲染 带来了极不好的用户体验. //由于TabBarView内部也是用的是PageView, 因此两者的解决方式相同 //页面的主体内容 Widget buidChildWidget() { return TabBarView( controller: tabController, children: <Widget>[ ItemPage1(1), ItemPage1(2), ItemPage1(3), ], ); }
SliverAppBar 的实现如下:
//flexibleSpace可折叠的内容区域buildSliverAppBar() { return SliverAppBar( title: buildHeader(), //标题居中 centerTitle: true, //当此值为true时 SliverAppBar 会固定在页面顶部 //当此值为fase时 SliverAppBar 会随着滑动向上滑动 pinned: true, //当值为true时 SliverAppBar设置的title会随着上滑动隐藏 //然后配置的bottom会显示在原AppBar的位置 //当值为false时 SliverAppBar设置的title会不会隐藏 //然后配置的bottom会显示在原AppBar设置的title下面 floating: false, //当snap配置为true时,向下滑动页面,SliverAppBar(以及其中配置的flexibleSpace内容)会立即显示出来, //反之当snap配置为false时,向下滑动时,只有当ListView的数据滑动到顶部时,SliverAppBar才会下拉显示出来。 snap: false, elevation: 0.0, //展开的高度 expandedHeight: 380, //AppBar下的内容区域 flexibleSpace: FlexibleSpaceBar( //背景 //配置的是一个widget也就是说在这里可以使用任意的 //Widget组合 在这里直接使用的是一个图片 background: buildFlexibleSpaceWidget(), ), bottom: buildFlexibleTooBarWidget(), );}
SliverAppBar中有三部分,第一部分是标题部分,通过title属性配置,代码如下:
//构建SliverAppBar的标题titlebuildHeader() { //透明组件 return Container( width: double.infinity, padding: EdgeInsets.only(left: 10), height: 38, decoration: BoxDecoration( color: Colors.white, border: Border.all(color: Colors.white), borderRadius: BorderRadius.circular(30), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.search_rounded, size: 18, ), SizedBox( width: 4, ), Text( "搜索", style: TextStyle( fontSize: 14, ), ), ], ), );}
第二部分就是用来折叠部分的轮播图,通过 flexibleSpace 属性配置的FlexibleSpaceBar中配置,代码如下:
buildFlexibleSpaceWidget() { return Column( children: [ Container( height: 240, child: BannerHomepage(isTitle: false,), ), Container( child: Row( children: [ Expanded( child: Container( height: 120, color: Colors.blueGrey, child: Image.asset("images/banner5.jpeg"), ), ), Expanded( child: Container( color: Colors.brown, height: 120, child: Image.asset("images/banner6.jpeg"), ), ), ], ), ) ], ); }
BannerHomepage 的实现在这里 Flutter Pageview 实现的轮播图
第三部分就是通过 bottom 配置的 TabBar 标签栏,在这里结合 PreferredSize 来使用,代码如下:
//[SliverAppBar]的bottom属性配制 Widget buildFlexibleTooBarWidget() { //[PreferredSize]用于配置在AppBar或者是SliverAppBar //的bottom中 实现 PreferredSizeWidget return PreferredSize( //定义大小 preferredSize: Size(MediaQuery.of(context).size.width, 44), //配置任意的子Widget child: Container( alignment: Alignment.center, child: Container( color: Colors.grey[200], //随着向上滑动,TabBar的宽度逐渐增大 //父布局Container约束为 center对齐 //所以程现出来的是中间x轴放大的效果 width: MediaQuery.of(context).size.width, child: TabBar( controller: tabController, tabs: [ new Tab( text: "标签一", ), new Tab( text: "标签二", ), new Tab( text: "标签三", ), ], ), ), ), ); }
点击查看原文来获取源码。