Add daily streak system and normalize bundle id to com.airkjw.blockseasons

Pure advanceStreak (1-day grace none, milestone flags at 3/7/14/30),
persisted in the save blob; StreakNotifier advances on every finished
attempt; home screen flame chip and milestone snackbar. Fix flutter
create's camelCased iOS bundle id and underscored Android application
id to the agreed lowercase form before any store registration.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 17:25:39 +09:00
parent 7bc26447f7
commit 607278928b
14 changed files with 271 additions and 15 deletions
+12
View File
@@ -113,12 +113,24 @@ class _GameScreenState extends ConsumerState<GameScreen> {
.recordWin(stars: next.starsEarned, score: next.score);
}
if (next.phase == GamePhase.lost) audio.play(Sfx.lose);
if (next.phase == GamePhase.won || next.phase == GamePhase.lost) {
ref.read(streakProvider.notifier).onStagePlayed(DateTime.now());
}
}
}
@override
Widget build(BuildContext context) {
ref.listen<GameViewState?>(gameSessionProvider, _onSessionChange);
ref.listen(streakProvider, (prev, next) {
final milestone = next.hitMilestone;
if (milestone != null && prev?.hitMilestone != milestone) {
final l10n = AppLocalizations.of(context)!;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(l10n.streakMilestone(milestone))),
);
}
});
final view = ref.watch(gameSessionProvider);
if (view == null) {
return const Scaffold(body: Center(child: CircularProgressIndicator()));
+19 -2
View File
@@ -1,14 +1,17 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../l10n/gen/app_localizations.dart';
import '../../state/providers.dart';
import 'season_map_screen.dart';
class HomeScreen extends StatelessWidget {
class HomeScreen extends ConsumerWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
Widget build(BuildContext context, WidgetRef ref) {
final l10n = AppLocalizations.of(context)!;
final streak = ref.watch(streakProvider);
return Scaffold(
body: SafeArea(
child: Center(
@@ -21,6 +24,20 @@ class HomeScreen extends StatelessWidget {
fontWeight: FontWeight.bold,
),
),
if (streak.current > 0) ...[
const SizedBox(height: 12),
Chip(
avatar: const Icon(
Icons.local_fire_department,
color: Colors.deepOrange,
size: 20,
),
label: Text(
'${streak.current}',
style: Theme.of(context).textTheme.titleMedium,
),
),
],
const SizedBox(height: 48),
FilledButton(
style: FilledButton.styleFrom(