Initial commit for driver_tripz

This commit is contained in:
Hamza-Ayed
2026-01-20 23:41:53 +03:00
parent 441385069c
commit 42a09bc6fa
25 changed files with 1954 additions and 1538 deletions

View File

@@ -1,54 +1,278 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import 'package:sefer_driver/views/widgets/mycircular.dart';
import '../../../controller/payment/driver_payment_controller.dart';
// --- DESIGN PALETTE IS NOW UPDATED TO USE AppColor ---
const kLightBackgroundColor = Color(0xFFF5F8FA);
const kCardBackgroundColor = AppColor.secondaryColor; // Using AppColor
const kPrimaryBlue = AppColor.primaryColor; // Using AppColor
const kPrimaryTextColor = Color(0xFF14171A);
const kSecondaryTextColor = Color(0xFF657786);
const kPositiveAmountColor = AppColor.greenColor; // Using AppColor
const kNegativeAmountColor = AppColor.redColor; // Using AppColor
class PaymentHistoryDriverPage extends StatelessWidget {
const PaymentHistoryDriverPage({super.key});
@override
Widget build(BuildContext context) {
// Controller initialization remains the same.
Get.put(DriverWalletHistoryController());
return MyScafolld(
title: 'Payment History'.tr,
body: [
GetBuilder<DriverWalletHistoryController>(
builder: (controller) => controller.isLoading
? const MyCircularProgressIndicator()
: ListView.builder(
itemCount: controller.archive.length,
itemBuilder: (BuildContext context, int index) {
var list = controller.archive[index];
return Padding(
padding: const EdgeInsets.all(4),
child: Container(
decoration: BoxDecoration(
color: double.parse(list['amount']) < 0
? AppColor.redColor.withOpacity(.4)
: AppColor.greenColor.withOpacity(.4)),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
list['amount'],
style: AppStyle.title,
),
Text(
list['created_at'],
style: AppStyle.title,
),
],
),
),
);
},
),
)
return Scaffold(
backgroundColor: kLightBackgroundColor,
appBar: AppBar(
title: Text(
'Payment History'.tr,
style: const TextStyle(
color: kPrimaryTextColor,
fontWeight: FontWeight.bold,
),
),
backgroundColor: kCardBackgroundColor,
elevation: 0.5,
centerTitle: true,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios, color: kPrimaryBlue),
onPressed: () => Get.back(),
),
),
body: GetBuilder<DriverWalletHistoryController>(
builder: (controller) {
// Smooth transition between loading and content states.
return AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
child: controller.isLoading
? _buildLoadingState()
: _buildContentState(controller),
);
},
),
);
}
/// Builds the loading state UI.
Widget _buildLoadingState() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(kPrimaryBlue),
),
SizedBox(height: 20),
Text(
"Loading History...".tr,
style: TextStyle(color: kSecondaryTextColor, fontSize: 16),
),
],
isleading: true);
),
);
}
/// Builds the main content when data is available.
Widget _buildContentState(DriverWalletHistoryController controller) {
if (controller.archive.isEmpty) {
return _buildEmptyState();
}
return ListView.builder(
padding: const EdgeInsets.all(16.0),
itemCount: controller.archive.length,
itemBuilder: (BuildContext context, int index) {
var transaction = controller.archive[index];
// Animated entry for each card
return _AnimatedListItem(
index: index,
child: _HistoryCard(transaction: transaction),
);
},
);
}
/// A user-friendly message for when there are no transactions.
Widget _buildEmptyState() {
return Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.history_toggle_off,
size: 80,
color: Colors.grey.shade300,
),
const SizedBox(height: 20),
Text(
'No History Found'.tr,
style: const TextStyle(
color: kPrimaryTextColor,
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
'Your entire transaction history will be displayed here.'.tr,
textAlign: TextAlign.center,
style: const TextStyle(
color: kSecondaryTextColor,
fontSize: 16,
),
),
],
),
),
);
}
}
/// A custom widget for a single history transaction card.
class _HistoryCard extends StatelessWidget {
final Map<String, dynamic> transaction;
const _HistoryCard({required this.transaction});
@override
Widget build(BuildContext context) {
// Determine if the amount is positive or negative
final double amount =
double.tryParse(transaction['amount'] ?? '0.0') ?? 0.0;
final bool isPositive = amount >= 0;
// Safely parse and format the date
String formattedDate = 'No Date';
try {
final date = DateTime.parse(transaction['created_at']);
formattedDate = DateFormat('MMM d, yyyy hh:mm a').format(date);
} catch (e) {
// Keep the original string if parsing fails
formattedDate = transaction['created_at'] ?? 'Invalid Date';
}
final Color amountColor =
isPositive ? kPositiveAmountColor : kNegativeAmountColor;
final IconData amountIcon =
isPositive ? Icons.arrow_upward : Icons.arrow_downward;
final String title = isPositive ? 'Credit'.tr : 'Debit'.tr;
return Card(
margin: const EdgeInsets.symmetric(vertical: 8.0),
elevation: 2,
shadowColor: Colors.grey.withOpacity(0.1),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
// Icon
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: amountColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Icon(amountIcon, color: amountColor, size: 28),
),
const SizedBox(width: 16),
// Title and Date
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
color: kPrimaryTextColor,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
formattedDate,
style: const TextStyle(
color: kSecondaryTextColor,
fontSize: 13,
),
),
],
),
),
// Amount
Text(
'${isPositive ? '+' : ''}${transaction['amount']} ${'L.E'.tr}',
style: TextStyle(
color: amountColor,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
],
),
),
);
}
}
/// A simple animation widget to fade in list items.
class _AnimatedListItem extends StatefulWidget {
final int index;
final Widget child;
const _AnimatedListItem({required this.index, required this.child});
@override
State<_AnimatedListItem> createState() => _AnimatedListItemState();
}
class _AnimatedListItemState extends State<_AnimatedListItem>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 400),
);
_animation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.easeOut,
),
);
// Stagger the animation based on the item index
Future.delayed(Duration(milliseconds: widget.index * 75), () {
if (mounted) {
_controller.forward();
}
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return FadeTransition(
opacity: _animation,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 0.2),
end: Offset.zero,
).animate(_controller),
child: widget.child,
),
);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,141 +1,297 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import 'package:sefer_driver/views/widgets/mycircular.dart';
import 'package:intl/intl.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/controller/payment/driver_payment_controller.dart';
import '../../../controller/payment/driver_payment_controller.dart';
// --- NEW LIGHT & DYNAMIC DESIGN PALETTE (TWITTER BLUE INSPIRED) ---
const kLightBackgroundColor = Color(0xFFF5F8FA); // Off-white
const kCardBackgroundColor = Color(0xFFFFFFFF); // Pure white for cards
const kPrimaryBlue = Color(0xFF1DA1F2); // Twitter Blue
const kSecondaryBlue = Color(0xFFE8F5FE); // Very light blue for accents
const kPrimaryTextColor = Color(0xFF14171A); // Almost black
const kSecondaryTextColor = Color(0xFF657786); // Gray for subtitles
const kGreenAccent = Color(0xFF17BF63); // Green for income
class WeeklyPaymentPage extends StatelessWidget {
const WeeklyPaymentPage({super.key});
@override
Widget build(BuildContext context) {
// Controller initialization remains the same.
Get.put(DriverWalletHistoryController());
return MyScafolld(
title: 'Payment History'.tr,
body: [
GetBuilder<DriverWalletHistoryController>(
builder: (controller) => controller.isLoading
? const MyCircularProgressIndicator()
: Column(
children: [
Container(
width: Get.width * .8,
decoration: AppStyle.boxDecoration1,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
controller.weeklyList.isEmpty
? '0'
: controller.weeklyList[0]
['totalAmount']
.toString(),
style: AppStyle.number,
),
),
),
),
Text(
' Total weekly is '.tr,
style: AppStyle.title,
),
],
),
),
const SizedBox(
height: 10,
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 5),
child: SizedBox(
height: Get.height * .75,
child: controller.weeklyList.isNotEmpty
? ListView.builder(
itemCount: controller.weeklyList.length,
itemBuilder:
(BuildContext context, int index) {
var list = controller.weeklyList[index];
return Padding(
padding: const EdgeInsets.all(2.0),
child: Container(
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(4),
child: Column(
children: [
Card(
elevation: 2,
color: list['paymentMethod'] ==
'visa'
? AppColor.blueColor
: AppColor.secondaryColor,
child: Padding(
padding:
const EdgeInsets.all(8.0),
child: Text(
list['paymentMethod'] ==
'Remainder'
? 'Remainder'.tr
: list['paymentMethod'] ==
'fromBudget'
? 'fromBudget'.tr
: list[
'paymentMethod'],
style: AppStyle.title,
),
),
),
Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Card(
child: Padding(
padding:
const EdgeInsets.all(
8.0),
child: Text(
list['amount'],
style: AppStyle.number,
),
),
),
Text(
DateFormat(
'yyyy-MM-dd hh:mm a')
.format(DateTime.parse(
list[
'dateUpdated'])),
style: AppStyle.number,
),
],
),
],
),
),
),
);
},
)
: const SizedBox(),
),
),
],
),
)
return Scaffold(
backgroundColor: AppColor.secondaryColor,
appBar: AppBar(
title: Text(
'Weekly Payments'.tr,
style: const TextStyle(
color: AppColor.blueColor,
fontWeight: FontWeight.bold,
),
),
backgroundColor: kCardBackgroundColor,
elevation: 0.5,
centerTitle: true,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios, color: kPrimaryBlue),
onPressed: () => Get.back(),
),
),
body: GetBuilder<DriverWalletHistoryController>(
builder: (controller) {
// Smooth transition between loading and content states.
return AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
child: controller.isLoading
? _buildLoadingState()
: _buildContentState(controller),
);
},
),
);
}
/// Builds the loading state UI.
Widget _buildLoadingState() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(kPrimaryBlue),
),
SizedBox(height: 20),
Text(
"Fetching Weekly Data...".tr,
style: TextStyle(color: kSecondaryTextColor, fontSize: 16),
),
],
isleading: true);
),
);
}
/// Builds the main content when data is available.
Widget _buildContentState(DriverWalletHistoryController controller) {
if (controller.weeklyList.isEmpty) {
return _buildEmptyState();
}
return ListView(
padding: const EdgeInsets.all(16.0),
children: [
_buildTotalSummaryCard(controller),
const SizedBox(height: 24),
_buildSectionTitle('Transaction Details'.tr),
const SizedBox(height: 8),
_buildTransactionList(controller),
],
);
}
/// A prominent card to display the total weekly amount.
Widget _buildTotalSummaryCard(DriverWalletHistoryController controller) {
return Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [kPrimaryBlue, kPrimaryBlue.withOpacity(0.8)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: kPrimaryBlue.withOpacity(0.3),
blurRadius: 12,
offset: const Offset(0, 6),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Total Weekly Earnings'.tr,
style: const TextStyle(
color: Colors.white70,
fontSize: 18,
),
),
const SizedBox(height: 8),
Text(
'${controller.weeklyList.isEmpty ? '0' : controller.weeklyList[0]['totalAmount'].toString()} ${'L.E'.tr}',
style: const TextStyle(
color: Colors.white,
fontSize: 40,
fontWeight: FontWeight.bold,
),
),
],
),
);
}
/// Builds the list of transaction cards.
Widget _buildTransactionList(DriverWalletHistoryController controller) {
return ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: controller.weeklyList.length,
itemBuilder: (BuildContext context, int index) {
var transaction = controller.weeklyList[index];
return _TransactionCard(transaction: transaction);
},
);
}
/// A user-friendly message for when there are no transactions.
Widget _buildEmptyState() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.receipt_long_outlined,
size: 80,
color: Colors.grey.shade300,
),
const SizedBox(height: 20),
Text(
'No Transactions Yet'.tr,
style: const TextStyle(
color: kPrimaryTextColor,
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
'Your weekly payment history will appear here.'.tr,
textAlign: TextAlign.center,
style: const TextStyle(
color: kSecondaryTextColor,
fontSize: 16,
),
),
],
),
);
}
/// A helper for creating styled section titles.
Widget _buildSectionTitle(String title) {
return Text(
title,
style: const TextStyle(
color: kPrimaryTextColor,
fontSize: 20,
fontWeight: FontWeight.bold,
),
);
}
}
/// A custom widget for a single transaction card for better code organization.
class _TransactionCard extends StatelessWidget {
final Map<String, dynamic> transaction;
const _TransactionCard({required this.transaction});
/// Helper to get descriptive details for each payment method.
({IconData icon, String title, Color color}) _getPaymentDetails(
String paymentMethod) {
switch (paymentMethod) {
case 'visa':
return (
icon: Icons.credit_card,
title: 'Visa Payout'.tr,
color: kPrimaryBlue
);
case 'fromBudget':
return (
icon: Icons.arrow_upward,
title: 'Points Purchase'.tr,
color: Colors.orange
);
case 'Remainder':
return (
icon: Icons.account_balance_wallet_outlined,
title: 'Cash Balance'.tr,
color: kGreenAccent
);
default:
return (
icon: Icons.receipt_long,
title: paymentMethod.tr,
color: kSecondaryTextColor
);
}
}
@override
Widget build(BuildContext context) {
final details = _getPaymentDetails(transaction['paymentMethod']);
final date = DateTime.parse(transaction['dateUpdated']);
final formattedDate = DateFormat('MMM d, yyyy').format(date);
final formattedTime = DateFormat('hh:mm a').format(date);
return Card(
margin: const EdgeInsets.symmetric(vertical: 8.0),
elevation: 2,
shadowColor: Colors.grey.withOpacity(0.1),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
// Icon
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: details.color.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Icon(details.icon, color: details.color, size: 28),
),
const SizedBox(width: 16),
// Title and Date
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
details.title,
style: const TextStyle(
color: kPrimaryTextColor,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
'$formattedDate at $formattedTime',
style: const TextStyle(
color: kSecondaryTextColor,
fontSize: 13,
),
),
],
),
),
// Amount
Text(
'${transaction['amount']} ${'L.E'.tr}',
style: const TextStyle(
color: kPrimaryTextColor,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
],
),
),
);
}
}