fix: journey map season-complete styling, scroll callback guard, palette constant
- Guard addPostFrameCallback with !_autoScrolled so it only fires once per widget lifetime instead of on every LayoutBuilder rebuild. - Derive seasonComplete flag; pass it into _node so the last stage uses gold "done" styling (not "current/next") when the season is fully 3-starred. - Extract const Color(0xFF232B4A) to GamePalette.lockedNode. - Remove warnIfMissed: false from tap call in season_map_screen_test; ensureVisible already guarantees hit-testing succeeds (confirmed: test still passes cleanly). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -65,6 +65,8 @@ class _JourneyMapState extends ConsumerState<_JourneyMap> {
|
||||
final ids = [for (final stage in pack.stages) stage.id];
|
||||
final unlocked = repo.highestUnlockedIndex(pack.seasonId, ids);
|
||||
final totalStars = repo.totalStars(pack.seasonId);
|
||||
final seasonComplete = totalStars == pack.stages.length * 3 &&
|
||||
pack.stages.isNotEmpty;
|
||||
final locale = Localizations.localeOf(context).languageCode;
|
||||
final colors = ThemeColors(pack.theme);
|
||||
|
||||
@@ -78,9 +80,11 @@ class _JourneyMapState extends ConsumerState<_JourneyMap> {
|
||||
builder: (context, constraints) {
|
||||
final layout = MapLayout(width: constraints.maxWidth);
|
||||
final count = pack.stages.length;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) =>
|
||||
_autoScrollTo(
|
||||
layout, unlocked, count, constraints.maxHeight));
|
||||
if (!_autoScrolled) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) =>
|
||||
_autoScrollTo(
|
||||
layout, unlocked, count, constraints.maxHeight));
|
||||
}
|
||||
return SingleChildScrollView(
|
||||
controller: _scroll,
|
||||
reverse: true,
|
||||
@@ -104,6 +108,7 @@ class _JourneyMapState extends ConsumerState<_JourneyMap> {
|
||||
unlocked,
|
||||
repo.progressFor(pack.seasonId, ids[i])?.stars ?? 0,
|
||||
colors,
|
||||
seasonComplete,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -166,9 +171,9 @@ class _JourneyMapState extends ConsumerState<_JourneyMap> {
|
||||
}
|
||||
|
||||
Widget _node(BuildContext context, MapLayout layout, int i, int count,
|
||||
int unlocked, int stars, ThemeColors colors) {
|
||||
int unlocked, int stars, ThemeColors colors, bool seasonComplete) {
|
||||
final center = layout.nodeCenter(i, count);
|
||||
final isCurrent = i == unlocked;
|
||||
final isCurrent = i == unlocked && !seasonComplete;
|
||||
final isUnlocked = i <= unlocked;
|
||||
final size = isCurrent ? 64.0 : 52.0;
|
||||
|
||||
@@ -213,7 +218,7 @@ class _JourneyMapState extends ConsumerState<_JourneyMap> {
|
||||
],
|
||||
)
|
||||
: null,
|
||||
color: isUnlocked ? null : const Color(0xFF232B4A),
|
||||
color: isUnlocked ? null : GamePalette.lockedNode,
|
||||
boxShadow: isCurrent
|
||||
? [
|
||||
BoxShadow(
|
||||
|
||||
@@ -22,6 +22,7 @@ class GamePalette {
|
||||
|
||||
static Color tile(int colorId) => tileColors[colorId % tileColors.length];
|
||||
|
||||
static const lockedNode = Color(0xFF232B4A);
|
||||
static const gem = Color(0xFF7CF5FF);
|
||||
static const ghostLegal = Color(0x66FFFFFF);
|
||||
static const ghostIllegal = Color(0x55FF5252);
|
||||
|
||||
@@ -89,10 +89,7 @@ void main() {
|
||||
// Stage 1 is starred, so stage 2 (index 1) is unlocked and playable.
|
||||
// Ensure the node is visible before tapping.
|
||||
await tester.ensureVisible(find.byKey(const Key('stage_node_1')));
|
||||
await tester.tap(
|
||||
find.byKey(const Key('stage_node_1')),
|
||||
warnIfMissed: false,
|
||||
);
|
||||
await tester.tap(find.byKey(const Key('stage_node_1')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(GameScreen), findsOneWidget);
|
||||
|
||||
Reference in New Issue
Block a user