306 lines
14 KiB
Dart
306 lines
14 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import '../bloc/redemption_bloc.dart';
|
|
import '../bloc/redemption_event.dart';
|
|
import '../bloc/redemption_state.dart';
|
|
import '../viewmodel/redemption_view_model.dart';
|
|
import 'manual_entry_screen.dart';
|
|
|
|
|
|
class TicketRedemptionScreen extends StatelessWidget {
|
|
const TicketRedemptionScreen({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return BlocProvider(
|
|
create: (_) => RedemptionBloc()..add(LoadRecentRedemptions()),
|
|
child: Builder(
|
|
builder: (context) {
|
|
final vm = RedemptionViewModel(context.read<RedemptionBloc>());
|
|
return BlocBuilder<RedemptionBloc, RedemptionState>(
|
|
builder: (context, state) {
|
|
return Scaffold(
|
|
backgroundColor: Colors.white,
|
|
// appBar: AppBar(
|
|
// backgroundColor: Colors.white,
|
|
// elevation: 0,
|
|
// leading: IconButton(
|
|
// icon: const Icon(Icons.arrow_back, color: Colors.black),
|
|
// onPressed: () => Navigator.pop(context),
|
|
// ),
|
|
// title: const Text(
|
|
// "Ticket Redemption",
|
|
// style: TextStyle(
|
|
// color: Colors.black,
|
|
// fontWeight: FontWeight.w600,
|
|
// ),
|
|
// ),
|
|
// ),
|
|
body: SafeArea(
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_buildHeaderSection(context),
|
|
const SizedBox(height: 68),
|
|
const Text(
|
|
"Recent Redemptions",
|
|
style: TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
Expanded(
|
|
child: ListView.builder(
|
|
itemCount: state.recentRedemptions.length,
|
|
itemBuilder: (context, i) {
|
|
final item = state.recentRedemptions[i];
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: 12.0),
|
|
child: Container(
|
|
height: 111,
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xffF5F5F5),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: [
|
|
/// 🧾 Left Content (Title + Subtitle Row)
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
/// Title
|
|
Text(
|
|
item['title'],
|
|
style: const TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.black,
|
|
),
|
|
),
|
|
const SizedBox(height: 6),
|
|
|
|
/// Subtitle Row (Date + Status)
|
|
Row(
|
|
children: [
|
|
Image.asset(
|
|
"assets/ticket/lets-icons_date-fill.png",
|
|
scale: 4,
|
|
),
|
|
const SizedBox(width: 8),
|
|
const Text(
|
|
"Today, 2:53 PM",
|
|
style: TextStyle(
|
|
fontSize: 13,
|
|
color: Colors.black87,
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Icon(
|
|
Icons.circle,
|
|
size: 10,
|
|
color: item['status'] ? Colors.green : Colors.red,
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
/// 🎟️ Trailing Ticket Image Section
|
|
Container(
|
|
height: 74,
|
|
width: 92,
|
|
decoration: const BoxDecoration(
|
|
borderRadius: BorderRadius.all(
|
|
Radius.circular(20),
|
|
),
|
|
color: Colors.white,
|
|
),
|
|
child: ClipRRect(
|
|
borderRadius: const BorderRadius.all(Radius.circular(17)),
|
|
child: Stack(
|
|
fit: StackFit.expand,
|
|
children: [
|
|
/// Background image
|
|
Image.asset(
|
|
"assets/ticket/dubai.jpg",
|
|
fit: BoxFit.cover,
|
|
),
|
|
|
|
/// Decorative vertical cutouts (aligned to right)
|
|
Align(
|
|
alignment: const FractionalOffset(0.7, 0.5),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Container(
|
|
height: 6,
|
|
width: 10,
|
|
decoration: const BoxDecoration(
|
|
borderRadius: BorderRadius.only(
|
|
bottomLeft: Radius.circular(20),
|
|
bottomRight: Radius.circular(20),
|
|
),
|
|
color: Color(0xffF5F5F5),
|
|
),
|
|
),
|
|
Container(
|
|
height: 12,
|
|
width: 8,
|
|
decoration: const BoxDecoration(
|
|
borderRadius: BorderRadius.all(Radius.circular(20)),
|
|
color: Color(0xffF5F5F5),
|
|
),
|
|
),
|
|
Container(
|
|
height: 12,
|
|
width: 8,
|
|
decoration: const BoxDecoration(
|
|
borderRadius: BorderRadius.all(Radius.circular(20)),
|
|
color: Color(0xffF5F5F5),
|
|
),
|
|
),
|
|
Container(
|
|
height: 6,
|
|
width: 8,
|
|
decoration: const BoxDecoration(
|
|
borderRadius: BorderRadius.only(
|
|
topRight: Radius.circular(20),
|
|
topLeft: Radius.circular(20),
|
|
),
|
|
color: Color(0xffF5F5F5),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
},
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
ElevatedButton(
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Color(0xffF95F62),
|
|
foregroundColor: Colors.white,
|
|
minimumSize: const Size.fromHeight(50),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
),
|
|
onPressed: () {},
|
|
child: const Text(
|
|
"Scan New Ticket",
|
|
style: TextStyle(fontWeight: FontWeight.w600),
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
OutlinedButton(
|
|
style: OutlinedButton.styleFrom(
|
|
side: const BorderSide(color: Colors.redAccent),
|
|
minimumSize: const Size.fromHeight(50),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
),
|
|
onPressed: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (_) => BlocProvider(
|
|
create: (_) => RedemptionBloc(),
|
|
child: const ManualEntryScreen(),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
child: const Text(
|
|
"Manual Ticket Entry",
|
|
style: TextStyle(
|
|
color: Colors.redAccent,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 5),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
|
|
Widget _buildHeaderSection(BuildContext context) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 2.0, vertical: 14.0),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
GestureDetector(
|
|
onTap: () {
|
|
Navigator.pop(context);
|
|
},
|
|
child: Container(
|
|
width: 44,
|
|
height: 44,
|
|
decoration: const BoxDecoration(
|
|
color: Color(0xFFF95F62),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: const Icon(Icons.arrow_back, color: Colors.white),
|
|
),
|
|
),
|
|
Text(
|
|
'Ticket Redemption',
|
|
style: const TextStyle(
|
|
fontWeight: FontWeight.w700,
|
|
fontSize: 28,
|
|
),
|
|
),
|
|
SizedBox(
|
|
width: 40,
|
|
)
|
|
],
|
|
),
|
|
),
|
|
SizedBox(height: 8),
|
|
Align(
|
|
alignment: Alignment.center,
|
|
child: Text(
|
|
'Redeem your passes at partner locations',
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
color: Colors.grey[600],
|
|
height: 1.4,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|