Flutter控件封装之轮播图Banner

简介: Flutter中实现轮播图的方式有很多种,比如使用三方flutter_swiper,card_swiper等等,使用这些三方,可以很快很方便的实现一个轮播图展示,基本上也能满足我们日常的开发需求,如果说,想要一些定制化的操作,那么就不得不去更改源码或者自己自定义一个,自己定义的话,Flutter中提供了原生组件PageView,可以使用它很方便的来实现一个轮播图。

Flutter中实现轮播图的方式有很多种,比如使用三方flutter_swiper,card_swiper等等,使用这些三方,可以很快很方便的实现一个轮播图展示,基本上也能满足我们日常的开发需求,如果说,想要一些定制化的操作,那么就不得不去更改源码或者自己自定义一个,自己定义的话,Flutter中提供了原生组件PageView,可以使用它很方便的来实现一个轮播图。


PageView类似于Android中的ViewPager,可以实现页面的横向或者纵向滑动,具体的使用方式可以直接PageView(),或者使用PageView.builder(),这两种方式都可以实现,区别就是前者会把所有页面一次性初始化出来,而后者则不会,为了便于大家了解这个组件,我们会简单的举一个小案例。


按照以往惯例,我们先看下本篇文章的大纲,大概如下:


1、最终的实现效果一览

2、PageView组件的属性和具体使用

3、轮播图封装注意事项

4、案例源码刨析

5、封装后的源码及使用方式

6、总结


一、最终的实现效果一览


利用PageView,封装了一些特定的效果,比如文字指示器,圆角指示器,以及指示器的位置,轮播图片的缩进展示等等,录制了一个Gif效果图,如下:



二、PageView组件的属性和具体使用


毕竟是使用PageView来实现一个轮播图,那么针对这个组件,我们需要简单的做个介绍:

先看一下基本的常见属性:


属性

类型

概述

scrollDirection

Axis

滚动方向,水平或者垂直,默认水平。

水平:Axis.horizontal

垂直:Axis.vertical

controller

PageController

滚动控制器,可以定位页面,获取页面等信息

onPageChanged

ValueChanged<int>

页面发生改变时的回调

physics

ScrollPhysics

滑动效果,不设置,会根据不同平台有不同的滚动效果

NeverScrollableScrollPhysics 设置后,页面就不可滚动

BouncingScrollPhysics 表示滚动到底了会有弹回的效果,就是iOS的默认交互

ClampingScrollPhysics 表示滚动到底了就给一个效果,就是Android的默认交互

FixedExtentScrollPhysics 就是iOS经典选择时间组件UIDatePicker那种交互

pageSnapping

bool

是否是整页滑动,默认为true


在实际的开发中,PageView.builder()方式使用是居多的,也建议大家以这种方式作为使用,很简单,只需要在itemBuilder里返回页面视图即可,代码如下:


PageView.builder(
itemCount: 6,
onPageChanged: (position) {
print("当前索引为:$position");
            },
itemBuilder: (context, index) {
returnContainer(
color: Colors.amber,
alignment: Alignment.center,
child: Text("我是第$index个页面"));
            })

基本效果如下:



三、轮播图封装注意事项


基本掌握了PageView的用法之后,我们就开始着手封装一个轮播图,先分析一下,构成轮播图的几个要素,第一,满足自动轮播的要求,而且可以动态设置轮播时长,第二,要能满足多种指示器要求,而且位置可以动态设置,第三,要满足手动轮播和自动轮播要求,并且要处理好手势和定时直接的冲突,第四,最主要的就是使用起来要简单。


定时器注意事项


简单的确定要素之后,我们就可以动手书写了,自动轮播很简单,我们只需要开启一个定时器即可,但是定时器需要注意开启和暂停,也就是什么时候开始,什么时候暂停,否则很容易造成轮播混乱现象。


轮播图开始,其一,也就是主动设置了自动轮播属性,进入到页面,我们就需要开启定时,如果页面退入后台,再重新回到前台,我们也是需要开启轮播的,其二就是暂停,除了退入后台暂停之外,还有就是手势滑动的时候也需要暂停,否则就会和定时造成冲突。


手势注意事项


关于手势,如果我们直接监听页面组件的手势,发现是和PageView有冲突的,为了解决这个手势问题,我们可以采用原始指针事件Listener来监听手势滑动。


部分代码如下,手指按下后,取消定时,手指抬起后,开启定时,当然了如果只有按下和抬起,那么则是一个点击事件,我们可以把这个事件回调给用户。


Listener(
onPointerDown: (event) {
//手指按下,定时取消_pauseTimer();
_isClick=true;
          },
onPointerMove: (event) {
_isClick=false;
          },
onPointerUp: (event) {
//手指抬起,定时开启_startTimer();
//作为点击事件if (_isClick&&widget.bannerClick!=null) {
widget.bannerClick!(_currentPage);
            }
          },
child: PageView.builder()t)


指示器注意事项


指示器需要注意,如果说自己用,一种指示器无可厚非,如果是给他人用,那么就要丰富多彩,尽量满足多的需求。


四、案例源码刨析


1、创建定时器


定时器使用的是Timer,定义了两个方法,便于开启和暂停,当轮播时间到时,就可以执行页面切换操作,使用PageController的animateToPage来切换。


/** 开启定时* */void_startTimer() {
if (!_isRunning) {
_isRunning=true;
_timer=Timer.periodic(Duration(seconds: widget.delay!), (timer) {
_controller.animateToPage(_pagePosition+1,
duration: constDuration(milliseconds: 800),
curve: Curves.easeInOut);
      });
    }
  }
/** 暂停定时* */void_pauseTimer() {
if (_isRunning) {
_isRunning=false;
_timer?.cancel(); //取消计时器    }
  }
@overridevoiddispose() {
_controller.dispose();
_timer?.cancel();
super.dispose();
  }


2、感知生命周期变化


当页面退入后台和回到前台,我们需要做暂停和开启定时,那么就需要针对页面做监听操作,添加监听后,记得当前类with WidgetsBindingObserver。


// 添加监听WidgetsBinding.instance.addObserver(this);
/** 感知生命周期变化* */@overridevoiddidChangeAppLifecycleState(AppLifecycleStatestate) {
super.didChangeAppLifecycleState(state);
if (state==AppLifecycleState.resumed&&widget.autoPlay!) {
_startTimer(); //页面可见,开启定时    } elseif (state==AppLifecycleState.paused&&_isRunning) {
_pauseTimer(); //页面不可见,关闭定时    }
  }


3、图片圆角


图片的圆角实现就比较多了,比如Container的装饰器,或者使用组件ClipRRect都可以的。


ClipRRect(
//设置图片圆角borderRadius: BorderRadius.circular(widget.radius!),
child: getBannerImage(imageUrl)))


4、指示器类型和位置


指示器类型,可以根据业务需求,进行专项定制,目前源码中的类型,有以下几种,分别是,圆形,圆角,矩形,文字,其位置,可以放到中间,左右两边以及轮播图的下方。


/** 指示器* */Widget_buildIndicators(mainAxisAlignment) {
if (widget.indicatorType==IndicatorType.text) {
//文字returnContainer(
alignment: widget.textIndicatorAlignment,
child: VipText(
"${_currentPage+1}/${widget.imageList!.length}",
style: widget.textIndicatorStyle,
backgroundColor: widget.textIndicatorBgColor,
padding: widget.textIndicatorPadding,
paddingLeft: widget.textIndicatorPaddingLeft,
paddingTop: widget.textIndicatorPaddingTop,
paddingRight: widget.textIndicatorPaddingRight,
paddingBottom: widget.textIndicatorPaddingBottom,
        ),
      );
    }
returnRow(
mainAxisAlignment: mainAxisAlignment,
children: List.generate(widget.imageList!.length, (index) {
returnContainer(
width: _currentPage==index?widget.indicatorWidth              : widget.indicatorUnWidth??widget.indicatorWidth,
height: _currentPage==index?widget.indicatorHeight              : widget.indicatorUnHeight??widget.indicatorHeight,
margin: EdgeInsets.symmetric(horizontal: widget.indicatorMargin!),
decoration: BoxDecoration(
shape: widget.indicatorType==IndicatorType.circle?BoxShape.circle                : BoxShape.rectangle,
borderRadius: widget.indicatorType==IndicatorType.rectangle?BorderRadius.all(Radius.circular(widget.indicatorRadius!))
                : null,
color: _currentPage==index?widget.indicatorSelectColor                : widget.indicatorUnSelectColor,
          ),
        );
      }),
    );
  }

5、轮播图缩进效果


缩进的话,有两种,一种除了当前图片,左右图片会变小,当滑动到当前图片之后才会放大,一种就是很简单的缩进。


viewportFraction 可以理解为一页内容占据屏幕的比例,铺满就是1,小于1就是不铺满。


PageController(viewportFraction: widget.viewportFraction!)

如果说,在滑动的时候,想要图片实现放大和缩小动画,那么我们需要执行一个放大和缩小动画Transform.scale。


returnTransform.scale(
scale: endScale,
child: Container(
margin: widget.imageMargin!=null?EdgeInsets.all(widget.imageMargin!)
                            : EdgeInsets.only(
left: widget.imageMarginLeft!,
top: widget.imageMarginTop!,
right: widget.imageMarginRight!,
bottom: widget.imageMarginBottom!),
child: ClipRRect(
//设置图片圆角borderRadius: BorderRadius.circular(widget.radius!),
child: getBannerImage(imageUrl))))

五、封装后的源码及使用方式

目前源码已经上传至了Github,大家需要的话,可以查看,由于篇幅有限,就不全部粘贴了,地址:


https://github.com/AbnerMing888/flutter_widget/blob/master/lib/ui/widget/vip_banner.dart


可用属性一览


属性

类型

概述

imageList

List<String>

图片地址集合

titleList

List<String>

标题集合

radius

double

图片圆角

height

double

图片高度

delay

int

多少时间轮播一次

autoPlay

bool

是否自动轮播

bannerClick

Function(int)

条目点击事件

showIndicators

bool

是否展示指示器

imageMarginLeft

double

图片距离左边的距离

imageMarginTop

double

图片距离上边的距离

imageMarginRight

double

图片距离右边的距离

imageMarginBottom

double

图片距离下边的距离

imageMargin

double

图片距离左上右下的距离,统一设置

marginLeft

double

轮播图整体距离左边的距离

marginTop

double

轮播图整体距离上边的距离

marginRight

double

轮播图整体距离右边的距离

marginBottom

double

轮播图整体距离下边的距离

margin

double

轮播图整体距离左上右下的距离

indicatorMarginLeft

double

指示器距离左边的距离

indicatorMarginRight

double

指示器距离右边的距离

indicatorMarginBottom

double

指示器距离底部的距离

indicatorSelectColor

Color

指示器选中的颜色

indicatorUnSelectColor

Color

指示器未选中的颜色

indicatorWidth

double

指示器宽

indicatorHeight

double

指示器高

indicatorUnWidth

double

指示器未选中宽

indicatorUnHeight

double

指示器未选中高

indicatorMargin

double

指示器边距

indicatorType

IndicatorType

指示器类型

circle, rectangle, text

indicatorRadius

double

指示器圆角度数

indicatorBannerBottom

bool

指示器位置,是在banner上还是Banner下

indicatorBottomColor

Color

指示器在Banner下的背景,默认是透明

indicatorBottomHeight

double

指示器在Banner下的高度

indicatorBottomMarginRight

double

指示器在Banner下的 距离右边

indicatorBottomMarginLeft

double

指示器在Banner下的 距离左边

indicatorBottomMainAxisAlignment

MainAxisAlignment

指示器在Banner下的位置

左,中,右

viewportFraction

double

banner缩进

textIndicatorAlignment

Alignment

文字的位置

textIndicatorStyle

TextStyle

文字样式

textIndicatorBgColor

Color

文字指示器背景

textIndicatorPadding

double

文字指示器内边距

textIndicatorPaddingLeft

double

文字指示器内边距左

textIndicatorPaddingTop

double

文字指示器内边距上

textIndicatorPaddingRight

double

文字指示器内边距右

textIndicatorPaddingBottom

double

文字指示器内边距下

titleBgColor

Color

文字Title背景

titleHeight

double

文字Title高度

titleAlignment

Alignment

文字Title的位置

titleStyle

TextStyle

文字Title样式

titleMarginBottom

double

文字Title距离底部

bannerOtherScale

double

除中间外的其他图片缩放比例

placeholderImage

String

Banner 占位图

errorImage

String

Banner 错误图

imageBoxFit

BoxFit

图片伸缩模式

使用方式


普通加载


VipBanner(
imageList: const [
"https://www.vipandroid.cn/ming/image/gan.png",
"https://www.vipandroid.cn/ming/image/zao.png"          ],
bannerClick: (position) {
//条目点击Toast.toast(context, msg: position.toString());
          })


文字指示器


VipBanner(
imageList: const [
"https://www.vipandroid.cn/ming/image/gan.png",
"https://www.vipandroid.cn/ming/image/zao.png"          ],
indicatorType: IndicatorType.text,
bannerClick: (position) {
Toast.toast(context, msg: position.toString());
          })


圆角指示器


VipBanner(
imageList: const [
"https://www.vipandroid.cn/ming/image/gan.png",
"https://www.vipandroid.cn/ming/image/zao.png"          ],
indicatorType: IndicatorType.rectangle,
indicatorRadius: 5,
indicatorWidth: 20,
indicatorHeight: 5,
bannerClick: (position) {
Toast.toast(context, msg: position.toString());
          })

使用方式呢,有很多的类型,就不一一举例了,大家可以看源码中的页面,地址是:

https://github.com/AbnerMing888/flutter_widget/blob/master/lib/ui/page/view/banner/banner_page.dart

六、总结


在封装的时候,务必要确定的有以下几个要素,一是定时轮播,二是手势和定时冲突解决,三是无限轮播,四是指示器的设置,五是图片轮播的效果,搞定这些潜在的要素,一个简简单单的轮播图封装起来并不难。

相关文章
|
4月前
|
Android开发
Flutter控件的显示与隐藏
Flutter控件的显示与隐藏
157 3
|
29天前
|
存储 缓存 Dart
Flutter&鸿蒙next 封装 Dio 网络请求详解:登录身份验证与免登录缓存
本文详细介绍了如何在 Flutter 中使用 Dio 封装网络请求,实现用户登录身份验证及免登录缓存功能。首先在 `pubspec.yaml` 中添加 Dio 和 `shared_preferences` 依赖,然后创建 `NetworkService` 类封装 Dio 的功能,包括请求拦截、响应拦截、Token 存储和登录请求。最后,通过一个登录界面示例展示了如何在实际应用中使用 `NetworkService` 进行身份验证。希望本文能帮助你在 Flutter 中更好地处理网络请求和用户认证。
152 1
|
29天前
|
Dart UED 开发者
Flutter&鸿蒙next中的按钮封装:自定义样式与交互
在Flutter应用开发中,按钮是用户界面的重要组成部分。Flutter提供了多种内置按钮组件,但有时这些样式无法满足特定设计需求。因此,封装一个自定义按钮组件变得尤为重要。自定义按钮组件可以确保应用中所有按钮的一致性、可维护性和可扩展性,同时提供更高的灵活性,支持自定义颜色、形状和点击事件。本文介绍了如何创建一个名为CustomButton的自定义按钮组件,并详细说明了其样式、形状、颜色和点击事件的处理方法。
75 1
|
29天前
|
开发工具 UED
Flutter&鸿蒙next中封装一个输入框组件
本文介绍了如何创建一个简单的Flutter播客应用。首先,通过`flutter create`命令创建项目;接着,在`lib`目录下封装一个自定义输入框组件`CustomInput`;然后,在主应用文件`main.dart`中使用该输入框组件,实现简单的UI布局和功能;最后,通过`flutter run`启动应用。本文还提供了后续扩展建议,如状态管理、网络请求和UI优化。
100 1
|
1月前
|
存储 缓存 JavaScript
Flutter 学习之封装 WebView
【10月更文挑战第24天】通过以上的探讨,我们可以看出,在 Flutter 中封装 WebView 是非常有必要的,它可以提高代码的复用性、增强可维护性、提供统一接口。在实际应用中,我们需要根据具体的需求和场景,选择合适的封装方法和技术,以实现更好的效果。
|
27天前
|
开发工具
Flutter&鸿蒙next中封装一个列表组件
Flutter&鸿蒙next中封装一个列表组件
41 0
|
29天前
|
Dart 安全 UED
Flutter&鸿蒙next中的表单封装:提升开发效率与用户体验
在移动应用开发中,表单是用户与应用交互的重要界面。本文介绍了如何在Flutter中封装表单,以提升开发效率和用户体验。通过代码复用、集中管理和一致性的优势,封装表单组件可以简化开发流程。文章详细讲解了Flutter表单的基础、封装方法和表单验证技巧,帮助开发者构建健壮且用户友好的应用。
66 0
|
5月前
|
JSON Dart API
Flutter dio http 封装指南说明
本文介绍了如何实现一个通用、可重构的 Dio 基础类,包括单例访问、日志记录、常见操作封装以及请求、输出、报错拦截等功能。
117 2
Flutter dio http 封装指南说明
|
4月前
|
开发者 监控 开发工具
如何将JSF应用送上云端?揭秘在Google Cloud Platform上部署JSF应用的神秘步骤
【8月更文挑战第31天】本文详细介绍如何在Google Cloud Platform (GCP) 上部署JavaServer Faces (JSF) 应用。首先,确保已准备好JSF应用并通过Maven构建WAR包。接着,使用Google Cloud SDK登录并配置GCP环境。然后,创建`app.yaml`文件以配置Google App Engine,并使用`gcloud app deploy`命令完成部署。最后,通过`gcloud app browse`访问应用,并利用GCP的监控和日志服务进行管理和故障排查。整个过程简单高效,帮助开发者轻松部署和管理JSF应用。
61 0
|
5月前
|
移动开发 UED 容器
Flutter-自定义可展开文本控件
Flutter-自定义可展开文本控件
90 0