Flutter for OpenHarmony 社团管理App实战 - 发现社团实现
摘要: 本文介绍了Flutter中实现"发现社团"页面的开发过程。该页面作为用户探索和加入社团的入口,主要功能包括分类浏览社团、查看排行榜和分类筛选。技术实现上采用StatefulWidget管理状态,TabController实现分类切换,Consumer监听数据变化。页面布局包含AppBar、TabBar和ListView,社团信息以卡片形式展示,点击可跳转详情页。关键点包

发现社团是用户探索和加入新社团的主要入口,可以按分类浏览所有社团。这篇文章带大家实现发现社团模块。
页面结构搭建
发现社团页面需要管理TabController,用StatefulWidget来实现:
class DiscoverPage extends StatefulWidget {
const DiscoverPage({super.key});
State<DiscoverPage> createState() => _DiscoverPageState();
}
StatefulWidget允许我们在页面内部维护可变状态。
const构造函数可以让Flutter在重建时复用Widget实例。
导入依赖包
在文件开头导入需要的包:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
material.dart提供Material Design风格的组件。
provider用于状态管理。
还需要导入项目内的文件:
import '../../providers/app_provider.dart';
import '../../models/club.dart';
import '../club/club_detail_page.dart';
import '../club/club_ranking_page.dart';
import '../club/club_category_page.dart';
这些是项目内部的页面和模型文件。
使用相对路径引入,…/…/表示向上两级目录。
状态变量定义
定义TabController和分类列表:
class _DiscoverPageState extends State<DiscoverPage>
with SingleTickerProviderStateMixin {
late TabController _tabController;
final List<String> _categories = [
'全部', '科技', '艺术', '体育', '学术', '公益', '文娱'
];
SingleTickerProviderStateMixin为TabController提供vsync参数。
分类列表包含全部和六个具体分类。
生命周期管理
在initState和dispose中管理TabController:
void initState() {
super.initState();
_tabController = TabController(
length: _categories.length,
vsync: this
);
}
initState中创建TabController,length参数指定Tab数量。
super.initState()必须首先调用。
释放资源:
void dispose() {
_tabController.dispose();
super.dispose();
}
dispose中释放TabController,避免内存泄漏。
这是Flutter开发中的最佳实践。
构建AppBar
AppBar包含标题、操作按钮和TabBar:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('发现社团'),
actions: [
Scaffold是Material Design的基础布局组件。
actions数组放置AppBar右侧的操作按钮。
排行榜按钮:
IconButton(
icon: const Icon(Icons.leaderboard),
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const ClubRankingPage()
)
),
),
leaderboard图标表示排行榜功能。
点击跳转到社团排行榜页面。
分类按钮:
IconButton(
icon: const Icon(Icons.category),
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const ClubCategoryPage()
)
),
),
],
category图标表示分类功能。
两个快捷入口满足不同的浏览需求。
配置TabBar
TabBar放在AppBar的bottom位置:
bottom: TabBar(
controller: _tabController,
isScrollable: true,
indicatorColor: Colors.white,
tabs: _categories.map((c) => Tab(text: c)).toList(),
),
),
isScrollable设为true让TabBar可以横向滚动。
白色指示器在蓝色背景上更醒目。
内容区域
使用Consumer监听AppProvider的数据变化:
body: Consumer<AppProvider>(
builder: (context, provider, _) {
return TabBarView(
controller: _tabController,
Consumer会在数据变化时自动重建子组件。
TabBarView和TabBar共用controller保证同步。
根据分类筛选社团:
children: _categories.map((category) {
final clubs = category == '全部'
? provider.clubs
: provider.clubs
.where((c) => c.category == category)
.toList();
return _buildClubList(clubs);
}).toList(),
);
},
),
);
}
全部分类显示所有社团,其他分类根据category字段筛选。
map方法将每个分类转换为对应的列表视图。
社团列表构建
定义构建社团列表的方法:
Widget _buildClubList(List<Club> clubs) {
if (clubs.isEmpty) {
return const Center(
child: Text(
'暂无社团',
style: TextStyle(color: Colors.grey)
)
);
}
空状态时显示友好的提示文字。
灰色文字不会太突兀。
使用ListView.builder构建列表:
return ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: clubs.length,
itemBuilder: (context, index) {
final club = clubs[index];
ListView.builder是懒加载列表,性能更好。
padding设置16像素内边距。
社团卡片设计
每个社团用Card组件包裹:
return Card(
margin: const EdgeInsets.only(bottom: 12),
child: InkWell(
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => ClubDetailPage(club: club)
)
),
Card自带阴影和圆角。
InkWell提供水波纹点击效果。
卡片内容区:
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Padding设置16像素内边距。
Row横向排列头像和信息区。
社团头像
左侧显示社团头像:
CircleAvatar(
radius: 30,
backgroundColor: const Color(0xFF4A90E2)
.withOpacity(0.1),
child: Text(
club.name[0],
style: const TextStyle(
color: Color(0xFF4A90E2),
fontSize: 24,
fontWeight: FontWeight.bold
)
),
),
const SizedBox(width: 16),
CircleAvatar显示社团名称的首字母。
蓝色配色和整体风格保持一致。
社团名称和已加入标签
显示社团名称,如果已加入则显示标签:
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
club.name,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16
)
)
),
社团名称用粗体16像素字号。
Expanded让名称占据剩余空间。
已加入标签:
if (club.isJoined)
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 2
),
decoration: BoxDecoration(
color: Colors.green.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: const Text(
'已加入',
style: TextStyle(
color: Colors.green,
fontSize: 12
)
),
),
],
),
已加入标签用绿色,和其他状态标签区分。
条件渲染只在已加入时显示。
社团简介
显示社团简介:
const SizedBox(height: 4),
Text(
club.description,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Colors.grey,
fontSize: 13
)
),
简介限制两行显示,超出部分用省略号。
灰色小字作为辅助信息。
分类和统计信息
显示分类标签、成员数和评分:
const SizedBox(height: 8),
Row(
children: [
Container(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 2
),
decoration: BoxDecoration(
color: const Color(0xFF4A90E2)
.withOpacity(0.1),
borderRadius: BorderRadius.circular(4),
),
child: Text(
club.category,
style: const TextStyle(
color: Color(0xFF4A90E2),
fontSize: 11
)
),
),
分类标签用蓝色背景,和整体风格保持一致。
圆角设为4像素。
成员数和评分:
const SizedBox(width: 8),
const Icon(
Icons.people,
size: 14,
color: Colors.grey
),
const SizedBox(width: 4),
Text(
'${club.memberCount}人',
style: const TextStyle(
fontSize: 12,
color: Colors.grey
)
),
const SizedBox(width: 12),
const Icon(
Icons.star,
size: 14,
color: Colors.amber
),
const SizedBox(width: 4),
Text(
club.rating.toStringAsFixed(1),
style: const TextStyle(
fontSize: 12,
color: Colors.grey
)
),
],
),
],
),
),
],
),
),
),
);
},
);
}
}
成员数用people图标,评分用amber色的star图标。
toStringAsFixed(1)保留一位小数。
TabBar可滚动的设计
当分类数量较多时,TabBar设置为可滚动是更好的选择。
这样可以容纳更多的分类,用户通过左右滑动查看所有分类。
如果强制一行显示,每个Tab会变得很窄,文字可能显示不全。
可滚动的TabBar在移动端是常见的设计模式。
社团卡片的信息密度
社团卡片在有限空间内展示了丰富的信息。
包括头像、名称、已加入状态、简介、分类、成员数、评分等。
这些信息帮助用户快速了解社团的基本情况。
用户可以决定是否要查看详情或申请加入。
小结
发现社团页面通过TabBar实现分类浏览功能。用户可以按照全部、科技、艺术等分类筛选社团。社团卡片展示头像、名称、简介、分类、成员数、评分等信息,已加入的社团显示绿色标签。点击卡片可以进入社团详情页面。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐
所有评论(0)