在这里插入图片描述

活动详情页展示活动的完整信息,包括时间地点、报名进度、活动介绍等,并提供报名和取消报名功能。

页面参数设计

详情页接收活动对象作为参数:

class ActivityDetailPage extends StatelessWidget {
  final Activity activity;

  const ActivityDetailPage({super.key, required this.activity});

通过构造函数传入activity对象,类型安全。
使用StatelessWidget因为页面本身不维护状态。

页面整体结构

使用Consumer监听数据变化:

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('活动详情')),
      body: Consumer<AppProvider>(
        builder: (context, provider, _) {
          final currentActivity = provider.activities.firstWhere(
            (a) => a.id == activity.id, 
            orElse: () => activity
          );

从Provider获取最新的活动数据,确保报名状态实时更新。
orElse处理找不到的情况,返回传入的原始数据。

页面布局:

          return SingleChildScrollView(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                _buildHeader(currentActivity),
                Padding(
                  padding: const EdgeInsets.all(16),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      _buildInfoCard(currentActivity),
                      const SizedBox(height: 16),
                      _buildProgressCard(currentActivity),
                      const SizedBox(height: 16),
                      _buildDescriptionCard(currentActivity),
                      const SizedBox(height: 24),
                      _buildActionButton(context, provider, currentActivity),
                    ],
                  ),
                ),
              ],
            ),
          );
        },
      ),
    );
  }

SingleChildScrollView让内容可滚动。
模块顺序:头部、信息卡片、进度卡片、详情卡片、操作按钮。

头部区域

根据状态设置颜色:

  Widget _buildHeader(Activity activity) {
    Color statusColor;
    switch (activity.status) {
      case '报名中':
        statusColor = Colors.green;
        break;
      case '即将开始':
        statusColor = Colors.orange;
        break;
      case '已结束':
        statusColor = Colors.grey;
        break;
      default:
        statusColor = Colors.blue;
    }

switch语句根据状态返回对应颜色。
颜色语义和活动列表保持一致。

头部容器:

    return Container(
      height: 180,
      decoration: BoxDecoration(
        gradient: LinearGradient(
          colors: [
            const Color(0xFF4A90E2), 
            const Color(0xFF357ABD).withOpacity(0.8)
          ],
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
        ),
      ),

渐变背景从左上到右下,蓝色调和主题一致。
180像素的高度足够展示标题和状态。

背景图标和内容:

      child: Stack(
        children: [
          Center(
            child: Icon(
              Icons.event, 
              size: 80, 
              color: Colors.white.withOpacity(0.3)
            )
          ),
          Positioned(
            left: 16,
            bottom: 16,
            right: 16,
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [

Stack叠加背景图标和前景内容。
Positioned把内容定位在左下角。

状态标签和标题:

                Container(
                  padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
                  decoration: BoxDecoration(
                    color: statusColor,
                    borderRadius: BorderRadius.circular(12),
                  ),
                  child: Text(
                    activity.status, 
                    style: const TextStyle(color: Colors.white, fontSize: 12)
                  ),
                ),
                const SizedBox(height: 8),
                Text(
                  activity.title, 
                  style: const TextStyle(
                    color: Colors.white, 
                    fontSize: 22, 
                    fontWeight: FontWeight.bold
                  )
                ),
                const SizedBox(height: 4),
                Text(
                  activity.clubName, 
                  style: const TextStyle(color: Colors.white70, fontSize: 14)
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

状态标签用实心背景,在渐变背景上更醒目。
标题22像素白色粗体,是头部最重要的信息。

信息卡片

构建信息卡片:

  Widget _buildInfoCard(Activity activity) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            _buildInfoRow(
              Icons.access_time, 
              '开始时间', 
              DateFormat('yyyy-MM-dd HH:mm').format(activity.startTime)
            ),
            const Divider(),
            _buildInfoRow(
              Icons.access_time_filled, 
              '结束时间', 
              DateFormat('yyyy-MM-dd HH:mm').format(activity.endTime)
            ),

开始和结束时间用不同的图标区分。
日期格式化成完整的年月日时分。

继续添加信息行:

            const Divider(),
            _buildInfoRow(Icons.location_on, '活动地点', activity.location),
            const Divider(),
            _buildInfoRow(Icons.person, '组织者', activity.organizer),
          ],
        ),
      ),
    );
  }

四行信息涵盖了活动的基本情况。
Divider分割线让各行更清晰。

封装信息行组件:

  Widget _buildInfoRow(IconData icon, String label, String value) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 8),
      child: Row(
        children: [
          Icon(icon, size: 20, color: const Color(0xFF4A90E2)),
          const SizedBox(width: 12),
          Text(label, style: const TextStyle(color: Colors.grey)),
          const Spacer(),
          Flexible(
            child: Text(
              value, 
              style: const TextStyle(fontWeight: FontWeight.w500), 
              textAlign: TextAlign.right
            )
          ),
        ],
      ),
    );
  }

Flexible让值文字可以换行,避免溢出。
textAlign右对齐让布局更整齐。

报名进度卡片

构建进度卡片:

  Widget _buildProgressCard(Activity activity) {
    final progress = activity.currentParticipants / activity.maxParticipants;
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                const Text(
                  '报名进度', 
                  style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)
                ),
                Text(
                  '${activity.currentParticipants}/${activity.maxParticipants}人', 
                  style: const TextStyle(
                    color: Color(0xFF4A90E2), 
                    fontWeight: FontWeight.bold
                  )
                ),
              ],
            ),

标题和人数分居两端。
人数用蓝色粗体突出显示。

进度条:

            const SizedBox(height: 12),
            ClipRRect(
              borderRadius: BorderRadius.circular(8),
              child: LinearProgressIndicator(
                value: progress,
                minHeight: 10,
                backgroundColor: Colors.grey[200],
                valueColor: AlwaysStoppedAnimation<Color>(
                  progress >= 1 ? Colors.red : const Color(0xFF4A90E2)
                ),
              ),
            ),

ClipRRect给进度条加圆角。
满员时变红色警示。

剩余名额提示:

            const SizedBox(height: 8),
            Text(
              progress >= 1 
                  ? '名额已满' 
                  : '还剩${activity.maxParticipants - activity.currentParticipants}个名额', 
              style: TextStyle(
                color: progress >= 1 ? Colors.red : Colors.grey, 
                fontSize: 13
              )
            ),
          ],
        ),
      ),
    );
  }

满员显示红色"名额已满",否则显示剩余名额。
文字颜色和进度条颜色保持一致。

活动详情卡片

构建详情卡片:

  Widget _buildDescriptionCard(Activity activity) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const Text(
              '活动详情', 
              style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)
            ),
            const SizedBox(height: 12),
            Text(
              activity.description, 
              style: const TextStyle(color: Colors.grey, height: 1.6)
            ),
          ],
        ),
      ),
    );
  }

行高1.6让多行文字阅读更舒适。
灰色文字作为正文内容。

操作按钮

处理已结束状态:

  Widget _buildActionButton(
    BuildContext context, 
    AppProvider provider, 
    Activity activity
  ) {
    final canJoin = activity.status == '报名中' && 
        activity.currentParticipants < activity.maxParticipants;

    if (activity.status == '已结束') {
      return SizedBox(
        width: double.infinity,
        height: 48,
        child: ElevatedButton(
          style: ElevatedButton.styleFrom(
            backgroundColor: Colors.grey, 
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(24)
            )
          ),
          onPressed: null,
          child: const Text(
            '活动已结束', 
            style: TextStyle(fontSize: 16, color: Colors.white)
          ),
        ),
      );
    }

已结束的活动显示灰色禁用按钮。
onPressed为null时按钮自动禁用。

正常状态的按钮:

    return SizedBox(
      width: double.infinity,
      height: 48,
      child: ElevatedButton(
        style: ElevatedButton.styleFrom(
          backgroundColor: activity.isJoined 
              ? Colors.red 
              : (canJoin ? const Color(0xFF4A90E2) : Colors.grey),
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(24)
          ),
        ),

已报名显示红色取消按钮,可报名显示蓝色报名按钮,满员显示灰色。
圆角24像素的胶囊形按钮。

点击事件处理:

        onPressed: canJoin || activity.isJoined
            ? () {
                if (activity.isJoined) {
                  showDialog(
                    context: context,
                    builder: (ctx) => AlertDialog(
                      title: const Text('确认取消'),
                      content: const Text('确定要取消报名吗?'),
                      actions: [
                        TextButton(
                          onPressed: () => Navigator.pop(ctx), 
                          child: const Text('取消')
                        ),
                        TextButton(
                          onPressed: () {
                            provider.cancelActivity(activity.id);
                            Navigator.pop(ctx);
                            ScaffoldMessenger.of(context).showSnackBar(
                              const SnackBar(content: Text('已取消报名'))
                            );
                          },
                          child: const Text(
                            '确定', 
                            style: TextStyle(color: Colors.red)
                          ),
                        ),
                      ],
                    ),
                  );

取消报名需要二次确认,防止误操作。
确认后调用provider方法并显示SnackBar反馈。

报名处理:

                } else {
                  provider.joinActivity(activity.id);
                  ScaffoldMessenger.of(context).showSnackBar(
                    const SnackBar(content: Text('报名成功'))
                  );
                }
              }
            : null,
        child: Text(
          activity.isJoined 
              ? '取消报名' 
              : (canJoin ? '立即报名' : '名额已满'),
          style: const TextStyle(fontSize: 16, color: Colors.white),
        ),
      ),
    );
  }
}

报名操作直接执行,不需要确认。
按钮文字根据状态动态变化。

小结

活动详情页通过渐变头部展示活动标题和状态,信息卡片展示时间地点等基本信息,进度卡片直观显示报名情况。操作按钮根据活动状态和用户报名状态动态变化,取消报名有二次确认,整体交互完善。


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

活动地图定位

显示活动地点的地图位置:

Widget _buildLocationMap() {
  return Container(
    margin: EdgeInsets.all(16.w),
    height: 200.h,
    decoration: BoxDecoration(
      color: Colors.grey[200],
      borderRadius: BorderRadius.circular(12.r),
    ),
    child: ClipRRect(
      borderRadius: BorderRadius.circular(12.r),
      child: Stack(
        children: [
          // 这里可以集成地图组件
          Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Icon(Icons.location_on, size: 48.sp, color: Colors.grey[400]),
                SizedBox(height: 8.h),
                Text(
                  activity.location,
                  style: TextStyle(fontSize: 14.sp, color: Colors.grey[600]),
                ),
              ],
            ),
          ),
          Positioned(
            bottom: 12.h,
            right: 12.w,
            child: ElevatedButton.icon(
              onPressed: () => _openMap(),
              icon: const Icon(Icons.navigation, size: 16),
              label: const Text('导航'),
              style: ElevatedButton.styleFrom(
                backgroundColor: Colors.white,
                foregroundColor: const Color(0xFF2196F3),
                elevation: 2,
              ),
            ),
          ),
        ],
      ),
    ),
  );
}

void _openMap() {
  // 调用系统地图应用导航到活动地点
  final uri = Uri.parse('geo:0,0?q=${activity.location}');
  launchUrl(uri);
}

地图区域显示活动地点的位置标记,用户可以直观地了解活动位置。右下角的导航按钮点击后调用系统地图应用,提供路线导航功能。这个功能在用户需要前往活动地点时非常实用。

地图使用圆角裁剪与整体设计风格保持一致。如果集成真实的地图SDK,可以显示详细的地图信息和周边环境。导航按钮使用白色背景和蓝色文字,在地图上清晰可见。

活动分享功能

支持将活动信息分享给好友:

Widget _buildShareButton() {
  return IconButton(
    icon: const Icon(Icons.share),
    onPressed: () => _shareActivity(),
  );
}

void _shareActivity() {
  final text = '''
【${activity.title}】

时间:${DateFormat('yyyy年MM月dd日 HH:mm').format(activity.startTime)}
地点:${activity.location}
${activity.description}

快来参加吧!
''';

  Share.share(text, subject: activity.title);
}

分享功能将活动的关键信息整理成文本,通过系统分享功能发送给好友。分享内容包含活动标题、时间、地点和描述,让接收者能够快速了解活动详情。

这个功能可以帮助活动组织者推广活动,也方便参与者邀请朋友一起参加。使用系统原生分享功能,支持多种分享渠道,如微信、短信、邮件等。

参与者列表

显示已报名的参与者:

Widget _buildParticipantsList() {
  return Container(
    margin: EdgeInsets.all(16.w),
    padding: EdgeInsets.all(16.w),
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: BorderRadius.circular(12.r),
      boxShadow: [
        BoxShadow(
          color: Colors.black.withOpacity(0.05),
          blurRadius: 10,
          offset: const Offset(0, 2),
        ),
      ],
    ),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Text(
              '参与者',
              style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold),
            ),
            Text(
              '${activity.participants.length}人',
              style: TextStyle(
                fontSize: 14.sp,
                color: const Color(0xFF2196F3),
                fontWeight: FontWeight.w600,
              ),
            ),
          ],
        ),
        SizedBox(height: 12.h),
        Wrap(
          spacing: 8.w,
          runSpacing: 8.h,
          children: activity.participants.take(10).map((participant) {
            return CircleAvatar(
              radius: 20.r,
              backgroundColor: const Color(0xFF2196F3).withOpacity(0.1),
              child: Text(
                participant.name.substring(0, 1),
                style: TextStyle(
                  fontSize: 14.sp,
                  color: const Color(0xFF2196F3),
                  fontWeight: FontWeight.bold,
                ),
              ),
            );
          }).toList(),
        ),
        if (activity.participants.length > 10) ...[
          SizedBox(height: 8.h),
          TextButton(
            onPressed: () => _showAllParticipants(),
            child: const Text('查看全部参与者'),
          ),
        ],
      ],
    ),
  );
}

void _showAllParticipants() {
  showModalBottomSheet(
    context: context,
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.vertical(top: Radius.circular(20.r)),
    ),
    builder: (context) => Container(
      padding: EdgeInsets.all(16.w),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            '全部参与者',
            style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold),
          ),
          SizedBox(height: 16.h),
          Expanded(
            child: ListView.builder(
              itemCount: activity.participants.length,
              itemBuilder: (context, index) {
                final participant = activity.participants[index];
                return ListTile(
                  leading: CircleAvatar(
                    backgroundColor: const Color(0xFF2196F3).withOpacity(0.1),
                    child: Text(
                      participant.name.substring(0, 1),
                      style: const TextStyle(color: Color(0xFF2196F3)),
                    ),
                  ),
                  title: Text(participant.name),
                  subtitle: Text(participant.role),
                );
              },
            ),
          ),
        ],
      ),
    ),
  );
}

参与者列表显示已报名成员的头像,最多显示10个,超过10个时显示"查看全部"按钮。点击按钮弹出底部菜单,展示完整的参与者列表。

头像使用圆形设计,显示姓名首字母。这种设计让用户能够快速了解有哪些人参加活动,增加活动的社交属性。参与者列表也能激励更多人报名参加。

活动评论功能

支持用户对活动进行评论:

List<Comment> _comments = [];
final _commentController = TextEditingController();

Widget _buildCommentsSection() {
  return Container(
    margin: EdgeInsets.all(16.w),
    padding: EdgeInsets.all(16.w),
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: BorderRadius.circular(12.r),
      boxShadow: [
        BoxShadow(
          color: Colors.black.withOpacity(0.05),
          blurRadius: 10,
          offset: const Offset(0, 2),
        ),
      ],
    ),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          '评论 (${_comments.length})',
          style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold),
        ),
        SizedBox(height: 12.h),
        if (_comments.isEmpty)
          Center(
            child: Padding(
              padding: EdgeInsets.symmetric(vertical: 20.h),
              child: Text(
                '暂无评论,快来抢沙发吧',
                style: TextStyle(fontSize: 14.sp, color: Colors.grey[500]),
              ),
            ),
          )
        else
          ..._comments.map((comment) => _buildCommentItem(comment)),
        SizedBox(height: 12.h),
        Row(
          children: [
            Expanded(
              child: TextField(
                controller: _commentController,
                decoration: InputDecoration(
                  hintText: '说点什么...',
                  border: OutlineInputBorder(
                    borderRadius: BorderRadius.circular(20.r),
                  ),
                  contentPadding: EdgeInsets.symmetric(
                    horizontal: 16.w,
                    vertical: 8.h,
                  ),
                ),
              ),
            ),
            SizedBox(width: 8.w),
            IconButton(
              onPressed: _addComment,
              icon: const Icon(Icons.send),
              color: const Color(0xFF2196F3),
            ),
          ],
        ),
      ],
    ),
  );
}

Widget _buildCommentItem(Comment comment) {
  return Container(
    margin: EdgeInsets.only(bottom: 12.h),
    child: Row(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        CircleAvatar(
          radius: 16.r,
          backgroundColor: const Color(0xFF2196F3).withOpacity(0.1),
          child: Text(
            comment.userName.substring(0, 1),
            style: TextStyle(
              fontSize: 12.sp,
              color: const Color(0xFF2196F3),
            ),
          ),
        ),
        SizedBox(width: 8.w),
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Row(
                children: [
                  Text(
                    comment.userName,
                    style: TextStyle(
                      fontSize: 13.sp,
                      fontWeight: FontWeight.w600,
                    ),
                  ),
                  SizedBox(width: 8.w),
                  Text(
                    DateFormat('MM-dd HH:mm').format(comment.createdAt),
                    style: TextStyle(fontSize: 11.sp, color: Colors.grey[500]),
                  ),
                ],
              ),
              SizedBox(height: 4.h),
              Text(
                comment.content,
                style: TextStyle(fontSize: 13.sp),
              ),
            ],
          ),
        ),
      ],
    ),
  );
}

void _addComment() {
  if (_commentController.text.trim().isEmpty) return;
  
  setState(() {
    _comments.add(Comment(
      id: DateTime.now().millisecondsSinceEpoch.toString(),
      userName: '当前用户', // 实际应用中从用户信息获取
      content: _commentController.text.trim(),
      createdAt: DateTime.now(),
    ));
    _commentController.clear();
  });
}

评论功能让用户可以对活动发表看法和提问。评论列表显示用户头像、姓名、时间和内容。底部的输入框支持快速发表评论,点击发送按钮即可提交。

评论功能增加了活动的互动性,用户可以在评论区交流活动相关的信息。组织者也可以通过评论区回答参与者的问题,提供更好的服务。

活动提醒设置

支持设置活动开始前的提醒:

bool _reminderEnabled = false;
int _reminderMinutes = 30;

Widget _buildReminderSetting() {
  return Container(
    margin: EdgeInsets.all(16.w),
    padding: EdgeInsets.all(16.w),
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: BorderRadius.circular(12.r),
      boxShadow: [
        BoxShadow(
          color: Colors.black.withOpacity(0.05),
          blurRadius: 10,
          offset: const Offset(0, 2),
        ),
      ],
    ),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Text(
              '活动提醒',
              style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold),
            ),
            Switch(
              value: _reminderEnabled,
              onChanged: (value) {
                setState(() => _reminderEnabled = value);
                if (value) {
                  _scheduleReminder();
                } else {
                  _cancelReminder();
                }
              },
              activeColor: const Color(0xFF2196F3),
            ),
          ],
        ),
        if (_reminderEnabled) ...[
          SizedBox(height: 12.h),
          Text(
            '提前提醒时间',
            style: TextStyle(fontSize: 14.sp, color: Colors.grey[600]),
          ),
          SizedBox(height: 8.h),
          Wrap(
            spacing: 8.w,
            children: [15, 30, 60, 120].map((minutes) {
              final isSelected = _reminderMinutes == minutes;
              return ChoiceChip(
                label: Text('${minutes}分钟'),
                selected: isSelected,
                onSelected: (selected) {
                  if (selected) {
                    setState(() => _reminderMinutes = minutes);
                    _scheduleReminder();
                  }
                },
                selectedColor: const Color(0xFF2196F3).withOpacity(0.2),
                labelStyle: TextStyle(
                  color: isSelected ? const Color(0xFF2196F3) : Colors.grey[700],
                ),
              );
            }).toList(),
          ),
        ],
      ],
    ),
  );
}

void _scheduleReminder() {
  final reminderTime = activity.startTime.subtract(
    Duration(minutes: _reminderMinutes),
  );
  
  // 这里应该调用本地通知服务设置提醒
  Get.snackbar(
    '提醒已设置',
    '将在活动开始前$_reminderMinutes分钟提醒您',
    snackPosition: SnackPosition.BOTTOM,
  );
}

void _cancelReminder() {
  // 取消已设置的提醒
  Get.snackbar(
    '已取消提醒',
    '',
    snackPosition: SnackPosition.BOTTOM,
  );
}

活动提醒功能让用户可以设置在活动开始前收到通知。提供15分钟、30分钟、1小时、2小时四个时间选项。开关控制是否启用提醒,选择芯片选择提前时间。

这个功能帮助用户不会错过重要的活动。提醒时间可以根据用户的习惯自由选择,满足不同场景的需求。实际应用中需要集成本地通知服务来实现真正的提醒功能。

活动签到功能

活动开始后支持签到:

bool _isCheckedIn = false;

Widget _buildCheckInButton() {
  if (!activity.isJoined) return const SizedBox.shrink();
  
  final now = DateTime.now();
  final canCheckIn = now.isAfter(activity.startTime.subtract(const Duration(minutes: 30))) &&
      now.isBefore(activity.endTime);
  
  if (!canCheckIn) return const SizedBox.shrink();
  
  return Container(
    margin: EdgeInsets.all(16.w),
    child: ElevatedButton.icon(
      onPressed: _isCheckedIn ? null : _checkIn,
      icon: Icon(_isCheckedIn ? Icons.check_circle : Icons.qr_code_scanner),
      label: Text(_isCheckedIn ? '已签到' : '扫码签到'),
      style: ElevatedButton.styleFrom(
        backgroundColor: _isCheckedIn ? Colors.grey : const Color(0xFF4CAF50),
        minimumSize: Size(double.infinity, 48.h),
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(12.r),
        ),
      ),
    ),
  );
}

void _checkIn() {
  // 这里应该打开扫码界面或其他签到方式
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: const Text('签到成功'),
      content: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(
            Icons.check_circle,
            size: 64.sp,
            color: const Color(0xFF4CAF50),
          ),
          SizedBox(height: 16.h),
          Text(
            '签到时间:${DateFormat('HH:mm').format(DateTime.now())}',
            style: TextStyle(fontSize: 14.sp),
          ),
        ],
      ),
      actions: [
        TextButton(
          onPressed: () {
            Navigator.pop(context);
            setState(() => _isCheckedIn = true);
          },
          child: const Text('确定'),
        ),
      ],
    ),
  );
}

签到功能在活动开始前30分钟到活动结束期间可用。只有已报名的用户才能看到签到按钮。签到后按钮变为灰色不可点击状态,显示"已签到"。

签到功能可以帮助组织者统计实际参与人数,也能作为用户参与活动的凭证。实际应用中可以集成二维码扫描功能,实现快速签到。

总结

活动详情页面通过丰富的功能模块,为用户提供了完整的活动信息和便捷的操作。从基本信息展示到报名管理,从地图导航到评论互动,每个功能都经过精心设计。

地图定位、分享功能、参与者列表增加了活动的实用性和社交属性。评论功能、提醒设置、签到功能提升了用户的参与体验。整个页面的设计既美观又实用,为社团活动管理提供了强大的支持。


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

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐