Add playable core UI: board painter, drag-and-drop, HUD, result overlay
CustomPainter board with gems/ghost/clear-flash, finger-lifted drag with snap preview, combo text effect, HUD chips, phase overlays with rescue stubs, demo stage. E2E widget test drives a real drag gesture. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../game/models/objective.dart';
|
||||
import '../../state/game_session_notifier.dart';
|
||||
|
||||
class HudWidget extends StatelessWidget {
|
||||
const HudWidget({super.key, required this.view});
|
||||
|
||||
final GameViewState view;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
_movesChip(theme),
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
transitionBuilder: (child, anim) =>
|
||||
ScaleTransition(scale: anim, child: child),
|
||||
child: Text(
|
||||
'${view.score}',
|
||||
key: ValueKey(view.score),
|
||||
style: theme.textTheme.headlineMedium
|
||||
?.copyWith(fontWeight: FontWeight.w800),
|
||||
),
|
||||
),
|
||||
_comboChip(theme),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
for (final obj in view.objectives) ...[
|
||||
_objectiveChip(theme, obj),
|
||||
const SizedBox(width: 12),
|
||||
],
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _movesChip(ThemeData theme) {
|
||||
return Chip(
|
||||
avatar: const Icon(Icons.swipe, size: 18),
|
||||
label: Text('${view.movesLeft}'),
|
||||
labelStyle: theme.textTheme.titleMedium,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _comboChip(ThemeData theme) {
|
||||
final visible = view.comboStreak >= 2;
|
||||
return AnimatedOpacity(
|
||||
duration: const Duration(milliseconds: 150),
|
||||
opacity: visible ? 1 : 0,
|
||||
child: Chip(
|
||||
avatar: const Icon(Icons.local_fire_department,
|
||||
size: 18, color: Colors.deepOrange),
|
||||
label: Text('x${view.comboStreak}'),
|
||||
labelStyle: theme.textTheme.titleMedium,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _objectiveChip(ThemeData theme, Objective obj) {
|
||||
final (icon, color) = switch (obj) {
|
||||
ClearGemsObjective() => (Icons.diamond, const Color(0xFF7CF5FF)),
|
||||
ReachScoreObjective() => (Icons.star, Colors.amber),
|
||||
ClearLinesObjective() => (Icons.table_rows, Colors.lightGreen),
|
||||
};
|
||||
final done = obj.isComplete;
|
||||
return Chip(
|
||||
avatar: Icon(done ? Icons.check_circle : icon, size: 18, color: color),
|
||||
label: Text('${obj.current.clamp(0, obj.target)}/${obj.target}'),
|
||||
labelStyle: theme.textTheme.titleSmall,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user