前言
本文介绍 Flutter 中可滚动的 Widget,主要有 ListView、GridView和PageView。
一、ListView
ListView 是可以线性排列子Widget 的可滚动Widget。ListView 可以和数据绑定用来实现瀑布流。
1. 使用方法
① 使用默认的构造函数,给 children 属性赋值
② 使用 ListView.builder,可用于和数据绑定实现大量或无限的列表
③ 使用 ListView.separated,具有分割项的 ListView.builder
④ 使用 ListView.custom,需要使用 SliverChildDelegate
(1)使用默认的构造函数,给 children 属性赋值
- 使用场景:
适用于数据量少的情况 - 代码示例:
import 'package:flutter/material.dart';
class ListViewDefaultWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: new Scaffold(
appBar: new AppBar(
title: new Text('ListViewDefaultWidget'),
),
body: ListView(
children: <Widget>[
ListTile(
title: new Text('Title1'),
),
ListTile(
title: new Text('Title2'),
),
ListTile(
title: new Text('Title3'),
),
ListTile(
title: new Text('Title4'),
),
ListTile(
title: new Text('Title5'),
),
ListTile(
title: new Text('Title6'),
),
],
),
),
);
}
}
-
运行结果:
(2)使用 ListView.builder,可用于和数据绑定实现大量或无限的列表
- 使用场景:
适用于构建大量或无限的列表,并且 ListView.builder 只会构建那些实际可见的子Widget。
大部分属性和 ListView 一样,多了 itemCount 和 @required IndexedWidgetBuilder itemBuilder 属性。
itemCount:代表子Widget 的数量,可以让 ListView 预估最大滑动距离,从而提升性能。如果不赋值,为null,则子节点数由[itemBuilder]返回null的最小索引确定。
@required IndexedWidgetBuilder itemBuilder:itemBuilder 用于创建实际可见的子Widget,只有索引大于或等于零且小于 itemCount 才会调用 itemBuilder。
- 代码示例:
import 'package:flutter/material.dart';
void main() => runApp(ListViewBuilderWidget(
items: List<String>.generate(10000, (i) => "Item $I"),
));
class ListViewBuilderWidget extends StatelessWidget {
List<String> items;
ListViewBuilderWidget({Key key, @required this.items}) :super(key: key);
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: new Scaffold(
appBar: new AppBar(
title: new Text('ListViewBuilderWidget'),
),
body: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('${items[index]}'),
);
}),
),
);
}
}
(3)使用 ListView.separated,具有分割项的 ListView.builder
- 使用场景:
适用于需要分割线的列表。
相比 ListView.builder 多了一个 separatorBuilder。
- 代码示例:
import 'package:flutter/material.dart';
class ListViewBuilderWidget extends StatelessWidget {
List<String> items;
ListViewBuilderWidget({Key key, @required this.items}) :super(key: key);
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: new Scaffold(
appBar: new AppBar(
title: new Text('ListViewBuilderWidget'),
),
body: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('${items[index]}'),
);
}),
),
);
}
}
-
运行结果:
(4)使用 ListView.custom,需要使用 SliverChildDelegate
- 使用场景:
SliverChildDelegate 有个默认实现 SliverChildListDelegate,我们可以用 SliverChildListDelegate 来实现 ListView.custom。 - 代码示例:
import 'package:flutter/material.dart';
class ListViewCustomWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: new Scaffold(
appBar: new AppBar(
title: new Text('ListViewCustomWidget'),
),
body: ListView.custom(
childrenDelegate: new SliverChildListDelegate(<Widget>[
ListTile(
title: new Text('Title1'),
),
ListTile(
title: new Text('Title2'),
),
ListTile(
title: new Text('Title3'),
),
ListTile(
title: new Text('Title4'),
),
ListTile(
title: new Text('Title5'),
),
])),
),
);
}
}
2. ListView 的构造函数及参数说明
class ListView extends BoxScrollView {
ListView({
Key key,//可选;类型Key;Widget的标识
Axis scrollDirection = Axis.vertical,//可选;类型Axis;滑动的方向
bool reverse = false,//可选;类型bool;列表项的排列顺序
ScrollController controller,//可选;类型ScrollController;控制ListView的滚动位置
bool primary,//可选;类型bool;是否是父级关联的主滚动视图
ScrollPhysics physics,//可选;类型ScrollPhysics;设置滚动效果
bool shrinkWrap = false,//可选;类型bool;是否根据列表项的总长度来设置ListView的长度
EdgeInsetsGeometry padding,//可选;类型EdgeInsetsGeometry;ListView 的内边距
this.itemExtent,//可选;类型double;列表项的大小
bool addAutomaticKeepAlives = true,
bool addRepaintBoundaries = true,
bool addSemanticIndexes = true,
double cacheExtent,
List<Widget> children = const <Widget>[],
int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.down,
})
...
}
二、GridView
GridView 是一个可以构建二维网格列表的可滚动Widget。
1. 使用方法:
① 使用默认的构造函数,给 children 属性赋值
② 使用 GridView.count
③ 使用 GridView.extent
④ 使用 GridView.builder,可用于和数据绑定实现大量或无限的列表
⑤ 使用 GridView.custom
(1)使用默认的构造函数,给 children 属性赋值
- 使用场景:
适用于使用少量 子Widget 的GridView - 代码示例:
import 'package:flutter/material.dart';
class GridViewDefaultWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(title: new Text('Flutter 可滚动Widget -- GridView')),
body: GridView(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),//3列
children: <Widget>[
ListTile(
title: Text('Title1'),
),
ListTile(
title: Text('Title2'),
),
ListTile(
title: Text('Title3'),
),
ListTile(
title: Text('Title4'),
),
ListTile(
title: Text('Title5'),
),
ListTile(
title: Text('Title6'),
),
],
)),
);
}
}
-
运行结果:
(2)使用 GridView.count
将构造函数里的 gridDelegate 属性,拆分成了 crossAxisCount、mainAxisSpacing、crossAxisSpacing 和 childAspectRatio。
- 代码示例:
import 'package:flutter/material.dart';
class GridViewCountWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(title: new Text('Flutter 可滚动Widget -- GridView')),
body: GridView.count(
crossAxisCount: 3,
children: <Widget>[
ListTile(
title: Text('Title1'),
),
ListTile(
title: Text('Title2'),
),
ListTile(
title: Text('Title3'),
),
ListTile(
title: Text('Title4'),
),
ListTile(
title: Text('Title5'),
),
ListTile(
title: Text('Title6'),
),
],
)),
);
}
}
(3)使用 GridView.extent
布局算法不一样
- 代码示例:
import 'package:flutter/material.dart';
class GridViewExtentWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(title: new Text('Flutter 可滚动Widget -- GridView')),
body: GridView.extent(
maxCrossAxisExtent: 300,
children: <Widget>[
ListTile(
title: Text('Title1'),
),
ListTile(
title: Text('Title2'),
),
ListTile(
title: Text('Title3'),
),
ListTile(
title: Text('Title4'),
),
ListTile(
title: Text('Title5'),
),
ListTile(
title: Text('Title6'),
),
ListTile(
title: Text('Title7'),
),
],
)),
);
}
}
(4)使用 GridView.builder,可用于和数据绑定实现大量或无限的列表
- 使用场景:
用于构建大量或无限的列表,而且只会构建实际可见的子Widget。 - 代码示例:
import 'package:flutter/material.dart';
void main() => runApp(GridViewBuilderWidget(
items: List<String>.generate(10000, (i) => "Item $I"),
));
class GridViewBuilderWidget extends StatelessWidget {
final List<String> items;
GridViewBuilderWidget({Key key, @required this.items}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Test',
home: Scaffold(
appBar: AppBar(title: new Text('Flutter 可滚动Widget -- GridView')),
body: GridView.builder(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 4),
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('${items[index]}'),
);
},
),
),
);
}
}
(5)使用 GridView.custom
- 使用场景:
增加了 childrenDelegate 的属性,类型为 SliverChildDelegate,可以自定义子Widget。 - 代码示例:
import 'package:flutter/material.dart';
void main() => runApp(GridViewCustomWidget(
items: List<String>.generate(10000, (i) => "Item $I"),
));
class GridViewCustomWidget extends StatelessWidget {
final List<String> items;
GridViewCustomWidget({Key key, @required this.items}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Test',
home: Scaffold(
appBar: AppBar(title: new Text('Flutter 可滚动Widget -- GridView')),
body: GridView.custom(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
childrenDelegate: SliverChildListDelegate(<Widget>[
ListTile(
title: Text('Title1'),
),
ListTile(
title: Text('Title2'),
),
ListTile(
title: Text('Title3'),
),
ListTile(
title: Text('Title4'),
),
ListTile(
title: Text('Title5'),
),
ListTile(
title: Text('Title6'),
),
ListTile(
title: Text('Title7'),
),
ListTile(
title: Text('Title8'),
),
]),
),
),
);
}
}
2. GridView 构造函数和参数说明
class GridView extends BoxScrollView {
GridView({
Key key,//可选;类型 Key;Widget的标识
Axis scrollDirection = Axis.vertical,//可选;类型 Axis;滑动的方向
bool reverse = false,//可选;类型 bool;列表项的排列顺序
ScrollController controller,//可选;类型 ScrollController;控制滚动位置
bool primary,//可选;类型 bool;是否是与父级关联的主滚动视图
ScrollPhysics physics,//可选;类型 ScrollPhysics;设置滚动效果,值必须为ScrollPhysics的子类,比如:AlwaysScrollableScrollPhysics():可以让 GridView 里没有足够的内容也能滑动,ScrollPhysics():GridView 在没有足够的内容的时候不能滑动
bool shrinkWrap = false,//可选;类型 bool;是否根据列表项的总长度来设置 GridView 的长度
EdgeInsetsGeometry padding,//可选;类型 EdgeInsetsGeometry;内边距
@required this.gridDelegate,//必选;类型 SliverGridDelegate;控制子Widget布局的委托
bool addAutomaticKeepAlives = true,
bool addRepaintBoundaries = true,
bool addSemanticIndexes = true,
double cacheExtent,
List<Widget> children = const <Widget>[],
int semanticChildCount,
})
...
}
三、PageView
PageView 是可以一页一页滑动的可滚动Widget,类似 Android 中ViewPager。
1.使用方式:
① 使用默认的构造函数
② 使用 PageView.builder
③ 使用 PageView.custom
(1)使用默认的构造函数,给 children 属性赋值
- 使用场景:
只适用于那些只有少量子Widget 的 PageView。 - 代码示例:
import 'package:flutter/material.dart';
class PageViewDefaultWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: new Text('PageViewDefaultWidget'),
),
body: PageView(
onPageChanged: (index) {
print('current page $index');
},
children: <Widget>[
ListTile(
title: new Text('Title0'),
),
ListTile(
title: new Text('Title1'),
),
ListTile(
title: new Text('Title2'),
),
ListTile(
title: new Text('Title3'),
),
ListTile(
title: new Text('Title4'),
),
],
),
),
);
}
}
-
运行结果:
(2) 使用 PageView.builder
- 使用场景:
用于构建大量和无限的列表。而且只会构建那些实际可见的子Widget。 - 代码示例:
多了 itemCount 和 itemBuilder 属性
void main() => runApp(PageViewBuilderWidget(
items: List<String>.generate(10000, (i) => "Item $I"),
));
class PageViewBuilderWidget extends StatelessWidget {
final List<String> items;
PageViewBuilderWidget({Key key, @required this.items}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Test',
home: Scaffold(
appBar: AppBar(title: new Text('Flutter 可滚动Widget -- PageView')),
body: PageView.builder(
onPageChanged: (index) {
print('current page $index ');
},
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('${items[index]}'),
);
},
),
),
);
}
}
(3)使用 PageView.custom
- 使用场景:
增加了childrenDelegate 的属性,类型为 SliverChildDelegate,具有自定义子Widget 的能力,不合适大量数据。 - 代码示例:
import 'package:flutter/material.dart';
void main() => runApp(PageViewCustomWidget());
class PageViewCustomWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Test',
home: Scaffold(
appBar: AppBar(title: new Text('Flutter 可滚动Widget -- PageView')),
body: PageView.custom(
onPageChanged: (index) {
print('current page $index ');
},
childrenDelegate: SliverChildListDelegate(<Widget>[
ListTile(title: Text('Title0')),
ListTile(title: Text('Title1')),
ListTile(title: Text('Title2')),
ListTile(title: Text('Title3')),
ListTile(title: Text('Title4')),
]),
)),
);
}
}
2. PageView 的构造函数和参数说明
class PageView extends StatefulWidget {
PageView({
Key key,//可选;类型 Key;Widget的标识
this.scrollDirection = Axis.horizontal,//可选;类型 Axis;滑动方向
this.reverse = false,//可选;类型 bool;子Widget的排列顺序
PageController controller,//可选;类型 PageController;控制滑动
this.physics,//可选;类型 ScrollPhysics;设置滚动效果
this.pageSnapping = true,//可选;类型 bool;默认值false,用来禁止页面捕捉,对自定义滚动行为有用
this.onPageChanged,//可选;类型 ValueChanged<int>;监听页面的切换
List<Widget> children = const <Widget>[],//可选;类型 List<Widget;PageView的列表项
this.dragStartBehavior = DragStartBehavior.down,//可选;类型 DragStartBehavior;确定处理拖动开始行为的方式。如果设置为DragStartBehavior.start,则在检测到拖动手势时将开始滚动拖动行为。如果设置为DragStartBehavior.down,它将在首次检测到向下事件时开始
}) :
...
}
总结
本文主要介绍的可滚动的 Widget,ListView、GridView 和 PageView。除了这三个之外还有很多可以滚动的 Widget,如 SingleChildScrollView、CustomScrollView 等等,若需要了解更多的 Widget 可以去查看官网的介绍https://flutter.dev/docs/development/ui/widgets/scrolling。