效果图:

过程脑洞自补!!!

类似于上图的效果,我们应该能想到用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的使用。
再补充一点: 设置 PageControllerviewportFraction 参数小于 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入口函数下MaterialApphome参数为IndexPage

有问题可以提出来,大家共同进步。

用Flutter实现一个类似于轮播图的效果相关推荐

  1. html+css部分面试题(4)+拼多多商家入驻网页轮播图hover效果

    目录 31:为什么要初始化样式? 32. BFC 是什么? 33.HTML 与 XHTML--二者有什么区别? 34.html 常见兼容性问题? 35.对 WEB 标准以及 W3C 的理解与认识 补充 ...

  2. vue如何使用原生js写动画效果_原生js写一个无缝轮播图插件(支持vue)

    轮播图插件(Broadcast.js) 前言:写这个插件的原因 前段时间准备用vue加上网易云的nodejs接口,模拟网易云音乐移动端.因为想自己写一遍所有的代码以及加固自己的flex布局,所以没有使 ...

  3. html怎么引轮播图插件,原生js写一个无缝轮播图插件(支持vue)

    轮播图插件(Broadcast.js) 前言:写这个插件的原因 前段时间准备用vue加上网易云的nodejs接口,模拟网易云音乐移动端.因为想自己写一遍所有的代码以及加固自己的flex布局,所以没有使 ...

  4. 用jq撸一个简易轮播图

    本次文章我将来用jq实现一个轮播图,废话少说下面放效果 先说下基本思想,先将item使用position: absolute;脱离文档流令他们叠在一起,使用z-index来实现轮播的效果,注:这里如果 ...

  5. js判断定时器是否启动_原生js如何做出轮播图的效果

    <div class="box"><ul><li class="active"><img src="./im ...

  6. 微信小程序之实现层叠轮播图的效果案例(前端学习收藏夹必备)

    效果展示 代码展示: WXML代码 <view class="selection_cards" bindtouchstart="touchstart" b ...

  7. html原生轮播图的实现,使用原生js实现点击切换图片(轮播图)效果

    要实现的功能大致为: 点击两边的切换按钮,可以按顺序切换不同的图片 具体操作如下: 一.布局html 1.添加一个div容器 2.添加一个轮播图img标签 3.添加两个按钮img标签 二.css样式设 ...

  8. html轮播图循环效果,TremulaJS-跨设备多功能的无限循环js轮播图插件

    TremulaJS是一款非常酷的跨设备多功能的无限循环js轮播图插件.TremulaJS是一个客户端javascript UI组件,它基于贝兹曲线和物理动量效应制作各种效果,可以制作无限循环的图片流, ...

  9. jq实现轮播图(景深效果)——功能实现

    最近工作不是很忙,正好可以多多练习一下jq,毕竟这块我实在是基础薄弱,在工作中经常会遇到各种各样的问题,导致效率很低. 今天的案例是一个自动轮播图: 上面的轮播图跟以往的轮播有点区别,常见的轮播图是只 ...

最新文章

  1. SpringBoot如何处理java内存溢出
  2. DataTable ,XML和JSON相互转化
  3. python处理excel-python处理Excel的简单示例
  4. getParameter的用法总结
  5. 常用公有云接入——阿里
  6. 配置源码管理工具(2)
  7. CV新赛事:密集场景行人检测
  8. 排序算法之 归并排序
  9. CENTOS6.8 修改主机名
  10. java直白解释,关于java的static和final关键字的直白、简单解释
  11. 去掉左边0_TiDB 4.0 在 VIPKID 的应用实践
  12. PHP生成二维码方法
  13. 易语言删除c盘源码,易语言基础教程利用API删除自身及子文件
  14. 什么是DNS,作用是什么?
  15. 偏差-方差分解、损失函数和正则化
  16. 如何用Python制作词云,对1000首古诗做词云分析!
  17. 学习目录-自己动手写CPU
  18. 怎么用matlab画心形曲线方程,matlab画心形曲线
  19. 基于beego的高并发开源论坛框架Casnode
  20. Photoshop几何3D立体头像制作教程

热门文章

  1. 工业树莓派如何保障电气安全?
  2. 力扣 1832. 判断句子是否为全字母句
  3. echarts图表导出为pdf
  4. centos xen虚拟机搭建
  5. 百度云因网络原因无法登陆
  6. 如何解决阿里巴巴国际站询盘少的问题
  7. 为什么使用CDN后网速还是很慢
  8. 分布式的,新一代版本控制系统Mercurial的介绍及简要入门
  9. python选择框_python - 自定义/删除Django选择框空白选项
  10. 厉害了!巾帼不让须眉,女程序员逆战贪吃蛇大作战,众人惊呆了