用Flutter实现一个类似于轮播图的效果
效果图:
过程脑洞自补!!!
类似于上图的效果,我们应该能想到用PageView来实现,但是思来想去,PageView官方并没有给自定义的参数,就是我们需要的下面的指示器的效果。现在我就来说下一下怎么来实现他。
在Flutter的字典中,有一句话说的好,“万事不决Stack”好了我们就套用Stack布局来实现图片和图片标题的内容嵌套。
首先我们先建立一个存放图片的类,本来想用api来,但是发现还是用代码来实现吧
class Hero {final Color color;final String image;final String title;Hero({@required this.color,@required this.image,@required this.title,});
}
这里我们建立了一个Hero类,里面有三个必填参数。然后我们根据这个类来建立我们所需要的轮播图的内容。
为了方便点我直接用List来显示
List heroes = [Hero(color: Color(0xFF4C314D),image: "https://game.gtimg.cn/images/lol/act/img/skin/big38005.jpg",title: '虚空行者-卡萨丁'),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: '九尾妖狐-阿狸',),Hero(color: Color(0xFF4C314D),image: "https://game.gtimg.cn/images/lol/act/img/skin/big51004.jpg",title: '皮城女警-凯瑟琳'),
];
然后进入我们的主要部分,创建一个有状态的组件Carousel,然后他接收连个必填的参数,上面我们定义的List内容列表,高度。然后在他的State里面用PageView和我们自定义的一个**PageIndicator(指示器)**来实现。
class Carousel extends StatefulWidget {final List items;final double height;const Carousel({@required this.items,@required this.height,});@override_CarouselState createState() => _CarouselState();
}class _CarouselState extends State<Carousel> {int _pageIndex = 0;PageController _pageController;@overridevoid initState() {super.initState();_pageController = PageController(initialPage: 0,viewportFraction: 0.8,);}@overrideWidget build(BuildContext context) {return Column(children: <Widget>[Container(height: widget.height,child: PageView.builder(pageSnapping: true,itemCount: heroes.length,controller: _pageController,onPageChanged: (int index) {setState(() {_pageIndex = index;});},itemBuilder: (BuildContext context, int index) {return _buildItem(_pageIndex, index);},),),PageIndicator(_pageIndex, widget.items.length),],);}Widget _buildItem(activeIndex, index) {final items = widget.items;return Center(child: AnimatedContainer(curve: Curves.easeInOut,duration: Duration(milliseconds: 300),height: activeIndex == index ? 500.0 : 450.0,margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 5.0),decoration: BoxDecoration(color: items[index].color,borderRadius: BorderRadius.all(Radius.circular(12.0)),),child: Stack(fit: StackFit.expand,children: <Widget>[ClipRRect(borderRadius: BorderRadius.all(Radius.circular(12.0),),child: Image.network(items[index].image,fit: BoxFit.cover,),),Align(alignment: Alignment.bottomCenter,child: Row(children: <Widget>[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,),),),)],),),],),),);}
}
分析代码:我们在组件内部用一个_pageIndex变量来保存当前显示页面的index,很自然的我们要在initState
声明周期里面要初始化一个PageController
用来控制PageView组件。在在里面的内容我就不多说了,这是最简单的PageView.builder的使用。
再补充一点: 设置 PageController 的 viewportFraction 参数小于 1,这个值是用来设置每个页面在屏幕上显示的比例,小于 1 的话,就可以在当前页面同时显示其它页面的内容了。
然后下面是我们自定义封装的指示器组件
class PageIndicator extends StatelessWidget {final int currentIndex;final int pageCount;const PageIndicator(this.currentIndex, this.pageCount);@overrideWidget build(BuildContext context) {return Row(mainAxisAlignment: MainAxisAlignment.center,children: _buildIndicators(),);}Widget _indicator(bool isActive) {return Container(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<Widget> _buildIndicators() {List<Widget> indicators = [];for (int i = 0; i < pageCount; i++) {indicators.add(i == currentIndex ? _indicator(true) : _indicator(false));}return indicators;}
}
分析代码: PageIndicator 组件我们接受了两个参数,一个是当前页面的索引,还有一个是页面的总数。多的话我也不说了,这里是很简单的逻辑。
最后我贴一下完整代码
import 'package:flutter/material.dart';class Hero {final Color color;final String image;final String title;Hero({@required this.color,@required this.image,@required this.title,});
}List heroes = [Hero(color: Color(0xFF4C314D),image: "https://game.gtimg.cn/images/lol/act/img/skin/big38005.jpg",title: '虚空行者-卡萨丁'),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: '九尾妖狐-阿狸',),Hero(color: Color(0xFF4C314D),image: "https://game.gtimg.cn/images/lol/act/img/skin/big51004.jpg",title: '皮城女警-凯瑟琳'),
];class Carousel extends StatefulWidget {final List items;final double height;const Carousel({@required this.items,@required this.height,});@override_CarouselState createState() => _CarouselState();
}class _CarouselState extends State<Carousel> {int _pageIndex = 0;PageController _pageController;@overridevoid initState() {super.initState();_pageController = PageController(initialPage: 0,viewportFraction: 0.8,);}@overrideWidget build(BuildContext context) {return Column(children: <Widget>[Container(height: widget.height,child: PageView.builder(pageSnapping: true,itemCount: heroes.length,controller: _pageController,onPageChanged: (int index) {setState(() {_pageIndex = index;});},itemBuilder: (BuildContext context, int index) {return _buildItem(_pageIndex, index);},),),PageIndicator(_pageIndex, widget.items.length),],);}Widget _buildItem(activeIndex, index) {final items = widget.items;return Center(child: AnimatedContainer(curve: Curves.easeInOut,duration: Duration(milliseconds: 300),height: activeIndex == index ? 500.0 : 450.0,margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 5.0),decoration: BoxDecoration(color: items[index].color,borderRadius: BorderRadius.all(Radius.circular(12.0)),),child: Stack(fit: StackFit.expand,children: <Widget>[ClipRRect(borderRadius: BorderRadius.all(Radius.circular(12.0),),child: Image.network(items[index].image,fit: BoxFit.cover,),),Align(alignment: Alignment.bottomCenter,child: Row(children: <Widget>[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,),),),)],),),],),),);}}class PageIndicator extends StatelessWidget {final int currentIndex;final int pageCount;const PageIndicator(this.currentIndex, this.pageCount);@overrideWidget build(BuildContext context) {return Row(mainAxisAlignment: MainAxisAlignment.center,children: _buildIndicators(),);}Widget _indicator(bool isActive) {return Container(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<Widget> _buildIndicators() {List<Widget> indicators = [];for (int i = 0; i < pageCount; i++) {indicators.add(i == currentIndex ? _indicator(true) : _indicator(false));}return indicators;}}class IndexPage extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(elevation: 0.0,backgroundColor: Colors.white,),body: Carousel(height: 510,items: heroes,),backgroundColor: Colors.white,);}
}
总结
本文主要是讲解了PageVIew的使用还有Stack在什么情况下适用(当然任何情况都可以,你要是适配做好了,按照设计图的尺寸直接Stack完成就是不太友好,代码的整体可读性不是太好,当然你要是做了全封装那当我没说)。还有我们自定义指示器的使用,其实很简单,就是一个index渲染的过程。最后我要说的是,我没贴上main入口函数的代码,相信只要看完了动动脑子的都知道,在这里我也说了你直接复制就能用,main入口函数下MaterialApp的home参数为IndexPage
。
有问题可以提出来,大家共同进步。
用Flutter实现一个类似于轮播图的效果相关推荐
- html+css部分面试题(4)+拼多多商家入驻网页轮播图hover效果
目录 31:为什么要初始化样式? 32. BFC 是什么? 33.HTML 与 XHTML--二者有什么区别? 34.html 常见兼容性问题? 35.对 WEB 标准以及 W3C 的理解与认识 补充 ...
- vue如何使用原生js写动画效果_原生js写一个无缝轮播图插件(支持vue)
轮播图插件(Broadcast.js) 前言:写这个插件的原因 前段时间准备用vue加上网易云的nodejs接口,模拟网易云音乐移动端.因为想自己写一遍所有的代码以及加固自己的flex布局,所以没有使 ...
- html怎么引轮播图插件,原生js写一个无缝轮播图插件(支持vue)
轮播图插件(Broadcast.js) 前言:写这个插件的原因 前段时间准备用vue加上网易云的nodejs接口,模拟网易云音乐移动端.因为想自己写一遍所有的代码以及加固自己的flex布局,所以没有使 ...
- 用jq撸一个简易轮播图
本次文章我将来用jq实现一个轮播图,废话少说下面放效果 先说下基本思想,先将item使用position: absolute;脱离文档流令他们叠在一起,使用z-index来实现轮播的效果,注:这里如果 ...
- js判断定时器是否启动_原生js如何做出轮播图的效果
<div class="box"><ul><li class="active"><img src="./im ...
- 微信小程序之实现层叠轮播图的效果案例(前端学习收藏夹必备)
效果展示 代码展示: WXML代码 <view class="selection_cards" bindtouchstart="touchstart" b ...
- html原生轮播图的实现,使用原生js实现点击切换图片(轮播图)效果
要实现的功能大致为: 点击两边的切换按钮,可以按顺序切换不同的图片 具体操作如下: 一.布局html 1.添加一个div容器 2.添加一个轮播图img标签 3.添加两个按钮img标签 二.css样式设 ...
- html轮播图循环效果,TremulaJS-跨设备多功能的无限循环js轮播图插件
TremulaJS是一款非常酷的跨设备多功能的无限循环js轮播图插件.TremulaJS是一个客户端javascript UI组件,它基于贝兹曲线和物理动量效应制作各种效果,可以制作无限循环的图片流, ...
- jq实现轮播图(景深效果)——功能实现
最近工作不是很忙,正好可以多多练习一下jq,毕竟这块我实在是基础薄弱,在工作中经常会遇到各种各样的问题,导致效率很低. 今天的案例是一个自动轮播图: 上面的轮播图跟以往的轮播有点区别,常见的轮播图是只 ...
最新文章
- SpringBoot如何处理java内存溢出
- DataTable ,XML和JSON相互转化
- python处理excel-python处理Excel的简单示例
- getParameter的用法总结
- 常用公有云接入——阿里
- 配置源码管理工具(2)
- CV新赛事:密集场景行人检测
- 排序算法之 归并排序
- CENTOS6.8 修改主机名
- java直白解释,关于java的static和final关键字的直白、简单解释
- 去掉左边0_TiDB 4.0 在 VIPKID 的应用实践
- PHP生成二维码方法
- 易语言删除c盘源码,易语言基础教程利用API删除自身及子文件
- 什么是DNS,作用是什么?
- 偏差-方差分解、损失函数和正则化
- 如何用Python制作词云,对1000首古诗做词云分析!
- 学习目录-自己动手写CPU
- 怎么用matlab画心形曲线方程,matlab画心形曲线
- 基于beego的高并发开源论坛框架Casnode
- Photoshop几何3D立体头像制作教程