Flutter實(shí)現(xiàn)可循環(huán)輪播圖

效果圖

壹耀里、控件分解圖

控件拆解圖

貳涛救、代碼實(shí)現(xiàn)

貳點(diǎn)壹、構(gòu)建根布局

新建AdPictureWidget繼承自StatefulWidget,新建_AdPictureWidgetState類(lèi)繼承自State<AdPictureWidget>孵奶,根布局為Stack燕刻,代碼如下:

class AdPictureWidget extends StatefulWidget {
  @override
  _AdPictureWidgetState createState() => _AdPictureWidgetState();
}

class _AdPictureWidgetState extends State<AdPictureWidget>{
  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
       ...
      ],
    );
  }
}

貳點(diǎn)貳塑煎、構(gòu)建PageView

PageView類(lèi)似于Android里的ViewPager瘪阁,我們可以使用PageController控制PageView 的滑動(dòng)行為,比如設(shè)置滑動(dòng)動(dòng)畫(huà)畦韭、令其滑動(dòng)到指定的頁(yè)面等等疼蛾。可以通過(guò)設(shè)置onPageChanged來(lái)監(jiān)聽(tīng)頁(yè)面的滑動(dòng)艺配,相當(dāng)于Android里的PageListener察郁。每一個(gè)Page里的布局可以通過(guò)children屬性進(jìn)行設(shè)置,例子中每一個(gè)Page里包含一張圖片转唉,圖片是通過(guò)網(wǎng)絡(luò)來(lái)加載的皮钠。代碼如下:

class _AdPictureWidgetState extends State<AdPictureWidget>{
  PageController _pageController = PageController();
  List _adPictures = [];
  
  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
       PageView(
          children: _adPictures.map((json) {
            var adPicture = AdPicture.fromJson(json);//可以先忽略這個(gè)實(shí)體類(lèi)
            return Image.network(
              adPicture.imageUrl,
              fit: BoxFit.fill,//使照片占滿整個(gè)屏幕
            );
          }).toList(),
          onPageChanged: _onPageChanged,
          controller: _pageController,
        ),
      ],
    );
  }
  
  void _onPageChanged(int index) {
   ...
  }
}

貳點(diǎn)叁、構(gòu)建下方的Indicator布局

屏幕下方的一行指示小圓點(diǎn)可以直接使用flutter的TabPageSelector搞定赠法,使用Align控制其顯示在屏幕的下方麦轰。我們只需要使用TabPageSelector的三個(gè)屬性即可,通過(guò)color屬性設(shè)置其未被選中時(shí)的顏色砖织,通過(guò)selectedColor設(shè)置選中時(shí)的顏色款侵,那如何控制選中還是未被選中呢,答案是它的controller屬性侧纯,我們直接new出一個(gè)TabController類(lèi)新锈,將其賦值給controller屬性即可,代碼如下:

class _AdPictureWidgetState extends State<AdPictureWidget>
    with SingleTickerProviderStateMixin {
  TabController _tabController;
  ...
  
  @override
  void initState() {
    _tabController = TabController(length: 0, vsync: this);
    super.initState();
  }

  @override
  void dispose() {
    ...
    _tabController.dispose();
    super.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        PageView(
          ...
        ),
        Align(
          alignment: Alignment(0.0, 0.5),
          child: TabPageSelector(
            color: Colors.white,
            selectedColor: Colors.black,
            controller: _tabController,
          ),
        ),
      ],
    );
  }

貳點(diǎn)肆眶熬、PageViewTabPageSelector聯(lián)動(dòng) & 定時(shí)自動(dòng)翻頁(yè)

二者的聯(lián)動(dòng)很簡(jiǎn)單妹笆,在PageView的滑動(dòng)回調(diào)里調(diào)用_tabControlleranimateTo方法即可實(shí)現(xiàn)二者的聯(lián)動(dòng)块请。如果需要定時(shí)翻頁(yè),則需要使用到一個(gè)Timer的類(lèi)拳缠,詳細(xì)代碼如下:

const timeout = const Duration(seconds: 2);
class _AdPictureWidgetState extends State<AdPictureWidget>
    with SingleTickerProviderStateMixin {
  ...
  Timer _timer;
  int _index = 0;

  @override
  void initState() {
    ...
    _timer = Timer.periodic(timeout, _handleTimeout);//一創(chuàng)建定時(shí)器就啟動(dòng)了墩新,每過(guò)timeout時(shí)間就會(huì)調(diào)用_handleTimeout這個(gè)回調(diào)。
    super.initState();
  }

  @override
  void dispose() {
    ...
    _timer.cancel();
    super.dispose();
  }
  
  _handleTimeout(Timer timer) {
      _index++;
      _pageController.animateToPage(
        _index % (_adPictures.length),//跳轉(zhuǎn)到的位置
        duration: Duration(milliseconds: 16),//跳轉(zhuǎn)的間隔時(shí)間
        curve: Curves.fastOutSlowIn,//跳轉(zhuǎn)動(dòng)畫(huà)
      );
      _tabController.animateTo(_index % (_adPictures.length));
  }
  

貳點(diǎn)五窟坐、循環(huán)翻頁(yè)實(shí)現(xiàn)

原本的數(shù)據(jù)

插入后的數(shù)據(jù)

假設(shè)只有三頁(yè)海渊,實(shí)現(xiàn)循環(huán)播放的原理是在原來(lái)的數(shù)據(jù)基礎(chǔ)上,在最開(kāi)始插入一張?jiān)镜奈岔?yè)狸涌,在最末尾插入一張?jiān)镜氖醉?yè)(看上面兩張圖也許更形象)切省,當(dāng)用戶滑動(dòng)到現(xiàn)在的尾頁(yè)時(shí),程序自動(dòng)的將其滑動(dòng)到現(xiàn)在的第二頁(yè)帕胆,滑動(dòng)的很快對(duì)用戶來(lái)說(shuō)是無(wú)感之的,同理般渡,當(dāng)用戶滑動(dòng)到現(xiàn)在的首頁(yè)時(shí)懒豹,程序自動(dòng)滑動(dòng)到現(xiàn)在的倒數(shù)第二頁(yè)。這種方法在Android里也是挺常用的驯用。

叁脸秽、可運(yùn)行的完整代碼

  • 依賴的第三方庫(kù):
  dio: 1.0.6  
  json_annotation: ^2.0.0
  • 代碼及文件名:

///文件名:AdPictureWidget.dart
class AdPictureWidget extends StatefulWidget {
  @override
  _AdPictureWidgetState createState() => _AdPictureWidgetState();
}

const timeout = const Duration(seconds: 2);

class _AdPictureWidgetState extends State<AdPictureWidget>
    with SingleTickerProviderStateMixin {
  TabController _tabController;
  PageController _pageController = PageController();
  Timer _timer;

  List _adPictures = [];
  int _index = 0;

  @override
  void initState() {
    _tabController = TabController(length: 0, vsync: this);
    _timer = Timer.periodic(timeout, _handleTimeout);
    loadAdPictures();
    super.initState();
  }

  @override
  void dispose() {
    _tabController.dispose();
    _timer.cancel();
    _pageController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        PageView(
          children: _adPictures.map((json) {
            var adPicture = AdPicture.fromJson(json);
            return Image.network(adPicture.imageUrl, fit: BoxFit.fill);
          }).toList(),
          onPageChanged: _onPageChanged,
          controller: _pageController,
        ),
        Align(
          alignment: Alignment(0.0, 0.5),
          child: TabPageSelector(
            color: Colors.white,
            selectedColor: Colors.black,
            controller: _tabController,
          ),
        ),
      ],
    );
  }

  _handleTimeout(Timer timer) {
    if (_adPictures.length - 2 != 0) {
      _index++;
      _pageController.animateToPage(
        _index % (_adPictures.length - 2),
        duration: Duration(milliseconds: 16),
        curve: Curves.fastOutSlowIn,
      );
    }
  }

  void _onPageChanged(int index) {
    _index = index;
    if (index == 0) {
      _tabController.animateTo(_tabController.length - 1);
      _pageController.jumpToPage(_adPictures.length - 2);
    } else if (index == _adPictures.length - 1) {
      _tabController.animateTo(0);
      _pageController.jumpToPage(1);
    } else {
      _tabController.animateTo(index - 1);
    }
  }

  void loadAdPictures() async {
    Dio dio = Dio();
    Response<List> response = await dio
        .get("http://www.wanandroid.com/tools/mockapi/2511/getAdPictures");

    List res = response.data;
    if (res.length != 0) {
      res.insert(0, res[res.length - 1]);
      res.add(res[1]);

      setState(() {
        _adPictures = res;
        _pageController.jumpToPage(1);
        _tabController =
            TabController(length: _adPictures.length - 2, vsync: this);
      });
    }
  }
}

///文件名:AdPicture.dart

library adpicture;

import 'package:json_annotation/json_annotation.dart';

part 'AdPicture.g.dart';

///首頁(yè)輪播圖
@JsonSerializable()
class AdPicture {
  final String imageUrl; //圖片鏈接

  AdPicture({
    this.imageUrl,
  });

  factory AdPicture.fromJson(Map<String, dynamic> json) =>
      _$AdPictureFromJson(json);
}


///文件名:AdPicture.g.dart
part of adpicture;

AdPicture _$AdPictureFromJson(Map<String, dynamic> json) {
  return AdPicture(imageUrl: json['imageUrl'] as String);
}

Map<String, dynamic> _$AdPictureToJson(AdPicture instance) => <String, dynamic>{
      'imageUrl': instance.imageUrl,
    };

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蝴乔,隨后出現(xiàn)的幾起案子记餐,更是在濱河造成了極大的恐慌,老刑警劉巖薇正,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件片酝,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡挖腰,警方通過(guò)查閱死者的電腦和手機(jī)雕沿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)猴仑,“玉大人审轮,你說(shuō)我怎么就攤上這事×伤祝” “怎么了疾渣?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)崖飘。 經(jīng)常有香客問(wèn)我榴捡,道長(zhǎng),這世上最難降的妖魔是什么坐漏? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任薄疚,我火速辦了婚禮碧信,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘街夭。我一直安慰自己砰碴,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布板丽。 她就那樣靜靜地躺著呈枉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪埃碱。 梳的紋絲不亂的頭發(fā)上猖辫,一...
    開(kāi)封第一講書(shū)人閱讀 50,050評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音砚殿,去河邊找鬼啃憎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛似炎,可吹牛的內(nèi)容都是我干的辛萍。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼羡藐,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼贩毕!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起仆嗦,我...
    開(kāi)封第一講書(shū)人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤辉阶,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后瘩扼,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體谆甜,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年邢隧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了店印。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡倒慧,死狀恐怖按摘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情纫谅,我是刑警寧澤炫贤,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站付秕,受9級(jí)特大地震影響兰珍,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜询吴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一掠河、第九天 我趴在偏房一處隱蔽的房頂上張望亮元。 院中可真熱鬧,春花似錦唠摹、人聲如沸爆捞。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)煮甥。三九已至,卻和暖如春藕赞,著一層夾襖步出監(jiān)牢的瞬間成肘,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工斧蜕, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留双霍,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓惩激,卻偏偏與公主長(zhǎng)得像店煞,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子风钻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,860評(píng)論 25 707
  • 1、通過(guò)CocoaPods安裝項(xiàng)目名稱(chēng)項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請(qǐng)求組件 FMDB本地?cái)?shù)據(jù)庫(kù)組件 SD...
    陽(yáng)明先生_X自主閱讀 15,969評(píng)論 3 119
  • 你好酒请,我是玉石玩家張麗云骡技,每天寫(xiě)一篇原創(chuàng)文章分享我的經(jīng)驗(yàn)和觀察,希望能給你一些啟發(fā)和幫助羞反。 ?這是第十二篇原創(chuàng)文章...
    云湛藍(lán)閱讀 644評(píng)論 0 0
  • 親愛(ài)的簡(jiǎn)書(shū)友友們: 因和室友們玩了一個(gè)游戲布朦,想5個(gè)人取同一個(gè)格式的名字,要求:前一個(gè)名字的尾字為后一個(gè)的尾字。5個(gè)...
    松鼠的松子閱讀 283評(píng)論 2 0
  • 07胡小雪20180926理想國(guó)微寫(xiě)作打卡 今天在動(dòng)車(chē)上看《圍城》昼窗,已經(jīng)看到方鴻漸和孫柔嘉結(jié)婚后的日子是趴,經(jīng)常吵吵鬧...
    cher1122閱讀 118評(píng)論 0 0