Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

下拉刷新 & 加载更多 #25

Open
Nealyang opened this issue Mar 19, 2019 · 1 comment
Open

下拉刷新 & 加载更多 #25

Nealyang opened this issue Mar 19, 2019 · 1 comment
Labels

Comments

@Nealyang
Copy link
Owner

前言

目前,我们的app首页已经有个样子出来了,并且数据也不是开玩笑的,都是真实数据,对于首页而言,我们当然还需要添加翻页的功能,甚至包括下拉刷新的功能。幸运是的,Flutter对于这方面来说,给我们封装的还是相当的便利的。

修复上面请求接口bug

今天重新打开页面的时候发现了页面报错,debug了一下发现是接口数据过来有些字段为空导致的。遂这里请求接口以及数据的处理上,做了一些调整

  • lib/util/data_utils.dart
      try {
        IndexCell cellData = new IndexCell.fromJson(responseList[i]);
        resultList.add(cellData);
      } catch (e) {
        // No specified type, handles all
        print('Something really unknown: $i');
      }

getIndexListData 方法里面加了一层异常捕获,毕竟不是我们自己跟后端的约束,对业务也并非清楚,所以里面一些字段我们也不清楚,这些问题也是难免。

  • lib/model/indexCell.dart
  factory IndexCell.fromJson(Map<String, dynamic> json) {
    String _tag = '';
    if(json['tags'].length>0){
      _tag = '${json['tags'][0]['title']}/';
    }
    return IndexCell(
      hot: json['hot'],
      collectionCount: json['collectionCount'],
      commentCount: json['commentsCount'],
      tag: '$_tag${json['category']['name']}',
      username: json['user']['username'],
      createdTime: Util.getTimeDuration(json['createdAt']),
      title: json['title'],
      detailUrl: json['originalUrl'],
      isCollection: json['type'] ,
    );
  }

添加了对tag的为空判断

下拉刷新

下拉刷新其实就是在我们之前调用下我们的getList方法。难点可能就是如何触发这个下拉刷新呢?非常幸运,Material 中 提供了 RefreshIndicator widget

同样,我们可以从他的源码中看到他的属性

img

  • lib/index_page.dart
  // 下拉刷新
  Future<void> _onRefresh() async{//The RefreshIndicator onRefresh callback must return a Future.
    _listData.clear();
    setState(() {
      _listData = _listData;
      //注意这里需要重置一切请求条件
      _hasMore = true;
    });
    getList(false);
    return null;
  }
  //build 方法返回如下
    return RefreshIndicator(
      onRefresh: _onRefresh,
      child: ListView.builder(
        itemCount: _listData.length + 2, //添加一个header 和 loadMore
        itemBuilder: (context, index) => _renderList(context, index),
      ),
    );

下拉刷新的方法中setState是为了重新build,毕竟空列表页面会有loading的出现。

loadMore的实现

  • lib/index_page.dart
    ListView.builder中有一个属性为Controller,他可以监听页面的滚动,所以我们在ListView.builder中加入这个属性
  ScrollController _scrollController = new ScrollController();
  

  // build方法中
    return RefreshIndicator(
      onRefresh: _onRefresh,
      child: ListView.builder(
        itemCount: _listData.length + 2, //添加一个header 和 loadMore
        itemBuilder: (context, index) => _renderList(context, index),
         controller: _scrollController,
      ),
    );

在页面初始化的时候键入滚动监听,并且触发getList方法

  @override
    void initState() {
      super.initState();
      getList(false);
      _scrollController.addListener(() {
        if (_scrollController.position.pixels ==
            _scrollController.position.maxScrollExtent) {
              print('loadMore');
          getList(true);
        }
      });
    }

一些别的变量的作用,由于触底这个动作可以反复触发,而网络请求是异步的,所以我们不可能在每一次触底都要去发送过一次请求,而是再一次请求结束后,再次触底才会再次发送请求。所以这里我们加入了 _isRequesting 的flag

  bool _isRequesting = false; //是否正在请求数据的flag
  bool _hasMore = true;

请求的方法也做了稍微的跳转

  getList(bool isLoadMore) {
    if (_isRequesting || !_hasMore) return;
    if (!isLoadMore) {
      // reload的时候重置page
      _pageIndex = 0;
    }
    _params['before'] = pageIndexArray[_pageIndex];
    _isRequesting = true;
    DataUtils.getIndexListData(_params).then((result) {
      _pageIndex += 1;
      List<IndexCell> resultList = new List();
      if(isLoadMore){
        resultList.addAll(_listData);
      }
      resultList.addAll(result);
      setState(() {
        _listData = resultList;
        _hasMore = _pageIndex < pageIndexArray.length;
        _isRequesting = false;
      });
    });
  }

添加加载器

页面触底,发送请求,讲道理应该是要给用户一个反馈的,由于这个可以很多页面公用,所以当然,我们将其封装为一个widget

  • lib/widgets/load_more.dart
    import 'package:flutter/material.dart';
    
    class LoadMore extends StatelessWidget {
      final bool hasMore;
    
      LoadMore(this.hasMore);
    
      @override
      Widget build(BuildContext context) {
        if (hasMore) {
          return Container(
            height: 70.0,
            child: Center(
              child: Opacity(
                opacity: 1.0,
                child: CircularProgressIndicator(
                  strokeWidth: 3.0,
                ),
              ),
            ),
          );
        }
        return Container(
          height: 70.0,
          child: Center(
            child: Text('亲,我也是有底线的',
                style: TextStyle(color: Theme.of(context).accentColor)),
          ),
        );
      }
    }

最终,我们的页面效果就出来了。 代码地址

loadMore

总结

通过这一章节,你应该学会

  • Dart中异常的处理和捕获
  • 下拉刷新
  • 加载更多
@0xEclair
Copy link

将CircularProgressIndicator传给ListView.builder的时候为什么它转了一会儿,单独测试的时候CircularProgressIndicator一直在转。。。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants