flutter自定義廣告Banner

Flutter 1.0 is out!

Tuesday, December 4, 2018

Banner是手機應用最常見的需求之一,https://pub.dartlang.org/flutter中搜索Banner找到兩個開源庫,

引入項目後,分別存在一些問題,其中banner庫,沒有提供頁碼指示器。banner_view在手動快速滑動的過程中,會導致bug。

因此決定結合兩個項目的優點進行改進,實現效果如下。

主要的佈局是由一個Stack包裹着banner內容視圖和Indicator指示器視圖

其中指示器視圖直接使用的banner_view中的代碼。banner內容視圖部分,使用的是PageView,pageView有個地方比較坑,pageView需要通過PageController控制頁面的跳轉,但是通過PageController拿到的當前page頁碼是double類型,並且會丟失精度,需要進行四捨五入,轉換成int類型之後,再作爲當前頁碼使用。

源碼:

import 'package:flutter/material.dart';

//Created by yangxiaowei at 2018/06/06
//indicator view of banner
class IndicatorWidget extends StatelessWidget {
  final Widget indicatorNormal;
  final Widget indicatorSelected;
  final double indicatorMargin;
  final int size;
  final int currentIndex;

  IndicatorWidget({
    Key key,
    this.size,
    this.currentIndex,
    this.indicatorNormal,
    this.indicatorSelected,
    this.indicatorMargin = 5.0,
  })  : assert(indicatorMargin != null),
        assert(size != null && size > 0),
        assert(currentIndex != null && currentIndex >= 0),
        super(key: key);

  @override
  Widget build(BuildContext context) {
    return this._renderIndicator(context);
  }

  //indicator container
  Widget _renderIndicator(BuildContext context) {
    Widget smallContainer = new Container(
      child: new Row(
        mainAxisSize: MainAxisSize.min,
        children: _renderIndicatorTag(),
      ),
    );

    //default implement
    return new Align(
      alignment: Alignment.bottomCenter,
      child: new Opacity(
        opacity: 0.5,
        child: new Container(
          height: 25,
          padding: new EdgeInsets.symmetric(horizontal: 16.0),
          color: Colors.black45,
          alignment: Alignment.centerRight,
          child: smallContainer,
        ),
      ),
    );
  }

  //generate every indicator item
  List<Widget> _renderIndicatorTag() {
    List<Widget> indicators = [];
    final int len = this.size;
    Widget selected = this.indicatorSelected ?? generateIndicatorItem(normal: false);
    Widget normal = this.indicatorNormal ?? generateIndicatorItem(normal: true);

    for (var index = 0; index < len; index++) {
      indicators.add(index == this.currentIndex ? selected : normal);
      if (index != len - 1) {
        indicators.add(new SizedBox(
          width: this.indicatorMargin,
        ));
      }
    }

    return indicators;
  }

  Widget generateIndicatorItem({bool normal = true, double indicatorSize = 8.0}) {
    return new Container(
      width: indicatorSize,
      height: indicatorSize,
      decoration: new BoxDecoration(
        shape: BoxShape.circle,
        color: normal ? Colors.white : Colors.red,
      ),
    );
  }
}
import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_biobank/widget/IndicatorWidget.dart';

///廣告banner
///author: yinbiao
///time:2018-12-5
class BannerView extends StatefulWidget {
  final int delayTime; //間隔時間秒
  final int scrollTime; //滑動耗時毫秒
  final double height; //banner高度
  final List<Widget> data; //banner內容
  int _index = 0; //當前位置

  BannerView({Key key, @required this.data, this.delayTime = 3, this.scrollTime = 200, this.height = 200.0}) : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return new BannerViewState();
  }
}

class BannerViewState extends State<BannerView> {
  PageController controller = new PageController();
  Timer timer;

  @override
  void initState() {
    super.initState();
    resetTimer();
  }

  ///重置計時器
  void resetTimer() {
    clearTimer();
    timer = new Timer.periodic(new Duration(seconds: widget.delayTime), (Timer timer) {
      if (controller.positions.isNotEmpty) {
        ///這裏controller.page會丟失精度,需要四捨五入
        widget._index = controller.page.round() + 1;
        controller.animateToPage(widget._index, duration: new Duration(milliseconds: widget.scrollTime), curve: Curves.linear);
        setState(() {});
      }
    });
  }

  ///清除計時器
  clearTimer() {
    if (timer != null) {
      timer.cancel();
      timer = null;
    }
  }

  @override
  Widget build(BuildContext context) {
    return new Stack(
      children: <Widget>[
        _buildBanner(),
        _renderIndicator(),
      ],
    );
  }

  Widget _buildBanner() {
    return new SizedBox(
      width: MediaQuery.of(context).size.width,
      height: widget.height,
      child: new GestureDetector(
        onTapDown: (details) {
          clearTimer();
        },
        onTapUp: (details) {
          resetTimer();
        },
        onTapCancel: () {
          resetTimer();
        },
        child: new PageView.builder(
          controller: controller,
          physics: const PageScrollPhysics(parent: const ClampingScrollPhysics()),
          itemBuilder: (BuildContext context, int index) {
            return widget.data[index % widget.data.length];
          },
          itemCount: 0x7fffffff,
          onPageChanged: (index) {
            setState(() {
              widget._index = index;
            });
          },
        ),
      ),
    );
  }

  /// indicator widget
  Widget _renderIndicator() {
    return new IndicatorWidget(
      size: widget.data.length,
      currentIndex: widget._index % widget.data.length,
    );
  }

  @override
  void dispose() {
    clearTimer();
    super.dispose();
  }
}

使用:

  Widget buildBanner() {
    return new Container(
        alignment: Alignment.center,
        height: 200.0,
        child: new BannerView(
          data: <Widget>[
            buildImage("images/bg_login.png"),
            buildImage("images/banner2.png"),
            buildImage("images/banner3.png"),
            buildImage("images/banner4.png"),
          ],
        ));
  }

參考:

https://github.com/zhangruiyu/BannerView

https://github.com/yangxiaoweihn/BannerView

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章