前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter跨平台移动端开发丨自定义 Banner Widget

Flutter跨平台移动端开发丨自定义 Banner Widget

作者头像
码脑
发布2019-07-04 11:37:17
8610
发布2019-07-04 11:37:17
举报
文章被收录于专栏:大前端

移动端开发过程中 Banner 组件非常常见,项目中用的到就封装一个,主要用到 Timer + PageView,采用定时轮播的方法实现


自定义 Banner Widget

可设置 banner 高度、图片展示时间、图片切换速度,如需其它支持可自行添加、更改

代码语言:javascript
复制
import 'package:flutter/material.dart';
import 'dart:async';
import 'BannerBean.dart';
import 'package:meta/meta.dart';

/**
 * @des banner 部件
 * @author liyongli 20190702
 * */
class BannerWidget extends StatefulWidget{

  // banner 数据实体集合
  List<BannerBean> bannerData;

  // banner 默认高度
  double bannerHeight;

  // banner 默认展示时间(毫秒)
  int bannerDuration;

  // banner 切换速度(毫秒)
  int bannerSwitch;

  // 图片加载器
  Build bannerBuild;

  // 点击事件回调接口
  OnBannerPress bannerPress;

  BannerWidget(
      {Key key,
        @required this.bannerData,
        this.bannerHeight,
        this.bannerDuration,
        this.bannerSwitch,
        this.bannerPress,
        this.bannerBuild})
      : super(key: key);

  @override
  State<StatefulWidget> createState() => BannerWidgetState();

}

/**
 * @des banner 部件 State
 * @author liyongli 20190702
 * */
const CountMax = 0x7fffffff;
typedef void OnBannerPress(int position, BannerBean entity);
typedef Widget Build(int position, BannerBean entity);
class BannerWidgetState extends State<BannerWidget>{

  // 定时器
  Timer bannerTimer;

  // 当前 banner 页下标
  int bannerIndex = 0;

  // 控制器
  PageController bannerController;

  @override
  void initState() {
    super.initState();
    double current = (CountMax / 2) - ((CountMax / 2) % widget.bannerData.length);
    bannerController = PageController(initialPage: current.toInt());
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      height: widget.bannerHeight,
      color: Colors.white,
      child: Stack(
        children: <Widget>[
          viewPages(),
          viewTips()
        ],
      ),
    );
  }

  /**
   * banner 图片组件
   * */
  Widget viewPages(){
    return PageView.builder(
      itemCount: CountMax,
      controller: bannerController,
      onPageChanged: onPageChanged,
      itemBuilder: (context, index){
        return InkWell(

          onTap: (){
            if(null != widget.bannerPress){
              widget.bannerPress(bannerIndex, widget.bannerData[bannerIndex]);
            }
          },

          child: widget.bannerBuild == null ? FadeInImage.memoryNetwork( image: widget.bannerData[index % widget.bannerData.length].bannerUrl, fit: BoxFit.fitWidth) : widget.bannerBuild(index, widget.bannerData[index % widget.bannerData.length])
        );
      },
    );
  }

  /**
   * 更新坐标与图片
   * */
  void onPageChanged(index){
    bannerIndex = index % widget.bannerData.length;
    setState(() {});
  }

  /**
   * banner 小原点组件
   * */
  Widget viewTips(){

    if(widget.bannerData.length <= 1){
      return Align();
    }

    return Align(
      alignment: Alignment.bottomCenter,
      child: Container(
        height: 32.0, 
        padding: EdgeInsets.all(5.0),
        color: Colors.black,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: <Widget>[
            Text(widget.bannerData[bannerIndex].bannerTitle, style: TextStyle(color: Colors.white)),
            Row(children: bannerCircle(),)
          ],
        ),
      ),
    );
  }

  /**
   * 绘制小圆点并组成集合返回
   * */
  List<Widget> bannerCircle(){
    List<Widget> circleList = [];
    for(var i = 0 ; i < widget.bannerData.length ; i++){
      circleList.add(
        Container(
          margin:  EdgeInsets.all(3.0),
          width: 5.0,
          height: 5.0,
          decoration: BoxDecoration(
            shape: BoxShape.circle,
            color: bannerIndex == i ? Colors.deepOrange : Colors.white,
          ),
        )
      );
    }
    return circleList;
  }

  /**
   * 启动计时器
   * */
  void start(){
    if(null != bannerTimer && bannerTimer.isActive){
      stop();
    }

    if(null == widget.bannerData || widget.bannerData.length <= 1){
      return;
    }

    bannerTimer = Timer.periodic(Duration(milliseconds: widget.bannerDuration), (bannerTimer){
      bannerController.animateToPage(bannerController.page.toInt() + 1, duration: Duration(milliseconds: widget.bannerSwitch), curve: Curves.linear);
    });
  }

  /**
   * 停止计时器
   * */
  void stop(){
    bannerTimer?.cancel();
    bannerTimer = null;
  }

  /**
   * 释放资源
   * */
  @override
  void dispose() {
    stop();
    bannerController?.dispose();
    super.dispose();
  }
}

/**
 * @des banner 组件抽象类
 * @author liyongli 20190702
 * */
abstract class BannerBeanUtils{

  // 获取 banner 地址
  get bannerUrl;

  // 获取 banner 介绍
  get bannerTitle;

}

自定义 Banner Widget 数据实体

可设置图片加载地址、图片配文、图片跳转参数等

代码语言:javascript
复制
import 'package:delongzhixuan/utils/banner/BannerWidget.dart';

/**
 * @des banner 组件实体类
 * @author liyongli 20190702
 * */
class BannerBean extends Object with BannerBeanUtils{

  String imageUrl;
  String titleStr;
  int intentType;

  BannerBean({this.imageUrl, this.titleStr, this.intentType});

  @override
  get bannerTitle => titleStr;

  @override
  get bannerUrl => imageUrl;

}

自定义 Banner Widget 应用

本实例展示加载本地图片

代码语言:javascript
复制
import 'dart:async';
import 'dart:core';
import 'package:delongzhixuan/utils/banner/BannerBean.dart';
import 'package:delongzhixuan/utils/banner/BannerWidget.dart';
import 'package:flutter/material.dart';

/**
 * @des 首页
 * @author liyongli
 * */
class MainHome extends StatefulWidget {

  @override
  State<StatefulWidget> createState() => new _MainHomeState();

}

/**
 * @des 首页 State
 * @author liyongli
 * */
class _MainHomeState extends State<MainHome> {

  @override
  Widget build(BuildContext context) {
    return _initWidget(context);
  }

  // 跨域访问
  GlobalKey<BannerWidgetState> globalKey = new GlobalKey<BannerWidgetState>();

  // banner 数据集合
  List<BannerBean> bannerData;

  /**
   * 初始化 widget
   * */
  Widget _initWidget(BuildContext context){
    return new Scaffold(
      body: SingleChildScrollView(
          child: Column(
            mainAxisSize: MainAxisSize.max,
            children: <Widget>[

              // banner 部分
              Container(
                width: double.maxFinite,
                height: 260.0,
                color: Colors.white,
                child: BannerWidget( // 自定义 banner widget
                  key: globalKey, // 跨域访问目标
                  bannerData: _initBannerData(), // 数据集合
                  bannerDuration: 5000, // 展示时间
                  bannerSwitch: 500, // 切换耗时
                  bannerPress: _bannerPress, // 点击事件监听
                  bannerBuild: (position, BannerBean){ // 加载器
                    return Image.asset(BannerBean.bannerUrl, fit: BoxFit.fitWidth);
                  }
                ),
              ),
            ],
          ),
      )
    );
  }

  /**
   * 初始化 banner 数据
   * */
  List<BannerBean> _initBannerData(){

    List<BannerBean> bannerList = [
      new BannerBean(imageUrl:"images/main_banner01.png", titleStr: "main_banner01", intentType: 0),
      new BannerBean(imageUrl:"images/main_banner01.png", titleStr: "main_banner02", intentType: 0),
      new BannerBean(imageUrl:"images/main_banner01.png", titleStr: "main_banner03", intentType: 0),
    ];

    // 2 秒后启动轮播
    Timer timer;
    timer = new Timer(new Duration(seconds: 2), () {
      globalKey.currentState.start();
      timer.cancel();
      timer = null;
    });

    return bannerList;
  }
  
  /**
   * banner 点击事件监听
   * */
  void _bannerPress(int position, BannerBean entity){
    print(position);
    print(entity.titleStr + entity.imageUrl);
  }

}

运行结果

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019.07.03 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 自定义 Banner Widget
  • 自定义 Banner Widget 数据实体
  • 自定义 Banner Widget 应用
  • 运行结果
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档