Flutter实现App功能引导页
app功能介绍页,主要是由介绍app功能的几张图片和当前页指示符组成,如下效果
我们来一步一步实现上面的界面,左右滑动切换显示功能页,这个可以通过pageview来实现,底部的指示符半透明覆盖在pageview上,开发过android同学知道可以用framelayout布局来实现,flutter上也有类似的控件stack,我们先完成骨架代码
// an highlighted block void main() => runapp(app()); class app extends statelesswidget { @override widget build(buildcontext context) { return materialapp( title: 'title', theme: themedata( primaryswatch: colors.blue, ), home: appfuncbrowse(), ); } } class appfuncbrowse extends statefulwidget { @override _appfuncbrowsestate createstate() { return _appfuncbrowsestate(); } } class _appfuncbrowsestate extends state<appfuncbrowse> { @override widget build(buildcontext context) { return scaffold( body: container( color: colors.white, child: stack( children: <widget>[ ], ), )); } }
appfuncbrowse 就是我们功能介绍页,上面编译运行功能后,我们先来着手实现功能介绍页面切换,pageview主要有两类构建函数,一个是懒加载页面(适合页面很多时使用),一个是适合少量页面的一次性加载的,我们实例显示3个页面,就简单的用这种方式, pageview用到了pagecontroller用来控制显示哪一页,先定义它
class _appfuncbrowsestate extends state<appfuncbrowse> { pagecontroller _pagecontroller = pagecontroller(); //省略... }
有了pagecontroller我们就可以创建pageview,代码如下
widget _createpageview() { return pageview( controller: _pagecontroller, onpagechanged: (pageindex) { setstate(() { _pageindex = pageindex; print(_pagecontroller.page); print(pageindex); }); }, children: <widget>[ container( color: colors.blue, child: center( child: text('page 1'), ), ), container( color: colors.red, child: center( child: text('page 2'), ), ), container( color: colors.green, child: center( child: text('page 3'), ), ), ], ); } @override widget build(buildcontext context) { // todo: implement build return scaffold( body: container( color: colors.white, // margin: edgeinsets.only(top: mediaquery.of(context).padding.top), child: stack( children: <widget>[ _createpageview(), ], ), )); }
到这里我们完成了一个可以左右滑动的功能页, 每页的内容大家可以自定义,一般是一张图片,我这里为了简单起见直接显示text,到目前为止我们离目标还缺一个页码指示器,学过ios的非常熟悉,需要用到uipagecontrol,可惜的是到目前为止flutter还没提供该原生控件,我们只能自己实现它,其实也很简单,代码如下:
_createpageindicator() { return opacity( opacity: 0.7, child: align( alignment: fractionaloffset.bottomcenter, child: container( margin: edgeinsets.only(bottom: 60), height: 40, width: 80, padding: edgeinsets.all(0), decoration: boxdecoration( color: colors.grey, //.withalpha(128), borderradius: borderradius.all(const radius.circular(6.0))), child: gesturedetector( behavior: hittestbehavior.translucent, ontapup: (detail) => _handlepageindicatortap(detail), child: row( key: _pageindicatorkey, mainaxisalignment: mainaxisalignment.spaceevenly, mainaxissize: mainaxissize.min, children: <widget>[ _dotwidget(0), _dotwidget(1), _dotwidget(2), ]), ), ), ), ); }
咋一看感觉好复杂,其实也很简单,学过设计模式的同学可能看出和装饰者模式一样,widget层次结构就类似这种模式,这也是flutter的强大之处,用现有的控件可以组合出各种效果,首先我们用opacity控件来封装子控件,设置opacity使其成为半透明,然后使用align控制其底部对齐,为了使其距离底部一段距离,还需要container来包装,为了实现圆角的container,强大的decoration属性就登场了,它能实现各种装饰效果,这里我们实现的效果是灰色背景和圆角。
现在讲完了这些装修,就剩下三个点了,三个点在同一水平线上,所以我们用row来做为父控件,row有几个属性值注意
mainaxisalignment 主轴对齐方式,row的主轴是水平方向,
crossaxisalignment 主轴的垂直方向对齐方式,row的主轴是水平方向,所以该字段表示上下对齐方式
mainaxissize 主轴方向的大小,对row来说即宽度
与row对应的控件是column,其对应上面属性刚好相反
上面属性的其他值可以自己试验,我们这里用到
mainaxisalignment: mainaxisalignment.spaceevenly, mainaxissize: mainaxissize.min,
表示子控件左右(包含与父控件)间距相同,并且宽度保持最小,最后我们来实现三个点,当前页的对应的点颜色需要和其他未显示的页有所区别,我们需要记录当前是第几页,从而使对应的第几个点highlight显示
class _appfuncbrowsestate extends state<appfuncbrowse> { pagecontroller _pagecontroller = pagecontroller(); int _pageindex = 0;
然后就可以创建点了
_dotwidget(int index) { return container( width: 10, height: 10, decoration: boxdecoration( shape: boxshape.circle, color: (_pageindex == index) ? colors.white70 : colors.black12)); }
滑动pageview,需要更新_pageindex,从而对应的更新当前页对应的点的颜色
return pageview( controller: _pagecontroller, onpagechanged: (pageindex) { setstate(() { _pageindex = pageindex; print(_pagecontroller.page); print(pageindex); }); },
onpagechanged就是pageview换页的事件,这里需要调用setstate从而使widget重建更新当前页的点颜色
到这里为止就剩下点击点更新pageview的功能了,我们来实现点击功能,就是监听点击手势,代码如下
_handlepageindicatortap(tapupdetails detail) { renderbox renderbox = _pageindicatorkey.currentcontext.findrenderobject(); size widgesize = renderbox.paintbounds.size; offset tapoffset = renderbox.globaltolocal(detail.globalposition); if (tapoffset.dx > widgesize.width / 2) { _scrolltonextpage(); } else { _scrolltopreviouspage(); } }
手势有一个属性behavior需要值得注意,默认值为defertochild,具体取值如下:
behavior: hittestbehavior.translucent 控制响应的点击区域:
translucent 表示整个区域,被遮挡的子视图也能响应
opaque 表示整个区域,被遮挡的子视图不能响应
defertochild 表示点击到子视图才响应,手势默认behavior
需要实现点击左半部分向前翻页,点击后半部分向后翻页,我们需要判断当前点击的区域,这就需要找到row的renderobject来获取控件区域,然后获取点击相对控件的偏移来确定,具体见代码,_pageindicatorkey就是传给row的key,具体定义如下
class _appfuncbrowsestate extends state<appfuncbrowse> { pagecontroller _pagecontroller = pagecontroller(); int _pageindex = 0; globalkey<_appfuncbrowsestate> _pageindicatorkey = globalkey();
现在只剩下前后翻页的代码了,代码如下
_scrolltopreviouspage() { if (_pageindex > 0) { _pagecontroller.animatetopage(_pageindex - 1, duration: const duration(milliseconds: 200), curve: curves.ease); } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。