Files
Siro/siro_driver/lib/views/gamification/challenges_page.dart
T

193 lines
7.5 KiB
Dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../constant/colors.dart';
import '../../../controller/gamification/challenges_controller.dart';
import '../widgets/ui/siro_ui.dart';
class ChallengesPage extends StatelessWidget {
ChallengesPage({super.key});
final ChallengesController controller = Get.put(ChallengesController());
@override
Widget build(BuildContext context) {
return Scaffold(
body: GetBuilder<ChallengesController>(builder: (cc) {
if (cc.isLoading) {
return const SiroLoading();
}
return CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: [
SliverAppBar(
expandedHeight: 150,
pinned: true,
foregroundColor: Colors.white,
backgroundColor: AppColor.primaryColor,
actions: [
IconButton(
icon: const Icon(Icons.refresh_rounded,
color: Colors.white),
onPressed: () => cc.fetchChallengeProgress())
],
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
titlePadding: const EdgeInsets.symmetric(
horizontal: 48, vertical: 14),
title: Text('Challenges'.tr,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 17)),
background: Stack(fit: StackFit.expand, children: [
Container(
decoration: BoxDecoration(
gradient: SiroUi.brandGradient(context))),
PositionedDirectional(
end: -30,
top: -10,
child: Icon(Icons.bolt_rounded,
size: 160,
color: Colors.white.withValues(alpha: 0.05))),
]),
),
),
SliverPadding(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 32),
sliver: SliverList(
delegate: SliverChildListDelegate([
// Daily Challenges
_sectionHeader(
context,
'Daily Challenges'.tr,
cc.dailyChallenges.where((c) => c.isCompleted).length,
cc.dailyChallenges.length),
...cc.dailyChallenges
.map((c) => _challengeCard(context, c, cc)),
const SizedBox(height: 16),
// Weekly Challenges
_sectionHeader(
context,
'Weekly Challenges'.tr,
cc.weeklyChallenges.where((c) => c.isCompleted).length,
cc.weeklyChallenges.length),
...cc.weeklyChallenges
.map((c) => _challengeCard(context, c, cc)),
]))),
]);
}),
);
}
Widget _sectionHeader(
BuildContext context, String title, int done, int total) {
return SiroSectionTitle(
title,
trailing: SiroTag('$done/$total', color: SiroUi.interactive(context)),
);
}
Widget _challengeCard(
BuildContext context, Challenge c, ChallengesController cc) {
final isAr = Get.locale?.languageCode == 'ar';
return SiroCard(
accent: c.isCompleted ? c.color : null,
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Row(children: [
SiroIconBadge(c.icon, color: c.color, size: 22),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(isAr ? c.titleAr : c.titleEn,
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(fontWeight: FontWeight.bold)),
Text(isAr ? c.descriptionAr : c.descriptionEn,
style: Theme.of(context)
.textTheme
.bodySmall
?.copyWith(fontSize: 11)),
])),
const SizedBox(width: 8),
SiroTag('+${c.reward}',
color: AppColor.yellowColor, icon: Icons.star_rounded),
]),
const SizedBox(height: 14),
// Progress bar
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Text('${c.currentProgress}/${c.target}',
style: Theme.of(context)
.textTheme
.bodySmall
?.copyWith(fontSize: 11, fontWeight: FontWeight.w600)),
Text('${(c.progress * 100).toStringAsFixed(0)}%',
style: TextStyle(
fontSize: 11, fontWeight: FontWeight.bold, color: c.color)),
]),
const SizedBox(height: 6),
Stack(children: [
Container(
height: 8,
decoration: BoxDecoration(
color: SiroUi.border(context),
borderRadius: BorderRadius.circular(4))),
LayoutBuilder(
builder: (ctx, cons) => AnimatedContainer(
duration: const Duration(milliseconds: 600),
curve: Curves.easeOutCubic,
height: 8,
width: cons.maxWidth * c.progress,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [c.color, c.color.withValues(alpha: 0.6)]),
borderRadius: BorderRadius.circular(4),
),
)),
]),
// Claim button
if (c.isCompleted && !c.isClaimed) ...[
const SizedBox(height: 12),
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: cc.isClaiming(c.id) ? null : () => cc.claimReward(c),
icon: cc.isClaiming(c.id)
? const SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
),
)
: const Icon(Icons.card_giftcard_rounded, size: 18),
label: Text(
cc.isClaiming(c.id) ? 'Claiming...'.tr : 'Claim Reward'.tr),
style: ElevatedButton.styleFrom(
backgroundColor: c.color,
foregroundColor: Colors.white,
),
)),
],
if (c.isClaimed) ...[
const SizedBox(height: 8),
Center(
child: Row(mainAxisSize: MainAxisSize.min, children: [
const Icon(Icons.check_circle_rounded,
color: AppColor.greenColor, size: 16),
const SizedBox(width: 4),
Text('Claimed'.tr,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: AppColor.greenColor)),
])),
],
]),
);
}
}