first commit

This commit is contained in:
Your Name
2026-06-10 18:23:39 +08:00
commit 790110de8e
225 changed files with 10068 additions and 0 deletions

View File

@@ -0,0 +1,216 @@
import 'dart:ui';
import 'package:flutter/material.dart';
typedef RewardAddTimeHandler =
Future<void> Function(void Function(int sec) grant);
class ResultModal extends StatefulWidget {
final bool visible;
final String status;
final int score;
final int bestScore;
final bool hasNext;
final VoidCallback onRetry;
final VoidCallback onNext;
final bool showAddTime;
final int addTimeSeconds;
final RewardAddTimeHandler? onRequestRewardAddTime;
const ResultModal({
super.key,
required this.visible,
required this.status,
required this.score,
required this.bestScore,
required this.hasNext,
required this.onRetry,
required this.onNext,
this.showAddTime = false,
this.addTimeSeconds = 30,
this.onRequestRewardAddTime,
});
@override
State<ResultModal> createState() => _ResultModalState();
}
class _ResultModalState extends State<ResultModal> {
String? toast;
bool rewardLoading = false;
void showToast(String msg) {
setState(() => toast = msg);
Future.delayed(const Duration(milliseconds: 1800), () {
if (mounted && toast == msg) {
setState(() => toast = null);
}
});
}
Future<void> _handleRewardAddTime() async {
if (rewardLoading) return;
setState(() => rewardLoading = true);
try {
if (widget.onRequestRewardAddTime != null) {
await widget.onRequestRewardAddTime!((_) {});
}
} catch (_) {
showToast('Rewarded ad failed to load. Please try again.');
} finally {
if (mounted) {
setState(() => rewardLoading = false);
}
}
}
@override
Widget build(BuildContext context) {
if (!widget.visible) return const SizedBox.shrink();
final isWin = widget.status == 'won';
final showNext = isWin && widget.hasNext;
final showAddTime = !isWin && widget.showAddTime;
return Positioned.fill(
child: Material(
color: Colors.transparent,
child: Stack(
children: [
Positioned.fill(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 8, sigmaY: 8),
child: Container(color: const Color.fromRGBO(0, 0, 0, 0.35)),
),
),
Center(
child: Container(
width: MediaQuery.of(context).size.width.clamp(0, 520).toDouble(),
margin: const EdgeInsets.symmetric(horizontal: 16),
child: AspectRatio(
aspectRatio: 0.67,
child: Stack(
fit: StackFit.expand,
children: [
Positioned.fill(
child: Image.asset(
isWin
? 'assets/images/settlement/win.png'
: 'assets/images/settlement/lose.png',
fit: BoxFit.fill,
),
),
Positioned.fill(
child: Padding(
padding: const EdgeInsets.fromLTRB(34, 168, 34, 32),
child: Column(
children: [
Text(
'Best Score',
style: TextStyle(
fontSize: isWin ? 22 : 20,
fontWeight: FontWeight.w900,
color: isWin
? const Color(0xFFB944D7)
: const Color(0xFF4F6BDE),
),
),
const SizedBox(height: 16),
Image.asset(
'assets/images/settlement/gold.png',
width: 118,
height: 118,
),
const SizedBox(height: 12),
Text(
'${widget.bestScore}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: isWin ? 38 : 34,
fontWeight: FontWeight.w900,
color: isWin
? const Color(0xFFFF6CA8)
: const Color(0xFF4F6BDE),
letterSpacing: 1,
),
),
const Spacer(),
Row(
children: [
if (showNext) ...[
Expanded(
child: _ActionImageButton(
asset: 'assets/images/settlement/next.png',
onTap: widget.onNext,
),
),
const SizedBox(width: 14),
],
if (showAddTime) ...[
Expanded(
child: _ActionImageButton(
asset: 'assets/images/settlement/addTime.png',
onTap: widget.onRequestRewardAddTime == null
? null
: _handleRewardAddTime,
),
),
const SizedBox(width: 14),
],
Expanded(
child: _ActionImageButton(
asset: 'assets/images/settlement/retry.png',
onTap: widget.onRetry,
),
),
],
),
if (toast != null) ...[
const SizedBox(height: 12),
Text(
toast!,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700,
),
textAlign: TextAlign.center,
),
],
],
),
),
),
],
),
),
),
),
],
),
),
);
}
}
class _ActionImageButton extends StatelessWidget {
const _ActionImageButton({
required this.asset,
required this.onTap,
});
final String asset;
final VoidCallback? onTap;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Opacity(
opacity: onTap == null ? 0.55 : 1,
child: SizedBox(
height: 56,
child: Image.asset(asset, fit: BoxFit.fill),
),
),
);
}
}