Flutter基础(前端教程②-卡片列表)

发布于:2025-07-10 ⋅ 阅读:(24) ⋅ 点赞:(0)

ListTile 是 Flutter 中专门为列表项设计的标准行组件,它提供了一种简洁的方式来创建带有标题、副标题、图标和按钮的列表项。就像乐高积木一样,ListTile 帮你快速搭建出常见的列表布局。

 为什么需要 ListTile?

想象你要创建一个联系人列表,每个联系人需要显示:

  • 左侧头像
  • 中间名字(主标题)
  • 下方电话号码(副标题)
  • 右侧操作按钮

手动用 ColumnRow 和 Padding 组合实现会很繁琐,而 ListTile 把这些功能集成在一起,一行代码搞定!

ListTile 的核心属性:

ListTile(
  leading: Icon(Icons.person),        // 左侧图标/图片
  title: Text('联系人姓名'),           // 主标题(粗体)
  subtitle: Text('电话号码'),          // 副标题(灰色小字)
  trailing: Icon(Icons.arrow_forward), // 右侧图标/按钮
  onTap: () {},                       // 点击事件
);

好的!Card 是 Flutter 中专门用于创建卡片式布局的组件,它提供了默认的圆角、阴影和内边距,让你轻松实现现代应用中常见的卡片设计。

Card 的最简形式:

Card(
  child: Text('这是一个简单的卡片'),
);

效果:一个带有圆角和轻微阴影的白色矩形区域。

常用属性

Card(
  elevation: 8,           // 阴影深度(默认2)
  color: Colors.blue,    // 卡片背景色
  shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(12), // 圆角半径
  ),
  margin: EdgeInsets.all(16), // 卡片外边距
  child: Text('自定义样式的卡片'),
);

卡片

Card(
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Image.network('https://example.com/news.jpg'), // 卡片顶部图片
      Padding(
        padding: EdgeInsets.all(12),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              '这是新闻标题',
              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
            SizedBox(height: 8),
            Text('这是新闻内容摘要...'),
          ],
        ),
      ),
    ],
  ),
);  

下面是这个代码对应的卡片视觉示意图(简化版):

+----------------------------------------+
|                                        |
|  [新闻图片]                            |  ← Image.network(顶部占满宽度)
|  (https://example.com/news.jpg)        |
|                                        |
+----------------------------------------+
|  这是新闻标题                           |  ← Text(加粗、18号字体)
|                                        |
|  这是新闻内容摘要...                    |  ← Text(普通文本)
|                                        |  (上下左右内边距12,与标题间距8)
+----------------------------------------+

Flutter 中创建列表的 3 种方式

方式 1:直接用 ListView

适合少量固定数量的列表项:

ListView(
  children: [
    Text('第一项'),
    Text('第二项'),
    Text('第三项'),
  ],
)
方式 2:用 ListView.builder 动态生成

适合大量数据(自动复用组件,性能更好):

ListView.builder(
  itemCount: 100, // 列表项数量
  itemBuilder: (context, index) {
    return Text('第 $index 项');
  },
)
方式 3:用 ListTile 快速创建标准列表项

适合联系人、设置项等标准行布局:

ListView(
  children: [
    ListTile(
      leading: Icon(Icons.person),
      title: Text('联系人1'),
      subtitle: Text('电话:13800000000'),
    ),
    ListTile(
      leading: Icon(Icons.person),
      title: Text('联系人2'),
      subtitle: Text('电话:13900000000'),
    ),
  ],
)

下面是一个类似微信聊天列表界面的简化实现,包含头像、名称、消息预览和时间:

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(primarySwatch: Colors.green),
      home: const WeChatHomePage(),
    );
  }
}

class WeChatHomePage extends StatelessWidget {
  const WeChatHomePage({Key? key}) : super(key: key);

  // 模拟聊天数据
  List<Map<String, dynamic>> getChatData() {
    return [
      {
        'name': '张三',
        'avatar': 'https://picsum.photos/100/100?random=1',
        'message': '下午一起吃饭吗?',
        'time': DateTime.now().subtract(const Duration(minutes: 5)),
        'unread': 2,
      },
      {
        'name': '李四',
        'avatar': 'https://picsum.photos/100/100?random=2',
        'message': '好的,我稍后回复你',
        'time': DateTime.now().subtract(const Duration(hours: 1)),
        'unread': 0,
      },
      {
        'name': '工作群',
        'avatar': 'https://picsum.photos/100/100?random=3',
        'message': '王五:明天上午10点开会',
        'time': DateTime.now().subtract(const Duration(hours: 3)),
        'unread': 5,
      },
      {
        'name': '妈妈',
        'avatar': 'https://picsum.photos/100/100?random=4',
        'message': '记得带伞,今天下雨',
        'time': DateTime.now().subtract(const Duration(days: 1)),
        'unread': 0,
      },
      {
        'name': '同事A',
        'avatar': 'https://picsum.photos/100/100?random=5',
        'message': '项目文档已发送,请查收',
        'time': DateTime.now().subtract(const Duration(days: 1)),
        'unread': 1,
      },
    ];
  }

  @override
  Widget build(BuildContext context) {
    final chatData = getChatData();
    
    return Scaffold(
      appBar: AppBar(
        title: const Text('微信'),
        actions: [
          IconButton(icon: const Icon(Icons.search), onPressed: () {}),
          IconButton(icon: const Icon(Icons.add), onPressed: () {}),
        ],
      ),
      body: ListView.builder(
        itemCount: chatData.length,
        itemBuilder: (context, index) {
          final chat = chatData[index];
          final formattedTime = _formatTime(chat['time']);
          
          return Column(
            children: [
              // 分隔线(除了第一个)
              if (index > 0) const Divider(height: 0),
              
              // 聊天项
              ListTile(
                leading: CircleAvatar(
                  backgroundImage: NetworkImage(chat['avatar']),
                  radius: 28,
                ),
                title: Text(
                  chat['name'],
                  style: TextStyle(
                    fontWeight: chat['unread'] > 0 ? FontWeight.bold : FontWeight.normal,
                  ),
                ),
                subtitle: Text(
                  chat['message'],
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                  style: TextStyle(
                    color: Colors.grey[600],
                  ),
                ),
                trailing: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.end,
                  children: [
                    // 时间
                    Text(
                      formattedTime,
                      style: TextStyle(
                        fontSize: 12,
                        color: Colors.grey[500],
                      ),
                    ),
                    
                    // 未读数(如果有)
                    if (chat['unread'] > 0)
                      Container(
                        margin: const EdgeInsets.only(top: 4),
                        padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 1),
                        decoration: BoxDecoration(
                          color: Colors.red,
                          borderRadius: BorderRadius.circular(10),
                        ),
                        child: Text(
                          chat['unread'].toString(),
                          style: const TextStyle(color: Colors.white, fontSize: 12),
                        ),
                      ),
                  ],
                ),
                onTap: () {
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(content: Text('打开与 ${chat['name']} 的聊天'))
                  );
                },
              ),
            ],
          );
        },
      ),
    );
  }

  // 格式化时间(根据是否今天显示不同格式)
  String _formatTime(DateTime time) {
    final now = DateTime.now();
    final today = DateTime(now.year, now.month, now.day);
    final yesterday = today.subtract(const Duration(days: 1));
    
    if (DateTime(time.year, time.month, time.day).isAtSameMomentAs(today)) {
      // 今天:显示时:分
      return DateFormat('HH:mm').format(time);
    } else if (DateTime(time.year, time.month, time.day).isAtSameMomentAs(yesterday)) {
      // 昨天:显示"昨天"
      return '昨天';
    } else {
      // 其他:显示月-日
      return DateFormat('MM-dd').format(time);
    }
  }
}


网站公告

今日签到

点亮在社区的每一天
去签到