Flutter for OpenHarmony 万能游戏库App实战 - 免费游戏列表实现
本文介绍了如何实现一个功能完整的免费游戏列表页面,支持多维度筛选和Tab切换。主要内容包括: 页面架构设计:采用TabController管理三个游戏平台(全部/PC/浏览器),配合分类筛选功能实现多维度筛选。 数据加载逻辑:根据选中的分类和平台加载游戏数据,支持串行和并行加载两种方式。 UI实现细节: 顶部TabBar实现平台切换 横向FilterChip列表实现分类筛选 TabBarView展
免费游戏是游戏库App的核心内容。与宝可梦图鉴不同,游戏列表需要支持多维度的筛选:按平台(PC、浏览器)、按分类(MMORPG、射击、策略等)。这篇文章我们来实现一个功能完整的免费游戏列表页面,包括Tab切换、分类筛选、列表展示等功能。
页面的整体架构
首先看看页面的设计。免费游戏列表分为两个维度的筛选:
class FreeGamesScreen extends StatefulWidget {
const FreeGamesScreen({super.key});
State<FreeGamesScreen> createState() => _FreeGamesScreenState();
}
class _FreeGamesScreenState extends State<FreeGamesScreen> with SingleTickerProviderStateMixin {
final FreeToGameApi _api = FreeToGameApi();
late TabController _tabController;
List<dynamic> _allGames = [];
List<dynamic> _pcGames = [];
List<dynamic> _browserGames = [];
bool _isLoading = true;
String _selectedCategory = 'all';
final List<String> _categories = ['all', 'mmorpg', 'shooter', 'strategy', 'moba', 'racing', 'sports', 'social', 'card'];
final Map<String, String> _categoryNames = {
'all': '全部',
'mmorpg': 'MMORPG',
'shooter': '射击',
'strategy': '策略',
'moba': 'MOBA',
'racing': '竞速',
'sports': '体育',
'social': '社交',
'card': '卡牌',
};
这个页面用SingleTickerProviderStateMixin来支持TabController。_tabController管理三个Tab:全部、PC、浏览器。
_selectedCategory记录当前选中的分类。_categories列表定义了所有可用的分类,_categoryNames是分类的中文名称映射。
TabController的初始化
initState中初始化TabController:
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
_loadGames();
}
void dispose() {
_tabController.dispose();
super.dispose();
}
TabController需要指定length(Tab数量)和vsync(通常是this)。vsync用来同步动画,确保Tab切换时的动画流畅。
在dispose中一定要释放TabController,否则会导致内存泄漏。
多维度的数据加载
_loadGames方法根据选中的分类和平台加载游戏:
Future<void> _loadGames() async {
setState(() => _isLoading = true);
try {
final category = _selectedCategory == 'all' ? null : _selectedCategory;
final all = await _api.getGameList(category: category);
final pc = await _api.getGameList(platform: 'pc', category: category);
final browser = await _api.getGameList(platform: 'browser', category: category);
setState(() {
_allGames = all;
_pcGames = pc;
_browserGames = browser;
_isLoading = false;
});
} catch (e) {
setState(() => _isLoading = false);
}
}
这个方法并行加载三个API请求:全部游戏、PC游戏、浏览器游戏。每个请求都可以指定分类。
当_selectedCategory为’all’时,category参数为null,这样API会返回所有分类的游戏。
三个请求是串行的(一个接一个),如果要提升性能可以用Future.wait并行加载。
页面的UI结构
页面分为三部分:AppBar、分类筛选、游戏列表:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('免费游戏'),
bottom: TabBar(
controller: _tabController,
tabs: const [
Tab(text: '全部'),
Tab(text: 'PC'),
Tab(text: '浏览器'),
],
),
),
AppBar的bottom是一个TabBar,显示三个Tab。TabBar会自动处理Tab之间的切换。
分类筛选的实现:
body: Column(
children: [
SizedBox(
height: 50,
child: ListView.builder(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
itemCount: _categories.length,
itemBuilder: (context, index) {
final cat = _categories[index];
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: FilterChip(
label: Text(_categoryNames[cat] ?? cat),
selected: _selectedCategory == cat,
onSelected: (selected) {
setState(() => _selectedCategory = cat);
_loadGames();
},
),
);
},
),
),
分类筛选用横向的ListView来展示。每个分类是一个FilterChip,用户可以点击来选择。
FilterChip的selected属性控制是否被选中。当用户选择一个分类时,更新_selectedCategory并重新加载游戏。
这个设计很灵活,用户可以快速切换分类,而不需要打开菜单。
TabBarView的内容
TabBarView根据选中的Tab显示不同的游戏列表:
Expanded(
child: _isLoading
? const LoadingWidget()
: TabBarView(
controller: _tabController,
children: [
_buildGameList(_allGames),
_buildGameList(_pcGames),
_buildGameList(_browserGames),
],
),
),
TabBarView会根据_tabController的当前索引显示对应的子Widget。用户切换Tab时,TabBarView会自动切换显示的内容。
加载中时显示LoadingWidget,加载完成后显示TabBarView。
游戏列表的展示
_buildGameList方法构建游戏列表:
Widget _buildGameList(List<dynamic> games) {
if (games.isEmpty) return const EmptyWidget(message: '暂无游戏');
return ListView.builder(
padding: const EdgeInsets.all(12),
itemCount: games.length,
itemBuilder: (context, index) {
final game = games[index];
return Card(
margin: const EdgeInsets.only(bottom: 12),
clipBehavior: Clip.antiAlias,
child: InkWell(
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => GameDetailScreen(gameId: game['id']))),
child: Row(
children: [
AppNetworkImage(
imageUrl: game['thumbnail'] ?? '',
width: 120,
height: 80,
borderRadius: BorderRadius.zero,
),
每个游戏用一个Card来展示,包含游戏的缩略图、标题、描述、分类和平台。
用Row来横向排列图片和文字,这样能充分利用屏幕空间。
游戏信息的展示:
Expanded(
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(game['title'] ?? '', style: const TextStyle(fontWeight: FontWeight.bold), maxLines: 1, overflow: TextOverflow.ellipsis),
const SizedBox(height: 4),
Text(game['short_description'] ?? '', style: TextStyle(fontSize: 12, color: Colors.grey[600]), maxLines: 2, overflow: TextOverflow.ellipsis),
const SizedBox(height: 8),
Row(
children: [
TagChip(label: game['genre'] ?? '', color: Colors.blue),
const SizedBox(width: 8),
TagChip(label: game['platform'] ?? '', color: Colors.green),
],
),
],
),
),
),
用Expanded让文字部分占满剩余空间。Column中从上到下显示:标题、描述、分类和平台。
标题用粗体显示,描述用较小的灰色文字。分类和平台用TagChip来展示,这样看起来很整洁。
所有文字都限制了行数和overflow,防止文字超出卡片。
分类筛选的设计
分类筛选是这个页面的一个重要特性。用户可以快速切换分类来查看不同类型的游戏:
final List<String> _categories = ['all', 'mmorpg', 'shooter', 'strategy', 'moba', 'racing', 'sports', 'social', 'card'];
final Map<String, String> _categoryNames = {
'all': '全部',
'mmorpg': 'MMORPG',
'shooter': '射击',
'strategy': '策略',
'moba': 'MOBA',
'racing': '竞速',
'sports': '体育',
'social': '社交',
'card': '卡牌',
};
_categories列表定义了所有可用的分类。_categoryNames是分类的中文名称映射。
这样设计的好处是易于维护。如果以后要添加新的分类,只需要在这两个地方添加即可。
多维度筛选的实现
这个页面支持两个维度的筛选:
- 平台维度 - 通过Tab切换(全部、PC、浏览器)
- 分类维度 - 通过FilterChip切换(MMORPG、射击、策略等)
这两个维度是独立的,用户可以在任何Tab下选择任何分类。比如用户可以查看"PC上的MMORPG游戏"或"浏览器上的卡牌游戏"。
用户体验的细节
1. 快速筛选
分类筛选用FilterChip实现,用户可以快速点击来切换分类,不需要打开菜单。
2. 视觉反馈
选中的分类会用不同的样式显示,用户能清楚地看到当前选中的分类。
3. 空状态处理
如果某个分类或平台没有游戏,会显示"暂无游戏"的提示。
4. 合理的布局
游戏列表用Row来横向排列图片和文字,这样能充分利用屏幕空间,显示更多信息。
性能考虑
虽然代码看起来简单,但有几个性能相关的点:
1. 数据缓存
_allGames、_pcGames、_browserGames分别缓存三个平台的游戏。当用户切换Tab时,不需要重新加载数据。
2. 列表优化
ListView.builder是懒加载的,只有即将显示的游戏卡片才会被构建。
3. 图片优化
游戏缩略图用AppNetworkImage加载,这样能处理加载中和加载失败的情况。
可能的改进
虽然当前的实现已经很完整,但还有一些可能的改进:
1. 并行加载
当前三个API请求是串行的,可以用Future.wait改成并行加载,提升性能。
2. 分页加载
如果游戏数量很多,可以实现分页加载,而不是一次性加载所有游戏。
3. 搜索功能
可以添加搜索功能,让用户快速找到想要的游戏。
4. 排序功能
可以添加排序功能,比如按热度、按发布时间等排序。
总结
这篇文章我们实现了一个功能完整的免费游戏列表页面。涉及到的知识点包括:
- TabController - 如何管理多个Tab
- 多维度筛选 - 如何实现平台和分类的双维度筛选
- FilterChip - 使用FilterChip实现快速筛选
- 列表布局 - 用Row和Column组合实现复杂的列表项布局
- 数据管理 - 如何缓存和管理多个数据源
- 用户体验 - 快速筛选、视觉反馈等细节
一个好的列表页面能让用户快速找到想要的内容。通过合理的筛选设计和清晰的信息展示,我们能创造一个高效的浏览体验。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)