使用Flutter实现一个走马灯布局的示例代码
走马灯是一种常见的效果,本文讲一下如何用PageView在Flutter里实现一个走马灯,效果如下,当前页面的高度比其它页面高,切换页面的时候有一个高度变化的动画。实现这样的效果主要用到的是PageView.builder部件。
首先创建一个IndexPage部件,这个部件用来放PageView,因为需要使用setState方法更新UI,所以它是stateful的。
import'package:flutter/material.dart'; classIndexPageextendsStatefulWidget{ @override _IndexPageStatecreateState()=>_IndexPageState(); } class_IndexPageStateextendsState{ @override Widgetbuild(BuildContextcontext){ returnScaffold( appBar:AppBar( elevation:0.0, backgroundColor:Colors.white, ), body:Column( children: [], ), ); } }
然后在部件内申明一个_pageIndex变量用来保存当前显示的页面的index,在initState生命周期里面初始化一个PageController用来配置PageView部件。
在body的Column里面创建一个PageView.builder,使用一个SizedBox部件指定PageView的高度,将controller设置为_pageController,在onPageChanged事件里将当前显示页面的index值赋值给_pageIndex变量。
int_pageIndex=0; PageController_pageController; @override voidinitState(){ super.initState(); _pageController=PageController( initialPage:0, viewportFraction:0.8, ); } body:Column( children:[ SizedBox( height:580.0, child:PageView.builder( itemCount:3, pageSnapping:true, controller:_pageController, onPageChanged:(intindex){ setState((){ _pageIndex=index; }); }, itemBuilder:(BuildContextctx,intindex){ return_buildItem(_pageIndex,index); }, ), ), ], ),
关键点:设置PageController的viewportFraction参数小于1,这个值是用来设置每个页面在屏幕上显示的比例,小于1的话,就可以在当前页面同时显示其它页面的内容了。
///Thefractionoftheviewportthateachpageshouldoccupy. ///Defaultsto1.0,whichmeanseachpagefillstheviewportinthescrollingdirection. finaldoubleviewportFraction;
实现_buildItem
接着实现_buildItem方法,这个方法就是返回PageView.builder里每一个页面渲染的内容,第一个参数activeIndex是当前显示在屏幕上页面的index,第二个参数index是每一项自己的index。
使用一个Center部件让内容居中显示,然后用一个AnimatedContainer添加页面切换时的高度变化的动画效果,切换页面的时候使用了setState方法改变了_pageIndex,Flutter重新绘制每一项。关键点在于判断当前页面是否为正在显示的页面,是的话它的高度就是500不是的话就是450。
_buildItem(activeIndex,index){ returnCenter( child:AnimatedContainer( curve:Curves.easeInOut, duration:Duration(milliseconds:300), height:activeIndex==index?500.0:450.0, margin:EdgeInsets.symmetric(vertical:20.0,horizontal:10.0), decoration:BoxDecoration( color:heroes[index].color, borderRadius:BorderRadius.all(Radius.circular(12.0)), ), child:Stack(), ), ); }
添加内容
然后给AnimatedContainer添加每一项的内容
child:Stack( fit:StackFit.expand, children:[ ClipRRect( borderRadius:BorderRadius.all( Radius.circular(12.0), ), child:Image.network( heroes[index].image, fit:BoxFit.cover, ), ), Align( alignment:Alignment.bottomCenter, child:Row( children: [ Expanded( child:Container( padding:EdgeInsets.all(12.0), decoration:BoxDecoration( color:Colors.black26, borderRadius:BorderRadius.only( bottomRight:Radius.circular(12.0), bottomLeft:Radius.circular(12.0), ), ), child:Text( heroes[index].title, textAlign:TextAlign.center, style:TextStyle( fontSize:20.0, fontWeight:FontWeight.bold, color:Colors.white, ), ), ), ) ], ), ), ], ),
实现指示器
然后实现页面的指示器,创建一个PageIndicator部件,需要传入pageCount表示总页数,以及currentIndex表示当前显示的页数索引。把所有指示器放在一个Row部件里,判断当前指示器的index是否为正在显示页面的index,是的话显示较深的颜色。
classPageIndicatorextendsStatelessWidget{ finalintpageCount; finalintcurrentIndex; constPageIndicator(this.currentIndex,this.pageCount); Widget_indicator(boolisActive){ returnContainer( width:6.0, height:6.0, margin:EdgeInsets.symmetric(horizontal:3.0), decoration:BoxDecoration( color:isActive?Color(0xff666a84):Color(0xffb9bcca), shape:BoxShape.circle, boxShadow:[ BoxShadow( color:Colors.black12, offset:Offset(0.0,3.0), blurRadius:3.0, ), ], ), ); } List_buildIndicators(){ List indicators=[]; for(inti=0;i 添加PageIndicator到SizedBox下面
封装Carousel
最后的最后优化一下代码,把部件封装一下,让它成为一个单独的部件,创建一个Carousel部件,对外暴露items和height两个属性,分别配置数据和高度。
classCarouselextendsStatefulWidget{ finalListitems; finaldoubleheight; constCarousel({ @requiredthis.items, @requiredthis.height, }); @override _CarouselStatecreateState()=>_CarouselState(); } class_CarouselStateextendsState{ int_pageIndex=0; PageController_pageController; Widget_buildItem(activeIndex,index){ finalitems=widget.items; returnCenter( child:AnimatedContainer( curve:Curves.easeInOut, duration:Duration(milliseconds:300), height:activeIndex==index?500.0:450.0, margin:EdgeInsets.symmetric(vertical:20.0,horizontal:10.0), decoration:BoxDecoration( color:items[index].color, borderRadius:BorderRadius.all(Radius.circular(12.0)), ), child:Stack( fit:StackFit.expand, children: [ ClipRRect( borderRadius:BorderRadius.all( Radius.circular(12.0), ), child:Image.network( items[index].image, fit:BoxFit.cover, ), ), Align( alignment:Alignment.bottomCenter, child:Row( children: [ Expanded( child:Container( padding:EdgeInsets.all(12.0), decoration:BoxDecoration( color:Colors.black26, borderRadius:BorderRadius.only( bottomRight:Radius.circular(12.0), bottomLeft:Radius.circular(12.0), ), ), child:Text( items[index].title, textAlign:TextAlign.center, style:TextStyle( fontSize:20.0, fontWeight:FontWeight.bold, color:Colors.white, ), ), ), ) ], ), ), ], ), ), ); } @override voidinitState(){ super.initState(); _pageController=PageController( initialPage:0, viewportFraction:0.8, ); } @override Widgetbuild(BuildContextcontext){ returnColumn( children: [ Container( height:widget.height, child:PageView.builder( pageSnapping:true, itemCount:heroes.length, controller:_pageController, onPageChanged:(intindex){ setState((){ _pageIndex=index; }); }, itemBuilder:(BuildContextctx,intindex){ return_buildItem(_pageIndex,index); }, ), ), PageIndicator(_pageIndex,widget.items.length), ], ); } } 之后在IndexPage部件里就只用实例化一个Carousel了,同时由于IndexPage不用管理部件状态了,可以将它变成StatelessWidget。
完整代码
import'package:flutter/material.dart'; classHero{ finalColorcolor; finalStringimage; finalStringtitle; Hero({ @requiredthis.color, @requiredthis.image, @requiredthis.title, }); } Listheroes=[ Hero( color:Color(0xFF86F3FB), image:"https://game.gtimg.cn/images/lol/act/img/skin/big22009.jpg", title:'寒冰射手-艾希', ), Hero( color:Color(0xFF7D6588), image:"https://game.gtimg.cn/images/lol/act/img/skin/big39006.jpg", title:'刀锋舞者-艾瑞莉娅', ), Hero( color:Color(0xFF4C314D), image:"https://game.gtimg.cn/images/lol/act/img/skin/big103015.jpg", title:'九尾妖狐-阿狸', ), ]; classCarouselextendsStatefulWidget{ finalListitems; finaldoubleheight; constCarousel({ @requiredthis.items, @requiredthis.height, }); @override _CarouselStatecreateState()=>_CarouselState(); } class_CarouselStateextendsState{ int_pageIndex=0; PageController_pageController; Widget_buildItem(activeIndex,index){ finalitems=widget.items; returnCenter( child:AnimatedContainer( curve:Curves.easeInOut, duration:Duration(milliseconds:300), height:activeIndex==index?500.0:450.0, margin:EdgeInsets.symmetric(vertical:20.0,horizontal:10.0), decoration:BoxDecoration( color:items[index].color, borderRadius:BorderRadius.all(Radius.circular(12.0)), ), child:Stack( fit:StackFit.expand, children: [ ClipRRect( borderRadius:BorderRadius.all( Radius.circular(12.0), ), child:Image.network( items[index].image, fit:BoxFit.cover, ), ), Align( alignment:Alignment.bottomCenter, child:Row( children: [ Expanded( child:Container( padding:EdgeInsets.all(12.0), decoration:BoxDecoration( color:Colors.black26, borderRadius:BorderRadius.only( bottomRight:Radius.circular(12.0), bottomLeft:Radius.circular(12.0), ), ), child:Text( items[index].title, textAlign:TextAlign.center, style:TextStyle( fontSize:20.0, fontWeight:FontWeight.bold, color:Colors.white, ), ), ), ) ], ), ), ], ), ), ); } @override voidinitState(){ super.initState(); _pageController=PageController( initialPage:0, viewportFraction:0.8, ); } @override Widgetbuild(BuildContextcontext){ returnColumn( children: [ Container( height:widget.height, child:PageView.builder( pageSnapping:true, itemCount:heroes.length, controller:_pageController, onPageChanged:(intindex){ setState((){ _pageIndex=index; }); }, itemBuilder:(BuildContextctx,intindex){ return_buildItem(_pageIndex,index); }, ), ), PageIndicator(_pageIndex,widget.items.length), ], ); } } classPageIndicatorextendsStatelessWidget{ finalintcurrentIndex; finalintpageCount; constPageIndicator(this.currentIndex,this.pageCount); Widget_indicator(boolisActive){ returnContainer( width:6.0, height:6.0, margin:EdgeInsets.symmetric(horizontal:3.0), decoration:BoxDecoration( color:isActive?Color(0xff666a84):Color(0xffb9bcca), shape:BoxShape.circle, boxShadow:[ BoxShadow( color:Colors.black12, offset:Offset(0.0,3.0), blurRadius:3.0, ), ], ), ); } List _buildIndicators(){ List indicators=[]; for(inti=0;i 至此,整个布局就完成了!:sunglasses:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。