Flutter ListView 组件

927 阅读3分钟

Flutter 中的 ListView 组件和 RN 中的 ScrollView/FlatList 非常相似

ListView({
  Axis scrollDirection = Axis.vertical,
  ScrollController controller,
  ScrollPhysics physics,
  bool shrinkWrap = false,
  EdgeInsetsGeometry padding,
  this.itemExtent,
  double cacheExtent,
  List<Widget> children = const <Widget>[],
})
  • scrollDirection: 列表的滚动方向,可选值有Axis的horizontal和vertical,可以看到默认是垂直方向上滚动;
  • controller : 控制器,与列表滚动相关,比如监听列表的滚动事件;
  • physics: 列表滚动至边缘后继续拖动的物理效果,Android与iOS效果不同。Android会呈现出一个波纹状(对应ClampingScrollPhysics),而iOS上有一个回弹的弹性效果(对应BouncingScrollPhysics)。如果你想不同的平台上呈现各自的效果可以使用AlwaysScrollableScrollPhysics,它会根据不同平台自动选用各自的物理效果。如果你想禁用在边缘的拖动效果,那可以使用NeverScrollableScrollPhysics;
  • shrinkWrap: 该属性将决定列表的长度是否仅包裹其内容的长度。当ListView嵌在一个无限长的容器组件中时,shrinkWrap必须为true,否则Flutter会给出警告;
  • padding: 列表内边距;
  • itemExtent: 子元素长度。当列表中的每一项长度是固定的情况下可以指定该值,有助于提高列表的性能(因为它可以帮助ListView在未实际渲染子元素之前就计算出每一项元素的位置);
  • cacheExtent: 预渲染区域长度,ListView会在其可视区域的两边留一个cacheExtent长度的区域作为预渲染区域(对于ListView.build或ListView.separated构造函数创建的列表,不在可视区域和预渲染区域内的子元素不会被创建或会被销毁);
  • children: 容纳子元素的组件数组。
class List extends StatelessWidget {

  const List({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: <Widget>[
        ACard(data: creditCardData),
        BCard(data: petCardData),
        CCard(data: friendCircleData),
      ],
    );
  }
}

class List extends StatelessWidget {

  const List({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Color(0xFFEFEFEF),
      child: ListView.builder(
        itemCount: accountList.length,
        itemBuilder: (context, index) {
          return AccountCard(data: accountList[index]);
        },
      ),
    );
  }
}

class List extends StatelessWidget {

  const List({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ListView.separated(
      itemCount: friendListData.length,
      itemBuilder: (context, index) {
        return FriendCard(data: friendListData[index]);
      },
      separatorBuilder: (context, index) {
        return Divider(
          height: .5,
          indent: 75,
          color: Color(0xFFDDDDDD),
        );
      },
    );
  }
}

下拉刷新

RefreshIndicator的用法十分简单,只要将我们原来的ListView作为其child,并且实现其onRefresh方法就好了

    return RefreshIndicator(
      onRefresh: this.onRefresh,
      child: ListView.separated(
        itemCount: friendListData.length,
        itemBuilder: (context, index) {
          return FriendCard(data: friendListData[index]);
        },
        separatorBuilder: (context, index) {
          return Divider(
            height: .5,
            indent: 75,
            color: Color(0xFFDDDDDD),
          );
        },
      ),
    );

上拉加载


import 'dart:async';

import 'package:flutter/material.dart';

class LoadMoreList extends StatefulWidget {
  const LoadMoreList({Key key}) : super(key: key);

  @override
  _LoadMoreListState createState() => _LoadMoreListState();
}

class _LoadMoreListState extends State<LoadMoreList> {
  bool loading = false;
  ScrollController controller = ScrollController();
  List<CardViewModel> list = List.from(cardList);

  @override
  void initState() {
    super.initState();
    // 给列表滚动添加监听
    this.controller.addListener(() {
      // 滑动到底部的关键判断
      if (
      !this.loading &&
          this.controller.position.pixels >= this.controller.position.maxScrollExtent
      ) {
        // 开始加载数据
        setState(() {
          this.loading = true;
          this.loadMore();
        });
      }
    });
  }

  @override
  void dispose() {
    // 释放资源,组件销毁时
    this.controller.dispose();
    super.dispose();
  }

  Future loadMore() {
    return Future.delayed(const Duration(seconds: 1), () {
      setState(() {
        this.loading = false;
        this.list.addAll(cardList);
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return ListView.separated(
      controller: this.controller,
      itemCount: this.list.length + 1,
      separatorBuilder: (context, index) {
        return Divider(height: .5, color: Color(0xFFEEEEEE));
      },
      itemBuilder: (context, index) {
        if (index < this.list.length) {
          return NewsCard(data: this.list[index]);
        } else {
          return this.genBottom();
        }
      },
    );
  }

  Widget genBottom() {
    if(this.loading) {
      return Container(
        padding: EdgeInsets.symmetric(vertical: 13.5),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              '加载中...',
              style: TextStyle(
                fontSize: 13.5,
                color: Color(0xFF222222),
              ),
            ),
            Padding(padding: EdgeInsets.only(left: 10)),
            SizedBox(
              width: 20,
              height: 20,
              child: CircularProgressIndicator(strokeWidth: 3.5),
            ),
          ],
        ),
      );
    } else {
      return Container(
        padding: EdgeInsets.symmetric(vertical: 13.5),
        alignment: Alignment.center,
        child: Text(
          '加载更多',
          style: TextStyle(
            fontSize: 13.5,
            color: Color(0xFF222222),
          ),
        ),
      );
    }
  }
}

class NewsCard extends StatelessWidget {
  final CardViewModel data;

  const NewsCard({Key key, this.data}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return null;
  }
}

class CardViewModel {
}

const List<CardViewModel> cardList = [];