Flutter笔记ListTile组件及其应用
作者:李俊才 (jcLee95):https://blog.csdn.net/qq_28550263
本文地址:https://blog.csdn.net/qq_28550263/article/details/133411883
目 录
1. 功能描述
ListTile 组件表示一个包含一到三行文本的列表项,它可以选择带有图标或其它组件。
需要特别说明的是,虽然
ListTile
经常与ListView
一起使用,但它并不仅限于ListView
。实际上,可以在许多其他布局中使用ListTile
,以创建各种不同的用户界面元素。例如,可以将ListTile
放置在Column
、Row
、Card
等其他布局中,以创建自定义的列表项或卡片。
2. 主要属性
ListTile 组件有以下常用属性:
属性 | 描述 |
leading |
列表项的前导部分,通常是一个图标或自定义小部件。 |
title |
列表项的主要标题文本。 |
subtitle |
列表项的副标题文本。 |
trailing |
列表项的尾部部分,通常包含右侧图标或控件。 |
isThreeLine |
布尔值,指示是否为三行列表项。如果为 true ,则可以显示额外的文本行。否则,只有一行文本。 |
dense |
布尔值,指示是否启用紧凑模式,紧凑模式下,文本和图标的大小将减小。 |
visualDensity |
控制布局紧凑性的视觉密度。 |
shape |
定义列表项的形状的形状对象。 |
style |
文本的样式。 |
selectedColor |
选定时的背景颜色。 |
iconColor |
图标的颜色。 |
textColor |
文本的颜色。 |
titleTextStyle |
标题文本的样式。 |
subtitleTextStyle |
副标题文本的样式。 |
leadingAndTrailingTextStyle |
前导和尾部部分文本的样式。 |
contentPadding |
内容的内边距。 |
enabled |
布尔值,指示列表项是否可用。如果为 false ,则列表项将不可点击。 |
onTap |
点击列表项时触发的回调函数。 |
onLongPress |
长按列表项时触发的回调函数。 |
onFocusChange |
获得或失去焦点时触发的回调函数。 |
mouseCursor |
指针悬停在列表项上时的鼠标指针样式。 |
selected |
布尔值,指示列表项是否已选择。 |
focusColor |
获取焦点时的背景颜色。 |
hoverColor |
鼠标悬停时的背景颜色。 |
splashColor |
点击列表项时的水波纹颜色。 |
focusNode |
用于处理焦点状态的 FocusNode 对象。 |
autofocus |
布尔值,指示列表项是否自动获取焦点。 |
tileColor |
列表项的背景颜色。 |
selectedTileColor |
选中列表项时的背景颜色。 |
enableFeedback |
是否启用触觉反馈。 |
horizontalTitleGap |
标题与前导/尾部之间的水平间距。 |
minVerticalPadding |
最小垂直内边距。 |
minLeadingWidth |
最小前导宽度。 |
titleAlignment |
标题文本的对齐方式。 |
3. ListTile的组成元素
- 前导部分(leading):通常是显示在
ListTile
左侧的部分,可以是一个图标(Icon
)、缩略图(Image
)或其他前导元素。 - 主标题(title):通常是
ListTile
的主要文本内容,显示在前导部分(如果有的话)的右侧,用于描述列表项的主要信息。 - 副标题(subtitle):可选项,显示在主标题下面,用于显示列表项的附加信息或次要信息。
- 尾部部分(trailing):通常是显示在
ListTile
右侧的部分,可以是一个图标(Icon
)、按钮或其他尾部元素。
这些是 ListTile
的基本组成部分,可以根据需要自定义和组合这些元素,以创建符合你设计需求的列表项。
4. 案例:一个简单的购物车UI
import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return const MaterialApp( home: ShoppingCartScreen(), ); } } class ShoppingCartScreen extends StatelessWidget { const ShoppingCartScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('购物车'), ), body: ListView.builder( itemCount: 9, itemBuilder: (context, index) { return ShoppingCartItem( productName: '商品$index', productDescription: '商品$index的一些描述', productQuantity: 1, // 产品数量 productImageUrl: 'https://gw.alicdn.com/bao/uploaded/i4/1711217080/O1CN018eotkR22Ah1q4eDcs_!!1711217080.jpg_300x300q90.jpg', // 替换为你的网络图片 URL ); }, ), ); } } class ShoppingCartItem extends StatefulWidget { final String productName; final String productDescription; final int productQuantity; final String productImageUrl; const ShoppingCartItem({ Key? key, required this.productName, required this.productDescription, required this.productQuantity, required this.productImageUrl, }) : super(key: key); @override State<ShoppingCartItem> createState() => _ShoppingCartItemState(); } class _ShoppingCartItemState extends State<ShoppingCartItem> { bool isChecked = false; @override Widget build(BuildContext context) { return Card( child: ListTile( leading: Checkbox( value: isChecked, onChanged: (value) { setState(() { isChecked = value!; }); }, ), title: Text(widget.productName), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(widget.productDescription), Text('数量:${widget.productQuantity}'), ], ), trailing: Image.network( widget.productImageUrl, width: 50, height: 50, fit: BoxFit.cover, ), ), ); } }
效果如下:
5. 案例2:一个新闻列表
import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return const MaterialApp( home: NewsListScreen(), ); } } class NewsListScreen extends StatefulWidget { const NewsListScreen({super.key}); @override State<NewsListScreen> createState() => _NewsListScreenState(); } class _NewsListScreenState extends State<NewsListScreen> { List<String> newsList = List.generate(5, (index) => '新闻标题 $index'); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('新闻列表'), // 设置页面标题 ), body: ListView.builder( itemCount: newsList.length, // 使用列表的长度 itemBuilder: (context, index) { return NewsListItem( headline: newsList[index], // 新闻标题 description: '新闻描述 $index', // 新闻描述 imageUrl: 'https://pics6.baidu.com/feed/5ab5c9ea15ce36d3733d7035255cf48be950b132.jpeg@f_auto?token=29586d3d429228d0a2c251be0f9a8a67', // 替换为你的新闻图片网络 URL onDelete: () { // 处理删除事件 setState(() { newsList.removeAt(index); // 移除对应索引的新闻标题 }); }, ); }, ), ); } } class NewsListItem extends StatelessWidget { final String headline; final String description; final String imageUrl; final VoidCallback onDelete; const NewsListItem({ Key? key, required this.headline, required this.description, required this.imageUrl, required this.onDelete, }) : super(key: key); @override Widget build(BuildContext context) { return Card( child: ListTile( contentPadding: const EdgeInsets.all(16.0), leading: Image.network( imageUrl, // 使用网络图片 URL width: 80, height: 80, fit: BoxFit.cover, ), title: Text( headline, style: const TextStyle(fontWeight: FontWeight.bold), ), subtitle: Text(description), trailing: IconButton( icon: const Icon(Icons.delete), onPressed: onDelete, // 触发删除操作 ), onTap: () { // 处理点击事件,例如打开新闻详情页面 // 这里可以添加你的代码逻辑 }, ), ); } }
效果如下:
6. 案例3:模拟文件资源管理器页面
import 'package:flutter/material.dart'; void main() { runApp(const FileExplorerApp()); } class FileExplorerApp extends StatelessWidget { const FileExplorerApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: const Text('文件资源管理器'), ), body: const FileExplorerUI(), ), ); } } class FileExplorerUI extends StatefulWidget { const FileExplorerUI({super.key}); @override State<FileExplorerUI> createState() => _FileExplorerUIState(); } class _FileExplorerUIState extends State<FileExplorerUI> { SortMode _sortMode = SortMode.name; @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Row( mainAxisAlignment: MainAxisAlignment.end, children: [ IconButton( icon: const Icon(Icons.view_list), onPressed: () { setState(() {}); }, ), IconButton( icon: const Icon(Icons.view_module), onPressed: () { setState(() {}); }, ), IconButton( icon: const Icon(Icons.view_headline), onPressed: () { setState(() {}); }, ), DropdownButton<SortMode>( value: _sortMode, onChanged: (value) { setState(() { _sortMode = value!; }); }, items: SortMode.values .map<DropdownMenuItem<SortMode>>( (mode) => DropdownMenuItem( value: mode, child: Text(mode.toString().split('.').last), ), ) .toList(), ), ], ), Expanded( child: ListView.builder( itemCount: 10, // 虚拟数据,实际根据文件列表长度设置 itemBuilder: (context, index) { return ListTile( leading: const Icon(Icons.folder), // 根据文件类型设置图标 title: Text('文件或文件夹 $index'), // 根据文件名称设置 subtitle: const Text('文件大小: 1 KB'), // 根据文件大小设置 trailing: const Text('修改日期: 2023-01-01'), // 根据修改日期设置 ); }, ), ), ], ); } } enum ViewMode { details, largeIcon, smallIcon } enum SortMode { name, size, type, date }
效果如图所示: