欢迎点赞+关注。你的鼓励是我写作的动力!
ListView是一个类似列的widget,它的内容对于其渲染框太长时会自动提供滚动。
ListView 摘要:
适用于构建少量子级的滚动列表,默认滚动方法垂直的
scrollDirection: Axis.vertical
示例:用ListView实现一个banner功能
myBannerView()中实现水平滚动:scrollDirection: Axis.horizontal
import 'package:flutter/material.dart';
Widget myBannerView() {
return ListView(
scrollDirection: Axis.horizontal,
children: [
Container(
width: 360,
height: 200,
color: Colors.amber[600],
child: Center(
child: Image.asset('images/banner.png',fit: BoxFit.fill,
width: double.infinity,
height: double.infinity,
),
),
),
Container(
width: 360,
height: 200,
color: Colors.amber[600],
child: Center(
child: Image.asset('images/banner.png',fit: BoxFit.fill,
width: double.infinity,
height: double.infinity,
),
),
),
Container(
width: 360,
height: 200,
color: Colors.amber[500],
child: Center(
//https://picsum.photos/250?image=9
child: Image.network('http://oss.suning.com/uedtool/png_bucket/20195/20190522095508918_灵栖ppt首页5.png',
fit: BoxFit.fill,
width: double.infinity,
height: double.infinity,
),
),
),
Container(
width: 360,
height: 200,
color: Colors.amber[100],
child: const Center(child: Text('Entry C')),
),
],
);
}
class ScrollListView extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Use the Todo to create our UI
return new Scaffold(
appBar: new AppBar(
title: new Text("scroll banner"),
),
body: Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
Container(
width: 360,
height: 200,
color: Colors.red,
child: myBannerView(),
),
],
),
),
);
}
}
上面涉及一个问题:图片如何充满父布局
即使Image.asset中设置了fit: BoxFit.cover,还是不满屏
最终需要在Image.asset增加
width: double.infinity,
height: double.infinity
这两个属性才可以满屏显示。
上面是一种图片充满父布局的方法,还有其他的方法不再陈述。
ListView.builder利用IndexedWidgetBuilder来按需构造。这个构造函数适合于具有大量(或无限)子视图的列表视图,因为构建器只对那些实际可见的子视图调用。
ListTile 是快捷cell 的Widget
// 函数2
Widget myListView2(BuildContext context){
return ListView.builder(
itemCount: 20,
itemBuilder: (context, index) {
return new ListTile(
title: new Text("我是主标题"),
subtitle:new Text("我是副标题") ,
// When a user taps on the ListTile, navigate to the DetailScreen.
// Notice that we're not only creating a new DetailScreen, we're
// also passing the current todo through to it!
onTap: () {
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => new DetailScreen(todo: todos[index]),
),
);
},
);
},
);
}
// 调用的地方
body: myListView2(context),
有直接的分隔符设置方式,对分隔符列表应用更实用;设置 separatorBuilder 属性即可;相当于iOS 的区头、区尾。
下面是一个类中的全部代码
// 数据源
final List titleItems = [
'自定义cell',
'GridView',
'Stack Test',
'pages',
'zoom_out_map',
'zoom_out',
'youtube_searched_for',
'wifi_tethering',
'wifi_lock',
'widgets',
'weekend',
'web',
'图accessible',
'ac_unit',
];
final List iconItems = [
new Icon(Icons.keyboard, size: 50),
new Icon(Icons.print, size: 50),
new Icon(Icons.router, size: 50),
new Icon(Icons.pages, size: 50),
new Icon(Icons.zoom_out_map, size: 50),
new Icon(Icons.zoom_out, size: 50),
new Icon(Icons.youtube_searched_for, size: 50),
new Icon(Icons.wifi_tethering, size: 50),
new Icon(Icons.wifi_lock, size: 50),
new Icon(Icons.widgets, size: 50),
new Icon(Icons.weekend, size: 50),
new Icon(Icons.web, size: 50),
new Icon(Icons.accessible, size: 50),
new Icon(Icons.ac_unit, size: 50),
];
final List subTitleItems = [
'subTitle: CustomCell',
'subTitle: GardViewTest',
'subTitle: StackTest',
'subTitle: pages',
'subTitle: zoom_out_map',
'subTitle: zoom_out',
'subTitle: youtube_searched_for',
'subTitle: wifi_tethering',
'subTitle: wifi_lock',
'subTitle: widgets',
'subTitle: weekend',
'subTitle: web',
'subTitle: accessible',
'subTitle: ac_unit',
];
class DemoList extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('DemoList'),
),
body: new ListView.separated(
itemCount: 20,
itemBuilder: (context, index) {
return new ListTile(
contentPadding: EdgeInsets.fromLTRB(10, 0, -5, 20),
leading: iconItems[index % 10],
title: Container(
padding: EdgeInsets.fromLTRB(10, 5, 0, 30),
child: new Text(
titleItems[index % 10],
style: TextStyle(fontSize: 18),
),
),
subtitle: new Text(subTitleItems[index % 10]),
trailing: new Icon(
Icons.keyboard_arrow_right,
size: 40,
color: Colors.black,
),
onTap: () {
routeSubpage(context, index);
});
},
//和itemBuilder 同级别的执行
separatorBuilder: (BuildContext context, int index) {
if (index == 2) {
return new Container(
height: 40.0,
color: Colors.red,
child: new Center(
child: new Text("类型1"),
),
);
} else if (index == 7) {
return new Container(
height: 40.0,
color: Colors.blue,
child: new Center(
child: new Text("类型2"),
),
);
} else if (index == 14) {
return new Container(
height: 40.0,
color: Colors.yellow,
child: new Center(
child: new Text("类型3"),
),
);
} else {
return Divider(
color: Colors.red,
height: 2,
indent: 20,
);
}
},
),
);
}
}
// onTap自定义跳转页面
void routeSubpage(BuildContext context, int index) {
print("点击了第几个cell----$index");
}
我的理解是自定义的cell。
// ignore: slash_for_doc_comments
/**
* 继承SliverChildBuilderDelegate 可以对列表的监听
*/
class MyChildrenDelegate extends SliverChildBuilderDelegate {
MyChildrenDelegate(
Widget Function(BuildContext, int) builder, {
int childCount,
bool addAutomaticKeepAlive = true,
bool addRepaintBoundaries = true,
}) : super(builder,
childCount: childCount,
addAutomaticKeepAlives: addAutomaticKeepAlive,
addRepaintBoundaries: addRepaintBoundaries);
///监听 在可见的列表中 显示的第一个位置和最后一个位置
@override
void didFinishLayout(int firstIndex, int lastIndex) {
print('firstIndex: $firstIndex, lastIndex: $lastIndex');
}
///可不重写 重写不能为null 默认是true 添加进来的实例与之前的实例是否相同 相同返回true 反之false
///listView 暂时没有看到应用场景 源码中使用在 SliverFillViewport 中
@override
bool shouldRebuild(SliverChildBuilderDelegate oldDelegate) {
// TODO: implement shouldRebuild
print("oldDelegate$oldDelegate");
return super.shouldRebuild(oldDelegate);
}
}
// 以上部分为重写内容
//listView custom 构建
//
// 函数声明
_onClick(){
print("你好啊,小驹!");
}
Widget listViewLayoutCustom() {
// return ListView.custom(childrenDelegate: new MyChildrenDelegate());
return ListView.custom(
//确定每一个item的高度 会让item加载更加高效
itemExtent: 60.0,
scrollDirection: Axis.vertical,
// padding: EdgeInsets.only(top:20),
padding: EdgeInsets.all(20),
childrenDelegate: MyChildrenDelegate(
(BuildContext context, int index) {
return new GestureDetector(
onTap:(){
print("你好啊,小驹!");
routeSubpage(context, index);
},
child: Container(
decoration: BoxDecoration(
color: Colors.blue,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
//1. cell content
Container(
padding: EdgeInsets.only(left: 10),
height: 58,
child: new Row(
// start 左对齐 spaceEvenly 平分
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"你好你好",
style: new TextStyle(
fontSize: 18.0, color: Colors.red,
decoration: TextDecoration.underline, // 划线位置
decorationStyle: TextDecorationStyle.solid // 划线样式
),
),
Text(
"你好你好111",
style: new TextStyle(fontSize: 18.0, color: Colors.orange),
),
Container(
padding: EdgeInsets.only(top: 0),
width: 90,
child: Text(
"你好你好2226",
textAlign: TextAlign.left, // 对齐方式
// 不指定宽度时,下面没有效果,尾部显示 黄色相间的胶带色块
overflow: TextOverflow.ellipsis,// 文字显示不全样式
maxLines: 1, // 最大显示行数,默认为最大
style: TextStyle(fontSize: 18.0, color: Colors.black54),
),
),
],
),
),
//2. 分割线
Container(
height: 2,
child:Divider(height:2.0,indent:15.0,color: Colors.red,),
),
],
),
),
);
},
childCount: 20,
),
cacheExtent: 0.0,
);
}
class CustomCell extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('自定义cell'),
),
body: listViewLayoutCustom(),
);
}
}
void routeSubpage(BuildContext context, int index) {
print("点击了第几个cell----$index");
}
这里面的代码涉及自定义视图,文本的显示,布局等。读者可以细细品味。
关于Flutter ListView的用法基本都在这里了,要想用好ListView,还要加大练习,在实践中琢磨ListView的使用场景和使用技巧。
欢迎点赞+关注。你的鼓励是我写作的动力!
Flutter ListView详解 - trues的博客 写的不错
https://blog.csdn.net/hao_m582/article/details/84112278