flutter3.0学习笔记

滚动组件ListView

Preview
  • children
  • ListView.builder
  • ListView.separated
  • 下拉刷新
  • 上拉加载更多
  • 添加固定列表头

在flutter中它可以使子组件沿一个方向上线性排列,并支持滚动。用ListView实现滚动列表有多种方式,或可以设置children属性,或利用构造方法ListView.builder,或用构造 方法ListView.separated。先来看其构造:

ListView({
    super.key,
    super.scrollDirection,//方向
    super.reverse,//reverse为true时,那么滑动方向就是从右往左
    super.controller,
    super.primary,
    super.physics,
    super.shrinkWrap,
    super.padding,
    this.itemExtent,//每个子组件的高度
    this.prototypeItem,
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
    bool addSemanticIndexes = true,
    super.cacheExtent,
    List<Widget> children = const <Widget>[],
    int? semanticChildCount,
    super.dragStartBehavior,
    super.keyboardDismissBehavior,
    super.restorationId,
    super.clipBehavior,
  }) 

children

ListViewchildren属性来实现一个滚动列表是最简单的一种实现方式。例子:

return Scaffold(
    appBar: AppBar(title: const Text('ListView'),),
    body: ListView(
      shrinkWrap: false,
      scrollDirection: Axis.vertical,
      padding: const EdgeInsets.all(30),
      itemExtent: 30,
      children: const [
        Text('1'),
        Text('2'),
        Text('3'),
        Text('4'),
        Text('5'),
        Text('6'),
      ],
    )
);

image.png

shrinkWrap属性为false时,代表ListView会在布局方向上尽可能多的占用空间。shrinkWrap值为true时,则会根据子Widget的大小来确定ListView的大小,shrinkWrap的值默认为false

ListView.builder

ListView.builder适合列表项比较多或者列表项不确定的情况。 例子:

 body: ListView.builder(
      padding: const EdgeInsets.all(30),
      itemCount: 30,//widget数量
      itemExtent: 50,//widget高度
      itemBuilder: (BuildContext context,int index){
        return Text('$index');
      }
  ),

image.png

  • itemBuilder:它是列表项的构建器,类型为IndexedWidgetBuilder,返回值为一个widget。当列表滚动到具体的index位置时,会调用该构建器构建列表项。
  • itemCount:列表项的数量,如果为null,则为无限列表。

ListView.separated

ListView.separated可以在生成的列表项之间添加一个分割组件,它比ListView.builder多了一个separatorBuilder参数,该参数是一个分割组件生成器。 例子:

body: ListView.separated(
    padding: const EdgeInsets.all(30),
    itemBuilder: (BuildContext context,int index){
      return Container(
        height: 50,
        child: ListTile(title: Text('$index'),),
      );
    },
      //设置分割线
    separatorBuilder: (BuildContext context,int index){
      return const Divider(color: Colors.black38,height: 1,);
    },
    itemCount: 30
  ),

image.png

下拉刷新

下拉刷新需要用RefreshIndicatorListView包装一层,然后实现onRefresh方法。 例子:

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

  @override
  State<ListViewPage> createState() => _ListViewPageState();
}

class _ListViewPageState extends State<ListViewPage> {
  var itemCount  = 30;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('滚动组件刷新'),),
      body: RefreshIndicator(
        onRefresh: _onRefresh,
        child: ListView.separated(
            padding: const EdgeInsets.all(30),
            itemBuilder: (BuildContext context,int index){
              return Container(
                height: 50,
                child: ListTile(title: Text('$index'),),
              );
            },
            separatorBuilder: (BuildContext context,int index){
              return const Divider(color: Colors.black38,height: 1,);
            },
            itemCount: itemCount+1
        ),
      ),
    );
  }

  Future _onRefresh(){
    return Future.delayed(Duration(seconds: 1),(){
      print('刷新完成');
      setState((){
        itemCount = 30;
      });
    });
  }

}

image.png

上拉加载更多

上拉加载系统并没有给我们提供好组件,但是我们可以根据ListViewcontroller属性进行监听,等ListView滚动至最后一个item开始上拉加载操作。 完整例子:

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

  @override
  State<ListViewPage> createState() => _ListViewPageState();
}

class _ListViewPageState extends State<ListViewPage> {
  var itemCount  = 50;
  //加载更多
  var isLoading = false;
  final ScrollController _scrollController = ScrollController();
  @override
  void initState() {
    // TODO: implement initState
    _scrollController.addListener(() {
      //监听滑动到最后
      if(isLoading == false && _scrollController.position.pixels > _scrollController.position.maxScrollExtent){
        setState(() {
          isLoading = true;
          _loadMore();
        });
      }
    });
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('滚动组件刷新'),),
      body: RefreshIndicator(
        onRefresh: _onRefresh,
        child: ListView.separated(
            padding: const EdgeInsets.all(30),
            controller: _scrollController,//监听
            itemBuilder: (BuildContext context,int index){
              if(index == itemCount){
                return _getLoadMore();
              }
              return Container(
                height: 50,
                child: ListTile(title: Text('$index'),),
              );
            },
            separatorBuilder: (BuildContext context,int index){
              return const Divider(color: Colors.black38,height: 1,);
            },
            itemCount: itemCount+1
        ),
      ),
    );
  }

  Widget _getLoadMore(){
    if(isLoading == true){
      return Container(
        alignment: Alignment.center,
        child: const SizedBox(
          width: 25.0,
          height: 25.0,
          child: CircularProgressIndicator(strokeWidth: 2.0,),
        ),
      );
    }else{
      return Container(
        alignment: Alignment.center,
        child: const Text('上拉加载'),
      );
    }
  }

  //上拉刷新
  Future _onRefresh(){
    return Future.delayed(Duration(seconds: 1),(){
      print('刷新完成');
      setState((){
        itemCount = 50;
      });
    });
  }

  //下拉加载
Future _loadMore(){
    // 请求接口
    return Future.delayed(Duration(seconds: 2),(){
      setState(() {
        print('加载完成');
        isLoading = false;
        itemCount = 50;
      });
    });
}

}

添加固定列表头

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

  @override
  State<ListViewPage> createState() => _ListViewPageState();
}

class _ListViewPageState extends State<ListViewPage> {
  var itemCount  = 50;
  //加载更多
  var isLoading = false;
  final ScrollController _scrollController = ScrollController();
  @override
  void initState() {
    // TODO: implement initState
    _scrollController.addListener(() {
      //监听滑动到最后
      if(isLoading == false && _scrollController.position.pixels > _scrollController.position.maxScrollExtent){
        setState(() {
          isLoading = true;
          _loadMore();
        });
      }
    });
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('滚动组件刷新'),),
      body: Column(
        children: [
          // const ListTile(title: Text('固定头部'),),
          Container(
            width: double.infinity,
            height: 60,
            color: Colors.yellow,
            child: const ListTile(title: Text('固定头部'),),
          ),
          Expanded(
            child: RefreshIndicator(
              onRefresh: _onRefresh,
              child: ListView.separated(
                  padding: const EdgeInsets.all(30),
                  controller: _scrollController,//监听
                  itemBuilder: (BuildContext context,int index){
                    if(index == itemCount){
                      return _getLoadMore();
                    }
                    return Container(
                      height: 50,
                      child: ListTile(title: Text('$index'),),
                    );
                  },
                  separatorBuilder: (BuildContext context,int index){
                    return const Divider(color: Colors.black38,height: 1,);
                  },
                  itemCount: itemCount+1
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _getLoadMore(){
    if(isLoading == true){
      return Container(
        alignment: Alignment.center,
        child: const SizedBox(
          width: 25.0,
          height: 25.0,
          child: CircularProgressIndicator(strokeWidth: 2.0,),
        ),
      );
    }else{
      return Container(
        alignment: Alignment.center,
        child: const Text('上拉加载'),
      );
    }
  }

  //上拉刷新
  Future _onRefresh(){
    return Future.delayed(Duration(seconds: 1),(){
      print('刷新完成');
      setState((){
        itemCount = 50;
      });
    });
  }

  //下拉加载
Future _loadMore(){
    // 请求接口
    return Future.delayed(Duration(seconds: 2),(){
      setState(() {
        print('加载完成');
        isLoading = false;
        itemCount = 50;
      });
    });
}

}

image.png