Flutter 开发实战

235课时
1K学过
8分

课程评价 (0)

请对课程作出评价:
0/300

学员评价

暂无精选评价
3分钟

06 上下刷新列表

毫无争议,必备控件

Flutter 中 为我们提供了 RefreshIndicator 作为内置下拉刷新控件;同时我们通过给 ListView 添加 ScrollController 做滑动监听,在最后增加一个 Item, 作为上滑加载更多的 Loading 显示。

如下代码所示,通过 RefreshIndicator 控件可以简单完成下拉刷新工作,这里需要注意一点是:可以利用 GlobalKey 对外提供 RefreshIndicatorRefreshIndicatorState,这样外部就 可以通过 GlobalKey 调用 globalKey.currentState.show();,主动显示刷新状态并触发 onRefresh

上拉加载更多在代码中是通过 _getListCount() 方法,在原本的数据基础上,增加实际需要渲染的 item 数量给 ListView 实现的,最后通过 ScrollController 监听到底部,触发 onLoadMore

如下代码所示,通过 _getListCount() 方法,还可以配置空页面,头部等常用效果。其实就是在内部通过改变实际item数量与渲染Item,以实现更多配置效果

class _GSYPullLoadWidgetState extends State<GSYPullLoadWidget> {
  ///···
  final ScrollController _scrollController = new ScrollController();

  @override
  void initState() {
    ///增加滑动监听
    _scrollController.addListener(() {
      ///判断当前滑动位置是不是到达底部,触发加载更多回调
      if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {
        if (this.onLoadMore != null && this.control.needLoadMore) {
          this.onLoadMore();
        }
      }
    });
    super.initState();
  }

  ///根据配置状态返回实际列表数量
  ///实际上这里可以根据你的需要做更多的处理
  ///比如多个头部,是否需要空页面,是否需要显示加载更多。
  _getListCount() {
    ///是否需要头部
    if (control.needHeader) {
      ///如果需要头部,用Item 0 的 Widget 作为ListView的头部
      ///列表数量大于0时,因为头部和底部加载更多选项,需要对列表数据总数+2
      return (control.dataList.length > 0) ? control.dataList.length + 2 : control.dataList.length + 1;
    } else {
      ///如果不需要头部,在没有数据时,固定返回数量1用于空页面呈现
      if (control.dataList.length == 0) {
        return 1;
      }

      ///如果有数据,因为部加载更多选项,需要对列表数据总数+1
      return (control.dataList.length > 0) ? control.dataList.length + 1 : control.dataList.length;
    }
  }

  ///根据配置状态返回实际列表渲染Item
  _getItem(int index) {
    if (!control.needHeader && index == control.dataList.length && control.dataList.length != 0) {
      ///如果不需要头部,并且数据不为0,当index等于数据长度时,渲染加载更多Item(因为index是从0开始)
      return _buildProgressIndicator();
    } else if (control.needHeader && index == _getListCount() - 1 && control.dataList.length != 0) {
      ///如果需要头部,并且数据不为0,当index等于实际渲染长度 - 1时,渲染加载更多Item(因为index是从0开始)
      return _buildProgressIndicator();
    } else if (!control.needHeader && control.dataList.length == 0) {
      ///如果不需要头部,并且数据为0,渲染空页面
      return _buildEmpty();
    } else {
      ///回调外部正常渲染Item,如果这里有需要,可以直接返回相对位置的index
      return itemBuilder(context, index);
    }
  }

  @override
  Widget build(BuildContext context) {
    return new RefreshIndicator(
      ///GlobalKey,用户外部获取RefreshIndicator的State,做显示刷新
      key: refreshKey,
      ///下拉刷新触发,返回的是一个Future
      onRefresh: onRefresh,
      child: new ListView.builder(
        ///保持ListView任何情况都能滚动,解决在RefreshIndicator的兼容问题。
        physics: const AlwaysScrollableScrollPhysics(),
        ///根据状态返回子孔健
        itemBuilder: (context, index) {
          return _getItem(index);
        },
        ///根据状态返回数量
        itemCount: _getListCount(),
        ///滑动监听
        controller: _scrollController,
      ),
    );
  }
  
  ///空页面
  Widget _buildEmpty() {
     ///···
  }

  ///上拉加载更多
  Widget _buildProgressIndicator() {
     ///···
  }
}

效果如图