Flutter轮播图
前端开发当中最有意思的就是实现动画特效,Flutter提供的各种动画组件可以方便实现各种动画效果。Flutter中的动画组件主要分为两类:
- 隐式动画控件:只需设置组件开始值,结束值,执行时间,比如AnimatedOpacity,AnimatedSize等组件。
- 显式动画控件:需要设置AnimationController,手动控制动画的执行。显式动画可以完成隐式动画的效果,甚至更加地可控和灵活,不过需要管理该动画的AnimationController生命周期,AnimationController并不是一个控件,所以需要将其放在StatefulWidget中。
不难看出,隐式动画控件封装程度更高,无需管理AnimationController的生命周期,代码因此更简单,我们开发时应该尽量选用隐式动画控件。接着我们就用隐式动画控件来实现在web当中很常见的轮播图。
FadeIn/FadeOut
AnimatedOpacity顾名思义就是专门设置opacity属性值变化的动画组件,其实就是类似css3 中的 transition: opacity time,该动画组件可以实现渐隐渐现动画,下面就是实现步骤:
- 创建StatefulWidget;
- 定义组件属性,zIndex(类似css的z-index),样式列表list,时间timer(实现js的setTimeout和setInterval);
- 实现动画播放的autoPlay功能,在initState方法中启动自动播放的动画,记得在dispose方法回收timer相关资源;
- 布局中Stack和Positioned组件就是实现html中positon: relative/absolute布局;
- AnimatedOpacity组件中的opacity是必须设置的属性,- curve属性与- css3中 动画函数一样,- duration就是动画持续的时间。
  
class OpacityBanner extends StatefulWidget {
  @override
  _OpacityBannerState createState() => _OpacityBannerState();
}
class _OpacityBannerState extends State<OpacityBanner> {
  int zIndex = 0;
  List<String> list = ['ff0000', '00ff00', '0000ff', 'ffff00'];
  Timer timer;
  //setInterval控制当前动画元素的下标,实现动画轮播
  autoPlay() {
    var second = const Duration(seconds: 2);
    timer = Timer.periodic(second, (t) {
      setState(() {
        zIndex = (++zIndex) % list.length;
      });
    });
  }
  @override
  void initState() {
    super.initState();
    timer = Timer(Duration(seconds: 2), autoPlay);
  }
  @override
  void dispose() {
    if (timer != null) timer.cancel();
    super.dispose();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
    body: Stack(alignment: Alignment.center, children: [
      Stack(
          children: list
              .asMap()
              .keys
              .map<Widget>((i) => AnimatedOpacity(
                    curve: Curves.easeIn,
                    duration: Duration(milliseconds: 600),
                    opacity: i == zIndex ? 1 : 0,
                    child: Container(
                      color: Color(int.parse(list[i], radix: 16)).withAlpha(255),
                      height: 300, //100%
                    ),
                  ))
              .toList()),
      Positioned(
          bottom: 20,
          child: Row(
              children: list
                  .asMap()
                  .keys
                  .map((i) => Container(
                      width: 10,
                      height: 10,
                      margin: EdgeInsets.symmetric(horizontal: 5),
                      decoration:
                          BoxDecoration(color: i == zIndex ? Colors.blue : Colors.grey, shape: BoxShape.circle)))
                  .toList()))
    ]));
  }
}
怎么样?实现起来非常简单吧。
SlideIn/SlideOut
接着我们使用AnimatedContainer实现移入/移出动画,同时加上touch事件实现手指左右滑动控制轮播图。实现的步骤和上面的一样,这里只介绍用到不同组件的地方:
- 移入移出动画和上面渐隐动画不同的是要同时控制两个动画元素,分别是移出和移入的元素,使用属性curr和next下标表示。
- AnimatedContainer组件可以控制很多的属性,可以说是实现过渡动画最常用的组件了。我们这里只需要设置transform属性即可,控制动画的属性上面已经介绍过。
- MediaQuery可以查询很多全局的属性,比如高度/宽度,dpr等。
- GestureDetector是一个事件的包装器,这里使用到了- onHorizontalDragStart,- onHorizontalDragUpdate,- onHorizontalDragEnd这三个事件,即横向拖动相关的事件。
  
class SlideBanner extends StatefulWidget {
  @override
  _SlideBannerState createState() => _SlideBannerState();
}
class _SlideBannerState extends State<SlideBanner> {
  List<String> list = [
    'https://upload-images.jianshu.io/upload_images/127924-dec37275411437de.jpg',
    '//upload-images.jianshu.io/upload_images/127924-0999617a887bb6a3.jpg',
    '//upload-images.jianshu.io/upload_images/127924-b48e22b6aef713ae.jpg',
    '//upload-images.jianshu.io/upload_images/127924-b06e44e6a17caf43.jpg'
  ];
  double dx = 0;//距离
  int curr = 0;//要移出的下标
  int next = 0;//要移入的下标
  bool toLeft = true;//自动播放的方向,默认向左
  Timer timer;
  /** 轮播图滑动相关 **/
  dragStart(Offset offset) {
    dx = 0;
  }
  //累计位移距离
  dragUpdate(Offset offset) {
    var x = offset.dx;
    dx += x;
  }
  //达到一定距离后则触发轮播图左右滑动
  dragEnd(Velocity v) {
    if (dx.abs() < 20) return;
    timer.cancel();
    if (dx < 0) {
      //向左
      if (!toLeft) {
        setState(() {
          toLeft = true;
          curr = next - 1 < 0 ? list.length - 1 : next - 1;
        });
      }
      setState(() {
        curr = next;
        next = (++next) % list.length;
      });
    } else {
      //向右
      if (toLeft) {
        setState(() {
          toLeft = false;
          curr = (next + 1) % list.length;
        });
      }
      setState(() {
        curr = next;
        next = --next < 0 ? list.length - 1 : next;
      });
    }
    //setTimeout
    timer = Timer(Duration(seconds: 2), autoPlay);
  }
  autoPlay() {
    var second = const Duration(seconds: 2);
    timer = Timer.periodic(second, (t) {
      setState(() {
        toLeft = true;
        curr = next;
        next = (++next) % list.length;
      });
    });
  }
  @override
  void initState() {
    super.initState();
    timer = Timer(Duration(seconds: 2), autoPlay);
  }
  @override
  void dispose() {
    if (timer != null) timer.cancel();
    super.dispose();
  }
  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;
    final width = size.width;
    return Scaffold(
        body: GestureDetector(
            onHorizontalDragStart: (details) => dragStart(details.globalPosition),
            onHorizontalDragUpdate: (details) => dragUpdate(details.delta),
            onHorizontalDragEnd: (details) => dragEnd(details.velocity),
            child: Stack(
                children: list
                    .asMap()
                    .keys
                    .map((i) => AnimatedContainer(
                        duration: Duration(milliseconds: (i == next || i == curr) ? 600 : 0),
                        curve: Curves.easeIn,
                        transform: Matrix4.translationValues(
                            i == next ? 0 : i == curr ? (toLeft ? -width : width) : (toLeft ? width : -width), 0, 0),
                        width: width,
                        height: 300,
                        child: Image.network(list[i], width: width, height:double.infinity ,fit: BoxFit.cover)))
                    .toList())));
  }
}
Flutter轮播图的更多相关文章
- 28 Flutter 轮播图 flutter_swiper
		中文地址: https://github.com/best-flutter/flutter_swiper/blob/master/README-ZH.md 基本参数 参数 默认值 描述 scrollD ... 
- 7、Flutter banner_view 轮播图的使用
		1.前言 实现轮播图,效果如下: 2.实现 将采用 banner_view 实现:资源库地址 2.1.yaml 引入依赖 在 pubspec.yaml 声明需要引用的库,执行命令 flutter pa ... 
- 05-06 Flutter JSON和序列化反序列化、创建模型类转换Json数据、轮播图数据渲染:Flutter创建商品数据模型 、请求Api接口渲染热门商品 推荐商品
		Config.dart class Config{ static String domain='http://jd.itying.com/'; } FocusModel.dart class Focu ... 
- Flutter学习五之网络请求和轮播图的实现
		上期讲到了,怎样实现一个下拉刷新和加载更多的列表,数据更新,需要使用到网络请求,在flutter中,怎样实现一个网络请求呢?官方使用的是dart io中的HttpClient发起的请求,但HttpCl ... 
- js 基础篇(点击事件轮播图的实现)
		轮播图在以后的应用中还是比较常见的,不需要多少行代码就能实现.但是在只掌握了js基础知识的情况下,怎么来用较少的而且逻辑又简单的方法来实现呢?下面来分析下几种不同的做法: 1.利用位移的方法来实现 首 ... 
- 实现下来ScrollView放大轮播图
		创建工程,创建一个UIScrollView属性,并遵循其协议: #define kWidth self.view.frame.size.width//屏幕宽 #define kHeight self. ... 
- ViewPager轮播图
		LoopViewPagerLayout无限轮播 项目地址:https://github.com/why168/LoopViewPagerLayout 支持三种动画: 支持修改轮播的速度: 支持修改滑动 ... 
- CSS-用伪类制作小箭头(轮播图的左右切换btn)
		先上学习地址:http://www.htmleaf.com/Demo/201610234136.html 作者对轮播图左右按钮的处理方法一改往常,不是简单地用btn.prev+btn.next的图片代 ... 
- phpcms首页实现轮播图
		1.在你想要加轮播图的位置加入以下 <div id="flowDiagram" > <div id="button"> <span ... 
随机推荐
- Java实现 LeetCode 524 通过删除字母匹配到字典里最长单词(又是一道语文题)
			524. 通过删除字母匹配到字典里最长单词 给定一个字符串和一个字符串字典,找到字典里面最长的字符串,该字符串可以通过删除给定字符串的某些字符来得到.如果答案不止一个,返回长度最长且字典顺序最小的字符 ... 
- Java实现 蓝桥杯VIP 算法提高 5-3日历
			算法提高 5-3日历 时间限制:1.0s 内存限制:256.0MB 问题描述 已知2007年1月1日为星期一.设计一函数按照下述格式打印2007年以后(含)某年某月的日历,2007年以前的拒绝打印.为 ... 
- Java实现 蓝桥杯 素因子去重
			素因子去重 问题描述 给定一个正整数n,求一个正整数p,满足p仅包含n的所有素因子,且每个素因子的次数不大于1 输入格式 一个整数,表示n 输出格式 输出一行,包含一个整数p. 样例输入 1000 样 ... 
- Linux 用户和用户组管理-用户信息文件
			用户信息文件存在在/etc/passwd中,vi /etc/passwd 其中,有七列以:分隔的信息 第一列表示用户(account),第二列表示密码标志(真正的密码存在在/etc/shadow中), ... 
- hibernate中的映射
			hibernate中的映射是指Java类和数据库表中的属性来进行关联,然后通过类来操作数据库中,这就是简单的映射. 
- Vue可响应式数组方法
			<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ... 
- Java 多线程基础(三) start() 和 run()
			Java 多线程基础(三) start() 和 run() 通过之前的学习可以看到,创建多线程过程中,最常用的便是 Thread 类中的 start() 方法和线程类的 run() 方法.两个方法都包 ... 
- MyBatis运行流程及入门第一个程序
			1. mybatis是什么? MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并 ... 
- MATLAB读取和保存nifti文件
			介绍 分析核磁数据时,数据的读取和保存是两个基本的操作.虽然大部分工具包都对这些功能进行了封装,但是如果你不了解如何使用这些工具包或者说当前的任务太简单不值得去使用这些庞大的工具包的时候就需要我们自己 ... 
- Javascript 16进制转有符号的10进制整数
			在赶项目中开发一个单片机对应的数据接口,需要将一个两字节的十六进制转化为-256~255的10进制数.百度了好久都没有对应且简明的教程,干脆就自己写一篇. 我们都知道JavaScript整数类型有 ... 
