Flutter for OpenHarmony 万能游戏库App实战 - 瑞克和莫蒂角色详情实现
这篇文章我们实现了一个信息丰富的角色详情页面。- 实现复杂的滚动效果- 创建可展开的AppBar渐变遮罩- 使用LinearGradient提高文字可读性信息展示- 使用Card、Row、Icon等组件清晰地展示信息列表展示- 使用Wrap实现自动换行的列表角色详情页面虽然功能相对简单,但通过精心的设计和布局,能为用户提供一个沉浸式的角色浏览体验。欢迎加入开源鸿蒙跨平台社区:https://ope
角色详情页面是展示角色信息的重要窗口。这篇文章我们来实现一个信息丰富的角色详情页面,包括角色头像、基本信息、出场剧集列表、以及收藏功能。通过精心的设计,我们能为用户提供一个沉浸式的角色浏览体验。
页面的基本结构
CharacterDetailScreen接收一个角色对象作为参数:
class CharacterDetailScreen extends StatefulWidget {
final Map<String, dynamic> character;
const CharacterDetailScreen({super.key, required this.character});
State<CharacterDetailScreen> createState() => _CharacterDetailScreenState();
}
class _CharacterDetailScreenState extends State<CharacterDetailScreen> {
Map<String, dynamic> get character => widget.character;
Color _getStatusColor(String status) {
switch (status.toLowerCase()) {
case 'alive':
return Colors.green;
case 'dead':
return Colors.red;
default:
return Colors.grey;
}
}
使用getter来简化对character的访问。
_getStatusColor方法根据角色状态返回对应的颜色。
收藏功能
_toggleFavorite方法处理收藏操作:
void _toggleFavorite() {
final favorites = context.read<FavoritesProvider>();
final isFav = favorites.isFavorite(character['id'].toString(), 'rick_morty');
favorites.toggleFavorite(FavoriteItem(
id: character['id'].toString(),
type: 'rick_morty',
name: character['name'] ?? '',
imageUrl: character['image'],
data: character,
));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(isFav ? '已取消收藏' : '已添加到收藏'),
duration: const Duration(seconds: 1),
),
);
}
使用context.read来获取FavoritesProvider。
创建一个FavoriteItem对象,包含角色的所有必要信息。
调用toggleFavorite来切换收藏状态。
显示SnackBar提示用户操作结果。
页面的整体布局
页面用CustomScrollView实现复杂的滚动效果:
Widget build(BuildContext context) {
final episodes = character['episode'] as List? ?? [];
return Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 350,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
background: Stack(
fit: StackFit.expand,
children: [
AppNetworkImage(imageUrl: character['image'] ?? '', fit: BoxFit.cover, borderRadius: BorderRadius.zero),
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.transparent, Colors.black.withOpacity(0.7)],
),
),
),
],
),
),
SliverAppBar的expandedHeight为350,这样能充分展示角色头像。
pinned: true表示AppBar滚动到顶部时会固定。
背景用Stack实现,包含角色图片和一个渐变遮罩。渐变遮罩从透明到黑色,这样能提高文字的可读性。
收藏按钮
AppBar右上角有一个收藏按钮:
actions: [
Consumer<FavoritesProvider>(
builder: (context, favorites, _) {
final isFav = favorites.isFavorite(character['id'].toString(), 'rick_morty');
return IconButton(
icon: Icon(isFav ? Icons.favorite : Icons.favorite_border, color: Colors.white),
onPressed: _toggleFavorite,
);
},
),
],
使用Consumer来监听FavoritesProvider的变化。
根据收藏状态显示不同的图标:已收藏时显示实心心形,未收藏时显示空心心形。
角色基本信息
SliverToBoxAdapter用来展示角色的基本信息:
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(character['name'] ?? '', style: Theme.of(context).textTheme.headlineMedium?.copyWith(fontWeight: FontWeight.bold)),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: _getStatusColor(character['status'] ?? ''),
borderRadius: BorderRadius.circular(20),
),
child: Text(character['status'] ?? '', style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
),
],
),
角色名称用headlineMedium样式,加粗显示。
状态用一个圆角容器展示,背景颜色根据状态变化。
角色的物种和性别信息:
const SizedBox(height: 8),
Text('${character['species'] ?? ''} • ${character['gender'] ?? ''}', style: TextStyle(fontSize: 16, color: Colors.grey[600])),
用•分隔物种和性别,这样能清晰地展示多个属性。
详细信息卡片
_buildInfoCard方法展示角色的详细信息:
const SizedBox(height: 24),
_buildInfoCard(context),
_buildInfoCard的实现:
Widget _buildInfoCard(BuildContext context) {
final origin = character['origin'] as Map<String, dynamic>?;
final location = character['location'] as Map<String, dynamic>?;
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
_buildInfoRow(Icons.public, '起源', origin?['name'] ?? 'Unknown'),
const Divider(),
_buildInfoRow(Icons.location_on, '当前位置', location?['name'] ?? 'Unknown'),
const Divider(),
_buildInfoRow(Icons.category, '物种', character['species'] ?? 'Unknown'),
const Divider(),
_buildInfoRow(Icons.person, '性别', character['gender'] ?? 'Unknown'),
],
),
),
);
}
用Card包装详细信息,这样能突出显示这些信息。
每个信息用_buildInfoRow来显示,用Divider分隔。
_buildInfoRow方法创建信息行:
Widget _buildInfoRow(IconData icon, String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Row(
children: [
Icon(icon, size: 20, color: Colors.grey),
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.end)),
],
),
);
}
每行包含一个图标、标签和值。
用Spacer把值推到右边。
用Flexible包装值,防止文字过长时溢出。
出场剧集列表
出场剧集用Wrap展示:
const SizedBox(height: 24),
Text('出场剧集 (${episodes.length})', style: Theme.of(context).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold)),
const SizedBox(height: 12),
Wrap(
spacing: 8,
runSpacing: 8,
children: episodes.take(20).map((e) {
final episodeNum = e.toString().split('/').last;
return Chip(label: Text('EP $episodeNum'));
}).toList(),
),
if (episodes.length > 20)
Padding(
padding: const EdgeInsets.only(top: 8),
child: Text('还有 ${episodes.length - 20} 集...', style: const TextStyle(color: Colors.grey)),
),
用Wrap展示剧集,这样能自动换行。
只显示前20集,如果超过20集就显示"还有X集…"的提示。
从剧集URL中提取剧集号。
总结
这篇文章我们实现了一个信息丰富的角色详情页面。涉及到的知识点包括:
- CustomScrollView - 实现复杂的滚动效果
- SliverAppBar - 创建可展开的AppBar
- 渐变遮罩 - 使用LinearGradient提高文字可读性
- 收藏功能 - 集成收藏功能到详情页面
- 信息展示 - 使用Card、Row、Icon等组件清晰地展示信息
- 列表展示 - 使用Wrap实现自动换行的列表
角色详情页面虽然功能相对简单,但通过精心的设计和布局,能为用户提供一个沉浸式的角色浏览体验。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐


所有评论(0)