209 lines
9.2 KiB
Dart
209 lines
9.2 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:lucide_icons/lucide_icons.dart';
|
|
import '../../providers/theme_provider.dart';
|
|
import '../../providers/ui_experiment_provider.dart';
|
|
import '../contextual_help_icon.dart';
|
|
import '../../theme/app_colors.dart';
|
|
|
|
class BadgeCase extends ConsumerWidget {
|
|
const BadgeCase({super.key});
|
|
|
|
static const List<Map<String, dynamic>> _badges = [
|
|
// Activity Badges (登録数)
|
|
{'id': 'first_step', 'name': '初めての一歩', 'emoji': '🍶', 'icon': LucideIcons.footprints, 'desc': '最初の1本を登録'},
|
|
{'id': 'collector_10', 'name': '愛好家', 'emoji': '🎉', 'icon': LucideIcons.star, 'desc': '10本以上登録'},
|
|
{'id': 'collector_50', 'name': 'コレクター', 'emoji': '📚', 'icon': LucideIcons.award, 'desc': '50本以上登録'},
|
|
{'id': 'collector_100', 'name': 'レジェンド', 'emoji': '👑', 'icon': LucideIcons.crown, 'desc': '100本以上登録'},
|
|
|
|
// Regional Badges (地域制覇)
|
|
{'id': 'regional_tohoku', 'name': '東北制覇', 'emoji': '👹', 'icon': LucideIcons.snowflake, 'desc': '東北6県の日本酒を登録'},
|
|
{'id': 'regional_kanto', 'name': '関東制覇', 'emoji': '🗻', 'icon': LucideIcons.building2, 'desc': '関東7都県の日本酒を登録'},
|
|
{'id': 'regional_kansai', 'name': '関西制覇', 'emoji': '🏯', 'icon': LucideIcons.landmark, 'desc': '関西6府県の日本酒を登録'},
|
|
|
|
// Flavor Badges (味覚)
|
|
{'id': 'flavor_dry', 'name': '辛口党', 'emoji': '🌶️', 'icon': LucideIcons.flame, 'desc': '辛口(+5以上)を10本登録'},
|
|
{'id': 'flavor_sweet', 'name': '甘口党', 'emoji': '🍯', 'icon': LucideIcons.candy, 'desc': '甘口(-3以下)を10本登録'},
|
|
{'id': 'flavor_aromatic', 'name': '香りの貴族', 'emoji': '🌸', 'icon': LucideIcons.flower, 'desc': '華やか(80以上)を10本登録'},
|
|
];
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final userProfile = ref.watch(userProfileProvider);
|
|
final unlocked = userProfile.unlockedBadges.toSet();
|
|
final useIcons = ref.watch(uiExperimentProvider).useBadgeIcons;
|
|
final appColors = Theme.of(context).extension<AppColors>()!;
|
|
|
|
return Container(
|
|
padding: const EdgeInsets.all(20),
|
|
decoration: BoxDecoration(
|
|
color: Theme.of(context).cardColor,
|
|
borderRadius: BorderRadius.circular(16),
|
|
border: Border.all(
|
|
color: Theme.of(context).dividerColor.withValues(alpha: 0.1),
|
|
),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Expanded(
|
|
child: Row(
|
|
children: [
|
|
Text(
|
|
'バッジケース',
|
|
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
const SizedBox(width: 4),
|
|
ContextualHelpIcon(
|
|
title: 'バッジについて',
|
|
customContent: _buildHelpContent(context, useIcons),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: appColors.brandPrimary.withValues(alpha: 0.15),
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(
|
|
color: appColors.brandPrimary.withValues(alpha: 0.4),
|
|
),
|
|
),
|
|
child: Text(
|
|
'${unlocked.length} / ${_badges.length}',
|
|
style: TextStyle(
|
|
fontSize: 13,
|
|
fontWeight: FontWeight.bold,
|
|
color: appColors.brandPrimary,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 16),
|
|
Wrap(
|
|
spacing: 12,
|
|
runSpacing: 12,
|
|
children: _badges.map((badge) {
|
|
final isUnlocked = unlocked.contains(badge['id']);
|
|
return Tooltip(
|
|
message: '${badge['name']}\n${badge['desc']}',
|
|
triggerMode: TooltipTriggerMode.tap,
|
|
child: AnimatedContainer(
|
|
duration: const Duration(milliseconds: 300),
|
|
width: 64,
|
|
height: 80,
|
|
child: Column(
|
|
children: [
|
|
Container(
|
|
width: 50,
|
|
height: 50,
|
|
alignment: Alignment.center,
|
|
decoration: BoxDecoration(
|
|
color: isUnlocked
|
|
? appColors.brandPrimary.withValues(alpha: 0.2)
|
|
: appColors.surfaceSubtle,
|
|
shape: BoxShape.circle,
|
|
border: Border.all(
|
|
color: isUnlocked
|
|
? appColors.brandPrimary
|
|
: appColors.divider,
|
|
width: 2.5,
|
|
),
|
|
boxShadow: isUnlocked ? [
|
|
BoxShadow(
|
|
color: appColors.brandPrimary.withValues(alpha: 0.3),
|
|
blurRadius: 10,
|
|
spreadRadius: 1,
|
|
)
|
|
] : [],
|
|
),
|
|
child: isUnlocked
|
|
? (useIcons
|
|
? Icon(badge['icon'], color: appColors.brandPrimary, size: 24)
|
|
: Text(badge['emoji'], style: const TextStyle(fontSize: 24)))
|
|
: Icon(LucideIcons.lock, color: appColors.iconSubtle, size: 20),
|
|
),
|
|
const SizedBox(height: 4),
|
|
Text(
|
|
badge['name'],
|
|
style: TextStyle(
|
|
fontSize: 10,
|
|
fontWeight: isUnlocked ? FontWeight.bold : FontWeight.normal,
|
|
color: isUnlocked
|
|
? appColors.textPrimary
|
|
: appColors.textTertiary,
|
|
),
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}).toList(),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildHelpContent(BuildContext context, bool useIcons) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const Text('バッジは特定の条件を達成すると獲得できます。\n\n【活動バッジ】'),
|
|
..._buildBadgeHelpRows(context, useIcons, ['first_step', 'collector_10', 'collector_50', 'collector_100']),
|
|
const SizedBox(height: 12),
|
|
const Text('【地域バッジ】'),
|
|
..._buildBadgeHelpRows(context, useIcons, ['regional_tohoku', 'regional_kanto', 'regional_kansai']),
|
|
const SizedBox(height: 12),
|
|
const Text('【味覚バッジ】'),
|
|
..._buildBadgeHelpRows(context, useIcons, ['flavor_dry', 'flavor_sweet', 'flavor_aromatic']),
|
|
const SizedBox(height: 16),
|
|
const Text('バッジを集めて、日本酒マスターを目指しましょう!'),
|
|
],
|
|
);
|
|
}
|
|
|
|
List<Widget> _buildBadgeHelpRows(BuildContext context, bool useIcons, List<String> ids) {
|
|
final appColors = Theme.of(context).extension<AppColors>()!;
|
|
// If badge doesn't exist, skip it safely
|
|
final validIds = ids.where((id) => _badges.any((b) => b['id'] == id));
|
|
|
|
return validIds.map((id) {
|
|
final badge = _badges.firstWhere((b) => b['id'] == id);
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
|
child: Row(
|
|
children: [
|
|
SizedBox(
|
|
width: 24,
|
|
child: useIcons
|
|
? Icon(badge['icon'], size: 16, color: appColors.brandPrimary)
|
|
: Text(badge['emoji'], style: const TextStyle(fontSize: 16))
|
|
),
|
|
const SizedBox(width: 8),
|
|
Expanded(
|
|
child: RichText(
|
|
text: TextSpan(
|
|
style: Theme.of(context).textTheme.bodyMedium,
|
|
children: [
|
|
TextSpan(text: '${badge['name']}: ', style: const TextStyle(fontWeight: FontWeight.bold)),
|
|
TextSpan(text: badge['desc']),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}).toList();
|
|
}
|
|
}
|