在这里插入图片描述

我的活动页面展示用户已报名参加的所有活动,方便用户查看和管理自己的活动安排。这篇文章带大家实现我的活动列表模块。

页面整体设计

我的活动页面以列表形式展示用户参与的活动,每个卡片显示日期、标题、社团、地点和状态信息。

页面采用StatelessWidget实现:

class MyActivitiesPage extends StatelessWidget {
  const MyActivitiesPage({super.key});

页面不需要维护内部状态,数据从Provider获取。
const构造函数可以让Flutter复用Widget实例。

导入依赖包

在文件开头导入必要的依赖:

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

material.dart提供Material Design风格的组件。
provider用于状态管理,intl用于日期格式化。

导入项目内部文件:

import '../../providers/app_provider.dart';
import 'activity_detail_page.dart';

app_provider包含用户活动列表数据。
activity_detail_page是活动详情页面。

构建页面骨架

使用Scaffold搭建基本结构:

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('我的活动')
      ),

Scaffold提供标准的Material页面结构。
AppBar显示页面标题"我的活动"。

监听数据变化

页面主体使用Consumer监听数据:

      body: Consumer<AppProvider>(
        builder: (context, provider, _) {
          final myActivities = provider.myActivities;

Consumer自动订阅AppProvider的变化。
myActivities是用户已报名的活动列表。

空状态处理

没有活动时显示友好提示:

          if (myActivities.isEmpty) {
            return const Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Icon(
                    Icons.event_busy, 
                    size: 64, 
                    color: Colors.grey
                  ),
                  SizedBox(height: 16),
                  Text(
                    '暂未参加任何活动', 
                    style: TextStyle(
                      color: Colors.grey, 
                      fontSize: 16
                    )
                  ),
                ],
              ),
            );
          }

event_busy图标直观表示没有活动。
灰色文字提示用户当前状态。

构建活动列表

使用ListView.builder构建列表:

          return ListView.builder(
            padding: const EdgeInsets.all(16),
            itemCount: myActivities.length,

ListView.builder采用懒加载机制。
padding设置16像素内边距。

活动卡片容器

每个活动用Card包裹:

            itemBuilder: (context, index) {
              final activity = myActivities[index];
              return Card(
                margin: const EdgeInsets.only(bottom: 12),
                child: InkWell(
                  onTap: () => Navigator.push(
                    context, 
                    MaterialPageRoute(
                      builder: (_) => ActivityDetailPage(
                        activity: activity
                      )
                    )
                  ),

Card提供Material风格的卡片效果。
InkWell添加水波纹点击效果。

卡片内容布局

卡片内部使用Row横向布局:

                  child: Padding(
                    padding: const EdgeInsets.all(16),
                    child: Row(
                      children: [

Padding设置16像素内边距。
Row让日期区和信息区横向排列。

日期显示区

左侧显示活动日期:

                        Container(
                          width: 60,
                          height: 60,
                          decoration: BoxDecoration(
                            color: const Color(0xFF4A90E2)
                                .withOpacity(0.1),
                            borderRadius: BorderRadius.circular(8),
                          ),
                          child: Column(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [
                              Text(
                                DateFormat('dd')
                                    .format(activity.startTime), 
                                style: const TextStyle(
                                  fontSize: 20, 
                                  fontWeight: FontWeight.bold, 
                                  color: Color(0xFF4A90E2)
                                )
                              ),

日期区用蓝色背景突出显示。
日期数字用大号粗体字。

月份显示:

                              Text(
                                DateFormat('MM月')
                                    .format(activity.startTime), 
                                style: const TextStyle(
                                  fontSize: 12, 
                                  color: Color(0xFF4A90E2)
                                )
                              ),
                            ],
                          ),
                        ),
                        const SizedBox(width: 16),

月份用小号字显示在日期下方。
这种日历式的日期展示很直观。

活动信息区

中间区域显示活动详细信息:

                        Expanded(
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: [
                              Text(
                                activity.title, 
                                style: const TextStyle(
                                  fontWeight: FontWeight.bold, 
                                  fontSize: 16
                                )
                              ),
                              const SizedBox(height: 4),
                              Text(
                                activity.clubName, 
                                style: const TextStyle(
                                  color: Color(0xFF4A90E2), 
                                  fontSize: 13
                                )
                              ),

标题用粗体16像素字号突出显示。
社团名称用蓝色显示,和日期区呼应。

地点信息:

                              const SizedBox(height: 4),
                              Row(
                                children: [
                                  const Icon(
                                    Icons.location_on, 
                                    size: 14, 
                                    color: Colors.grey
                                  ),
                                  const SizedBox(width: 4),
                                  Expanded(
                                    child: Text(
                                      activity.location, 
                                      style: const TextStyle(
                                        fontSize: 12, 
                                        color: Colors.grey
                                      ), 
                                      overflow: TextOverflow.ellipsis
                                    )
                                  ),
                                ],
                              ),
                            ],
                          ),
                        ),

定位图标配合地点信息。
Expanded让地点文字占据剩余空间。

状态标签

右侧显示活动状态:

                        Container(
                          padding: const EdgeInsets.symmetric(
                            horizontal: 8, 
                            vertical: 4
                          ),
                          decoration: BoxDecoration(
                            color: activity.status == '已结束' 
                                ? Colors.grey.withOpacity(0.1) 
                                : Colors.green.withOpacity(0.1),
                            borderRadius: BorderRadius.circular(4),
                          ),
                          child: Text(
                            activity.status, 
                            style: TextStyle(
                              color: activity.status == '已结束' 
                                  ? Colors.grey 
                                  : Colors.green, 
                              fontSize: 12
                            )
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

已结束用灰色,进行中用绿色。
状态标签帮助用户快速判断活动时效。

日期展示的设计

日历式的日期展示有几个优点。
首先视觉上很直观,用户一眼就能看到日期。

其次和传统日历的展示方式一致。
用户不需要学习就能理解。

状态颜色的意义

绿色表示活动还在进行或即将开始。
灰色表示活动已经结束。

这种颜色区分让用户快速判断活动状态。
不需要阅读文字就能获取信息。

列表性能优化

ListView.builder的懒加载机制提供基础性能保障。
只有可见区域的卡片才会被创建。

如果用户参加了很多活动,这种方式可以显著提升性能。
滚动时新的卡片才会被创建。

筛选功能扩展

可以按状态筛选活动:

final statusFilter = ['全部', '即将开始', '进行中', '已结束'];

ChoiceChip(
  label: Text(status),
  selected: selectedStatus == status,
  onSelected: (selected) {
    setState(() {
      selectedStatus = status;
    });
  },
)

ChoiceChip实现单选筛选效果。
用户可以只看即将开始的活动。

日历视图

可以添加日历视图展示活动:

TableCalendar(
  focusedDay: DateTime.now(),
  firstDay: DateTime(2020),
  lastDay: DateTime(2030),
  eventLoader: (day) {
    return myActivities.where(
      (a) => isSameDay(a.startTime, day)
    ).toList();
  },
)

日历视图让用户直观看到活动分布。
有活动的日期会显示标记点。

提醒功能

可以为活动设置提醒:

IconButton(
  icon: Icon(
    activity.hasReminder 
        ? Icons.notifications_active 
        : Icons.notifications_none
  ),
  onPressed: () {
    // 设置提醒
  },
)

提醒图标显示在卡片上。
用户可以为重要活动设置提醒。

签到功能

活动开始时可以签到:

if (activity.status == '进行中')
  ElevatedButton(
    onPressed: () {
      // 签到逻辑
    },
    child: Text('签到'),
  )

签到按钮只在活动进行中显示。
签到后可以获得积分奖励。

小结

我的活动页面通过列表形式展示用户参与的活动,每个卡片显示日历式日期、标题、社团、地点和状态。已结束的活动用灰色标识,进行中的用绿色标识。点击卡片可进入活动详情页面。


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐