【Flutter】開發之進階Widget(五)

前言

在上一篇中,咱們學習了controller、點擊事件onPressedGestureDetectorTabBarPageView聯動的使用,這一篇,咱們來講說ListView的上拉刷新、下拉加載和輪播圖。java

刷新控件

下拉刷新

官方爲咱們提供了RefreshIndicator,主要有colorbackgroundColordisplacementonRefresh等屬性git

RefreshIndicator(
        //刷新進度條顏色
        color: Colors.black45,
        //背景色
        backgroundColor: Colors.blue,
        //觸發下拉刷新的距離 默認40
        displacement: 40,
        //下拉回調方法,方法須要有async和await關鍵字,沒有await,刷新圖標立馬消失,沒有async,刷新圖標不會消失
        onRefresh: refresh,
        child: ListView.separated(
          itemBuilder: ((context, index) {
            return MoveItem();
          }),
          separatorBuilder: (context, index) {
            return Divider(
              color: Colors.black45,
              height: 10,
            );
          },
          itemCount: count,
        ),
      ),
複製代碼
int count = 2;

  Future refresh() async {
    await Future.delayed(Duration(seconds: 3), () {
      setState(() {
        count = 10;
      });
    });
  }
複製代碼

效果圖以下 github

20190625_174848.gif

上拉加載

上拉加載的話,須要使用ListViewcontroller屬性app

final ScrollController _scrollController = new ScrollController();

 @override
  void initState() {
    ///增長滑動監聽
    _scrollController.addListener(() {
      ///判斷當前滑動位置是否是到達底部,觸發加載更多回調
      if (_scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent) {
        setState(() {
          count += 5;
        });
      }
    });

    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
    _scrollController.dispose();
  }
複製代碼

而後將_scrollController設置給ListViewasync

當咱們運行時卻發現,不只不能上拉加載,連下拉刷新也失效了!ide

這是由於,RefreshIndicatorScrollController有兼容性問題,固然官方也給出瞭解決辦法,給ListView添加以下代碼: physics: const AlwaysScrollableScrollPhysics()oop

效果圖以下 學習

20190625_175404.gif

雖說功能實現了,可是感受效果有點怪怪的,新的item出現前應該有個過渡。 思路以下,當觸發上拉加載時,給ListView添加一個加載中的item,當加載完成後,再移除。 先定義個變量,bool loadMore = false; 當觸發上拉加載時,設置爲true動畫

@override
  void initState() {
    ///增長滑動監聽
    _scrollController.addListener(() {
      ///判斷當前滑動位置是否是到達底部,觸發加載更多回調
      if (_scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent) {
        setState(() {
          loadMore = true;
        });
        getMore();
      }
    });
    super.initState();
  }
複製代碼

加載完成時,設置爲falseui

Future getMore() async {
    await Future.delayed(Duration(seconds: 3), () {
      setState(() {
        loadMore = false;
        count += 5;
      });
    });
  }
複製代碼

這時,item就不能是固定的了

Widget getItem(int index) {
    if (loadMore && index == count) {
      return LoadMoreItem();
    } else {
      return MoveItem();
    }
  }
複製代碼

包括count

int getItemCount() {
    if (loadMore) {
      return count + 1;
    } else {
      return count;
    }
  }
複製代碼

效果圖以下

20190625_174934.gif

完整代碼以下

class ListViewDemo extends StatefulWidget {
  @override
  _ReListViewDemoState createState() => _ReListViewDemoState();
}

class _ReListViewDemoState extends State<ListViewDemo> {
  int count = 2;
  final ScrollController _scrollController = new ScrollController();

  bool loadMore = false;

  Future refresh() async {
    await Future.delayed(Duration(seconds: 3), () {
      setState(() {
        count = 10;
      });
    });
  }

  Future getMore() async {
    await Future.delayed(Duration(seconds: 3), () {
      setState(() {
        loadMore = false;
        count += 5;
      });
    });
  }

  @override
  void initState() {
    ///增長滑動監聽
    _scrollController.addListener(() {
      ///判斷當前滑動位置是否是到達底部,觸發加載更多回調
      if (_scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent) {
        setState(() {
          loadMore = true;
        });
        getMore();
      }
    });
    super.initState();
  }

  Widget getItem(int index) {
    if (loadMore && index == count) {
      return LoadMoreItem();
    } else {
      return MoveItem();
    }
  }

  int getItemCount() {
    if (loadMore) {
      return count + 1;
    } else {
      return count;
    }
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('ListViewDemo'),
        centerTitle: true,
        brightness: Brightness.dark,
      ),
      body: RefreshIndicator(
        //刷新進度條顏色
        color: Colors.black45,
        //背景色
        backgroundColor: Colors.blue,
        ////觸發下拉刷新的距離 默認40
        displacement: 40,
        //下拉回調方法,方法須要有async和await關鍵字,沒有await,刷新圖標立馬消失,沒有async,刷新圖標不會消失
        onRefresh: refresh,
        child: ListView.separated(
          itemBuilder: ((context, index) {
            return getItem(index);
          }),
          separatorBuilder: (context, index) {
            return Divider(
              color: Colors.black45,
              height: 10,
            );
          },
          itemCount: getItemCount(),
          controller: _scrollController,
          //保持ListView任何狀況都能滾動,解決在RefreshIndicator的兼容問題。
          physics: const AlwaysScrollableScrollPhysics(), ), ), );
  }
}
複製代碼

輪播圖

首先,用到的是PageView,以及PageController,這些以前已經說過,就不在細說

Widget _pageView() {
    return PageView(
      children: <Widget>[
        Image.network(
          'http://img4.imgtn.bdimg.com/it/u=1621655683,865218969&fm=200&gp=0.jpg',
          height: 150,
          fit: BoxFit.fitWidth,
        ),
        Image.network(
          'http://img1.imgtn.bdimg.com/it/u=1901690610,3955011377&fm=200&gp=0.jpg',
          height: 150,
          fit: BoxFit.fitWidth,
        ),
        Image.network(
          'http://img3.imgtn.bdimg.com/it/u=1546158593,2358526642&fm=200&gp=0.jpg',
          height: 150,
          fit: BoxFit.fitWidth,
        ),
      ],
      controller: pageController,
    );
  }
複製代碼

其次,須要一個定時器,別忘記取消

int count = 3;
  int currentPosition = 0;

  @override
  void initState() {
    super.initState();
    _timer = new Timer.periodic(Duration(seconds: 2), (time) {
       //每2秒執行一次
      changePage();
    });
  }

  @override
  void dispose() {
    super.dispose();
    _timer.cancel();
    pageController.dispose();
  }
  void changePage() {
      pageController.animateToPage(currentPosition % count,
          duration: Duration(milliseconds: 200), curve: Curves.fastOutSlowIn);
    currentPosition++;
  }
複製代碼

效果圖以下

20190625_175123.gif

當咱們手動滑動時,currentPosition會錯亂,咱們須要對其進行調整,須要用到的是onPageChanged屬性

onPageChanged: (index) {
        _timer.cancel();
        currentPosition = index;
        _timer = new Timer.periodic(Duration(seconds: 2), (time) {
          changePage();
        });
      },
複製代碼

固然,這麼經常使用的控件,已經有造好的輪子了 flutter_swiper

首先添加依賴flutter_swiper: ^1.1.6

下面是一些經常使用屬性

Widget _swiper() {
    return new Swiper(
      itemBuilder: (BuildContext context, int index) {
        return new Image.network(
          "http://img4.imgtn.bdimg.com/it/u=1621655683,865218969&fm=200&gp=0.jpg",
          fit: BoxFit.fitWidth,
          height: 150,
        );
      },
      itemCount: 3,
      //動畫時間,默認300.0毫秒
      duration: 300,
      //初始位置
      index: 0,
      //無限輪播模式開關
      loop: true,
      //是否自動播放,默認false
      autoplay: true,
      layout: SwiperLayout.DEFAULT,
      //滾動方式
      scrollDirection: Axis.horizontal,
      //點擊輪播的事件
      onTap: (index) {},
      //用戶拖拽的時候,是否中止自動播放
      autoplayDisableOnInteraction: true,
      //指示器
      pagination: new SwiperPagination(),
      //左右箭頭
      control: null,
    );
  }
複製代碼

你的承認,是我堅持更新博客的動力,若是以爲有用,就請點個贊,謝謝

相關文章
相關標籤/搜索