🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:Flutter学习
🌠 首发时间:2024年5月24日
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾
目录
列表组件
列表布局是我们项目开发中最常用的一种布局方式。在 Flutter 中,我们可以通过 ListView
来定义列表项,支持垂直和水平方向展示,通过一个属性就可以控制列表的显示方向。列表有以下分类:
- 垂直列表
- 垂直图文列表
- 水平列表
- 动态列表
列表组件常用参数:
名称 | 类型 | 说明 |
---|---|---|
scrollDirection | Axis | 默认为垂直列表:Axis.vertical ;水平列表:Axis.horizontal |
padding | EdgeInsetsGeometry | 内边距 |
resolve | bool | 组件反向排序 |
children | List | 列表元素 |
垂直列表
示例一
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(primarySwatch: Colors.yellow),
home: Scaffold(
appBar: AppBar(title: const Text("垂直列表")),
body: const MyHomePage(),
),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return ListView(
children: const [
ListTile(
title: Text("我是一个列表"),
),
//一根线
Divider(),
ListTile(
title: Text("我是一个列表"),
),
Divider(),
ListTile(
title: Text("我是一个列表"),
),
Divider(),
ListTile(
title: Text("我是一个列表"),
),
Divider(),
ListTile(
title: Text("我是一个列表"),
),
Divider(),
ListTile(
title: Text("我是一个列表"),
),
Divider(),
ListTile(
title: Text("我是一个列表"),
),
Divider(),
ListTile(
title: Text("我是一个列表"),
),
Divider(),
ListTile(
title: Text("我是一个列表"),
),
Divider(),
ListTile(
title: Text("我是一个列表"),
),
Divider(),
ListTile(
title: Text("我是一个列表"),
),
Divider(),
ListTile(
title: Text("我是一个列表"),
),
Divider(),
],
);
}
}
垂直列表是可以上下滑动的
示例二
实现如下效果:
ListTile
组件的 leading
属性写在 title
属性前面,可以放图标或图片
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return ListView(
children: const [
ListTile(
leading: Icon(Icons.assignment, color: Colors.red),
title: Text("全部订单"),
),
//一根线
Divider(),
ListTile(
leading: Icon(Icons.payment, color: Colors.green),
title: Text("待付款"),
),
Divider(),
ListTile(
leading: Icon(Icons.local_car_wash, color: Colors.orange),
title: Text("待收货"),
),
Divider(),
ListTile(
leading: Icon(Icons.favorite, color: Colors.lightGreen),
title: Text("我的收藏"),
),
Divider(),
ListTile(
leading: Icon(Icons.people, color: Colors.black54),
title: Text("在线客服"),
),
Divider(),
],
);
}
}
垂直图文列表
示例一
实现如下类似新闻页面的效果:
ListTile
组件的 trailing
属性写在 title
属性后面,可以放图标或图片;用 subtitle
属性来指定这个列表项的副标题。
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return ListView(
children: [
ListTile(
leading: Image.network(
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/1.jpg"),
title: const Text("今天地磁暴活动趋于减弱"),
subtitle:
const Text("针对24日到26日发生的地磁暴现象,中国气象局国家卫星气象中心的专家分析,峰值已出现在25日凌晨。"),
),
const Divider(),
ListTile(
leading: Image.network(
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/2.jpg"),
title: const Text("不小心掉进黑洞会发生什么?能看到宇宙的尽头吗?"),
subtitle: const Text("黑洞是宇宙中神秘的天体,由超新星爆炸形成。"),
trailing: Image.network(
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/3.jpg"),
),
const Divider(),
ListTile(
title: const Text("一枚钴弹真的可以消灭全人类吗?钴弹到底是什么?"),
subtitle: const Text("钴炸弹是一种假设的特种核武器,通过释放放射性元素钴60造成巨大破坏。"),
trailing: Image.network(
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/4.jpg"),
),
const Divider(),
ListTile(
leading: Image.network(
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/5.jpg"),
title: const Text("71年等一回!“天外来客”长什么样?"),
subtitle: const Text(
"近期,12P/Pons-Brooks彗星成为全球瞩目的对象,我国不少天文爱好者成功观测并拍摄到这位“天外来客”。"),
),
const Divider(),
ListTile(
title: const Text("大地磁暴确定发生,东北局部已见极光!网友:因为它我都睡不醒"),
subtitle: const Text("中国气象局国家空间天气监测预警中心发出地磁扰动预警,3月24日至26日将出现中等到大地磁暴"),
trailing: Image.network(
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/6.jpg"),
),
const Divider(),
],
);
}
}
示例二
实现如下效果:
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return ListView(
padding: const EdgeInsets.all(10),
children: [
Image.network(
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/1.jpg"),
Container(
height: 44,
padding: const EdgeInsets.fromLTRB(0, 10, 0, 10),
child: const Text(
'我是一个标题',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
),
),
),
Image.network(
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/2.jpg"),
Container(
height: 44,
padding: const EdgeInsets.fromLTRB(0, 10, 0, 10),
child: const Text(
'我是一个标题',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
),
),
),
Image.network(
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/3.jpg"),
Container(
height: 44,
padding: const EdgeInsets.fromLTRB(0, 10, 0, 10),
child: const Text(
'我是一个标题',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
),
),
),
],
);
}
}
水平列表
实现如下效果,可左右滑动:
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return
//用SizedBox来限制水平列表的高度
SizedBox(
height: 140,
child: ListView(
//设置为水平方向
scrollDirection: Axis.horizontal,
children: [
SizedBox(
width: 130,
child: Column(
children: [
Image.network(
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/1.jpg",
width: 100,
height: 100,
fit: BoxFit.cover,
),
const SizedBox(height: 3),
const Text('我是一个文本')
],
),
),
SizedBox(
width: 130,
child: Column(
children: [
Image.network(
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/2.jpg",
width: 100,
height: 100,
fit: BoxFit.cover,
),
const SizedBox(height: 3),
const Text('我是一个文本')
],
),
),
SizedBox(
width: 130,
child: Column(
children: [
Image.network(
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/3.jpg",
width: 100,
height: 100,
fit: BoxFit.cover,
),
const SizedBox(height: 3),
const Text('我是一个文本')
],
),
),
SizedBox(
width: 130,
child: Column(
children: [
Image.network(
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/4.jpg",
width: 100,
height: 100,
fit: BoxFit.cover,
),
const SizedBox(height: 3),
const Text('我是一个文本')
],
),
),
],
),
);
}
}
动态列表及循环动态数据
for循环实现动态列表
在前面,我们是一个个手动添加列表中的列表项,这是很繁琐的,下面我们用 for 循环来实现同样的效果
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
List<Widget> _initListData() {
List<Widget> list = [];
for (var i = 0; i < 10; i++) {
list.add(ListTile(
title: Text("我是第${i + 1}个列表"),
));
}
return list;
}
Widget build(BuildContext context) {
return ListView(
children: _initListData(),
);
}
}
在正式开发中,我们创建列表的数据都是从服务器获取的,现在我们准备了一个文件,里面包含了列表项的信息,模拟这个过程:
在 lib 目录下新建一个 res 文件夹,res 下新建一个 listData.dart 文件:
List listData = [
{
"title": 'Candy Shop',
"author": 'Mohamed Chahin',
"imageUrl": 'https://www.itying.com/images/flutter/1.png',
},
{
"title": 'Childhood in a picture',
"author": 'Google',
"imageUrl": 'https://www.itying.com/images/flutter/2.png',
},
{
"title": 'Alibaba Shop',
"author": 'Alibaba',
"imageUrl": 'https://www.itying.com/images/flutter/3.png',
},
{
"title": 'Candy Shop',
"author": 'Mohamed Chahin',
"imageUrl": 'https://www.itying.com/images/flutter/4.png',
},
{
"title": 'Tornado',
"author": 'Mohamed Chahin',
"imageUrl": 'https://www.itying.com/images/flutter/5.png',
},
{
"title": 'Undo',
"author": 'Mohamed Chahin',
"imageUrl": 'https://www.itying.com/images/flutter/6.png',
},
{
"title": 'white-dragon',
"author": 'Mohamed Chahin',
"imageUrl": 'https://www.itying.com/images/flutter/7.png',
}
];
import 'package:flutter/material.dart';
import './res/listData.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(primarySwatch: Colors.yellow),
home: Scaffold(
appBar: AppBar(title: const Text("for循环实现动态列表")),
body: const MyHomePage(),
),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
List<Widget> _initListData() {
List<Widget> list = [];
for (var i = 0; i < listData.length; i++) {
list.add(ListTile(
leading: Image.network(listData[i]["imageUrl"]),
title: Text(listData[i]["title"]),
subtitle: Text(listData[i]["author"]),
));
}
return list;
}
Widget build(BuildContext context) {
return ListView(
children: _initListData(),
);
}
}
ListView.builder实现动态列表
class MyHomePage extends StatelessWidget {
List<String> list = [];
MyHomePage({super.key}) {
for (var i = 0; i < 10; i++) {
list.add("我是第${i + 1}条数据");
}
}
Widget build(BuildContext context) {
//ListView.builder和for循环差不多, itemCount是循环次数
return ListView.builder(
itemCount: list.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(list[index]),
);
},
);
}
}
和前面,从 listData.dart 读取数据展示在列表项中:
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
List<Widget> _initListData() {
var tempList = listData.map((value) {
return ListTile(
leading: Image.network(value["imageUrl"]),
title: Text(value["title"]),
subtitle: Text(value["author"]),
);
});
return tempList.toList();
}
Widget build(BuildContext context) {
return ListView(
children: _initListData(),
);
}
}
GridView网格布局组件
组件介绍
GridView
网格布局在实际项目中用的也是非常多的,当我们想让可以滚动的元素使用矩阵方式排列的时候,此时我们可以用网格列表组件 GridView
实现布局。
GridView
创建网格列表主要有下面三种方式
- 可以通过
GridView.count
实现网格布局 - 可以通过
GridView.extent
实现网格布局 - 通过
GridView.builder
实现动态网格布局
常用属性:
GridView.count实现网格布局
GridView.count
构造函数内部使用了 SliverGridDelegateWithFixedCrossAxisCount
,我们通过它可以
快速的创建横轴固定数量子元素的 GridView
实现如下效果:
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: 3,
children: const [
Icon(Icons.home),
Icon(Icons.ac_unit),
Icon(Icons.search),
Icon(Icons.settings),
Icon(Icons.airport_shuttle),
Icon(Icons.all_inclusive),
Icon(Icons.beach_access),
Icon(Icons.cake),
Icon(Icons.circle),
],
);
}
}
GridView.extent实现网格布局
GridView.extent
构造函数内部使用了 SliverGridDelegateWithMaxCrossAxisExtent
,我们通过它可以
快速的创建横轴子元素为固定最大长度的 GridView
。
实现如下效果:
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return GridView.extent(
maxCrossAxisExtent: 150,
children: const [
Icon(Icons.home),
Icon(Icons.ac_unit),
Icon(Icons.search),
Icon(Icons.settings),
Icon(Icons.airport_shuttle),
Icon(Icons.all_inclusive),
Icon(Icons.beach_access),
Icon(Icons.cake),
Icon(Icons.circle),
],
);
}
}
GridView.count和GridView.extent属性详解
实现如下效果:
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
List<Widget> _getListData() {
List<Widget> list = [];
for (var i = 0; i < 10; i++) {
list.add(
Container(
alignment: Alignment.center,
color: Colors.blue,
child: Text(
'第${i + 1}条数据',
style: const TextStyle(
color: Colors.white,
fontSize: 20,
),
),
),
);
}
return list;
}
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: 2, //一行的widget数量
padding: const EdgeInsets.all(10),
crossAxisSpacing: 20, //水平子组件之间的间距
mainAxisSpacing: 20, //垂直子组件之间的间距
childAspectRatio: 1, //宽高比
children: _getListData(),
);
}
}
GridView.count实现动态列表
实现如下效果:
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
List<Widget> _getListData() {
var tempList = listData.map((value) {
return Container(
decoration: BoxDecoration(
border: Border.all(
color: const Color.fromRGBO(233, 233, 233, 0.9), width: 1)),
child: Column(
children: [
Image.network(value["imageUrl"]),
const SizedBox(height: 10),
Text(
value["title"],
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 16),
)
],
),
);
});
return tempList.toList();
}
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: 2, //一行的widget数量
padding: const EdgeInsets.all(10),
crossAxisSpacing: 10, //水平子组件之间的间距
mainAxisSpacing: 10, //垂直子组件之间的间距
childAspectRatio: 1, //宽高比
children: _getListData(),
);
}
}
GridView.builder实现动态列表
SliverGridDelegateWithFixedCrossAxisCount
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget _getListData(context, index) {
return Container(
decoration: BoxDecoration(
border: Border.all(
color: const Color.fromRGBO(233, 233, 233, 0.9), width: 1)),
child: Column(
children: [
Image.network(listData[index]["imageUrl"]),
const SizedBox(height: 10),
Text(
listData[index]["title"],
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 16),
)
],
),
);
}
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, //一行的widget数量
crossAxisSpacing: 10, //水平子组件之间的间距
mainAxisSpacing: 10, //垂直子组件之间的间距
),
itemCount: listData.length,
itemBuilder: _getListData,
);
}
}
SliverGridDelegateWithMaxCrossAxisExtent
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget _getListData(context, index) {
return Container(
decoration: BoxDecoration(
border: Border.all(
color: const Color.fromRGBO(233, 233, 233, 0.9), width: 1)),
child: Column(
children: [
Image.network(listData[index]["imageUrl"]),
const SizedBox(height: 10),
Text(
listData[index]["title"],
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 18),
)
],
),
);
}
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 300,
crossAxisSpacing: 10, //水平子组件之间的间距
mainAxisSpacing: 10, //垂直子组件之间的间距
),
itemCount: listData.length,
itemBuilder: _getListData,
);
}
}
Padding组件
在 html
中常见的布局标签都有 padding
属性,但是 Flutter
中很多 Widget
是没有 padding
属性的。这个时候我们可以用 Padding
组件处理容器与子元素之间的间距。
属性 | 说明 |
---|---|
padding | padding 值,EdgeInsets 设置填充的值 |
child | 子组件 |
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(20),
child: Image.network(
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/2.jpg"),
);
}
}