赞
踩
本示例是在 Linux 16.04.1-Ubuntu 搭配 VS Code 使用。
当组件内容超过当前显示视口(ViewPort)时,如果不做处理,Flutter 会提示 Overflow 错误。
针对 overflow 问题,flutter 提供了可滚动组件去显示长列表和长布局。
可滚动组件的核心组件 Scrollable
Scrollable({
//...
this.axisDirection = AxisDirection.down, // 滚动方向
this.controller, // 接受 ScrollController 对象,控制滚动位置和监听滚动事件
this.physics, // 接受 ScrollPhysics 对象,响应用户操作。
@required this.viewportBuilder, // 后面介绍
})
基于Sliver的延迟构建
因为可滚动组件的子组件可能会非常多,如果一次性将全部子组件构建出来会非常耗费内存。因此 Flutter 提出一个 Sliver 的概念,一个可滚动组件如果支持 Sliver 模型,那么该滚动可以将子组件分成多个 Sliver, 只有当 Sliver 出现在 ViewPort 时才去构建它。
类似于 Android 中的 ScrollView。不支持 Sliver 的延迟实例化模型,因此只适合不太多的内容。
class SingleChildScrollViewTestRoute extends StatelessWidget { @override Widget build(BuildContext context) { String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; return Scrollbar( // 显示进度条 child: SingleChildScrollView( padding: EdgeInsets.all(16.0), child: Center( child: Column( //动态创建一个List<Widget> children: str.split("") //每一个字母都用一个Text显示,字体为原来的两倍 .map((c) => Text(c, textScaleFactor: 2.0,)) .toList(), ), ), ), ); } }
可以沿一个方向线性排布所有子组件,支持 Sliver 的延迟实例化模型。
实现一个可以自加载数据的 listview,并设置 listview 的标题,在 listview 滚动的时候其标题一直置顶。
class ListViewRoute extends StatefulWidget { @override _ListViewState createState() => new _ListViewState(); } class _ListViewState extends State<ListViewRoute> { static const loadingTag = "##loading##"; var _words = <String>[loadingTag]; void _retrieveData() { print("_retrieveData"); Future.delayed(Duration(seconds: 2)).then((e) { setState(() { _words.insertAll(_words.length - 1, generateWordPairs().take(20).map((e) => e.asPascalCase).toList() ); }); }); } @override Widget build(BuildContext context) { Widget divider1 = Divider(color: Colors.blue,); Widget divider2 = Divider(color: Colors.green); return Scaffold( appBar: AppBar( title: Text("ListView"), ), body: Column( children: <Widget>[ Container ( decoration: BoxDecoration ( color: Colors.purple, ), child: ListTile(title:Text("Word_List"), trailing: Icon(Icons.keyboard_arrow_right), leading: Icon(Icons.list_alt), onTap: () => print(_words.length),), ), Expanded( child: ListView.separated( itemCount: _words.length, itemBuilder: (context, index) { if(_words[index] == loadingTag) { if(_words.length - 1 < 100 ) { _retrieveData(); return Container( padding: const EdgeInsets.all(16.0), alignment: Alignment.center, child: SizedBox( width: 24.0, height: 24.0, child: CircularProgressIndicator(strokeWidth: 2.0,), ), ); } else { return Container( alignment: Alignment.center, padding: EdgeInsets.all(16.0), child: Text("没有更多了", style: TextStyle(color: Colors.grey),) ); } } return ListTile(title: Text(_words[index])); }, separatorBuilder: (context, index) { return index % 2 == 0 ? divider1 : divider2; } ), ) ], ) ); } }
利用 GridView 可以构建一个二维网格列表。
class GridViewRoute extends StatefulWidget { @override _GridViewState createState() => new _GridViewState(); } class _GridViewState extends State<GridViewRoute> { List<IconData> _icons = []; @override void initState() { super.initState(); _retrieveIcons(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("GridView"), ), body: GridView.builder( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, childAspectRatio: 1.0, ), itemCount: _icons.length, itemBuilder: (context, index) { if(index == _icons.length - 1 && _icons.length < 200) { _retrieveIcons(); } return Icon(_icons[index]); }, ), ); } void _retrieveIcons() { Future.delayed(Duration(milliseconds: 200)).then((e) { setState(() { _icons.addAll([ Icons.ac_unit, Icons.airport_shuttle, Icons.all_inclusive, Icons.beach_access, Icons.cake, Icons.free_breakfast, ]); }); }); } }
CustomScrollView 可以将多个 Sliver 合在一起,这些 Sliver 公用 CustomScrollView 的 Scrollable,实现统一的滑动效果。
class CustomScrollViewRoute extends StatelessWidget { @override Widget build(BuildContext context) { return Material( child: CustomScrollView( slivers: <Widget>[ //AppBar,包含一个导航栏 SliverAppBar( pinned: true, expandedHeight: 250.0, flexibleSpace: FlexibleSpaceBar( title: const Text('Demo'), ), ), SliverPadding( padding: const EdgeInsets.all(8.0), sliver: new SliverGrid( //Grid gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, //Grid按两列显示 mainAxisSpacing: 10.0, crossAxisSpacing: 10.0, childAspectRatio: 4.0, ), delegate: new SliverChildBuilderDelegate( (BuildContext context, int index) { //创建子widget return new Container( alignment: Alignment.center, color: Colors.cyan[100 * (index % 9)], child: new Text('grid item $index'), ); }, childCount: 10, ), ), ), //List new SliverFixedExtentList( itemExtent: 50.0, delegate: new SliverChildBuilderDelegate( (BuildContext context, int index) { //创建列表项 return new Container( alignment: Alignment.center, color: Colors.lightBlue[100 * (index % 9)], child: new Text('list item $index'), ); }, childCount: 10 ), ), ], ), ); } }
控制可滚动组件的滚动位置
//新建一个 ScrollController 工具类 class ScrollControllerUtil { ScrollController _controller = new ScrollController(); //在 init 的时候 register void init(String tag) { _controller.addListener(() { print(tag + " : $_controller.offset"); }); } //不需要的时候 dispose,避免内存泄漏 void dispose() { _controller.dispose(); } ScrollController getController() { return _controller; } } //使用 //在 gridview 或者 listview 的 control 属性中设置 ScrollController
NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification notification) {
double progress = notification.metrics.pixels /
notification.metrics.maxScrollExtent;
//重新构建
setState(() {
_progress = "${(progress * 100).toInt()}%";
});
print("BottomEdge: ${notification.metrics.extentAfter == 0}");
return true;
},
);
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。