Files
driver_tripz/lib/views/home/my_wallet/walet_captain.dart
2026-01-20 23:41:53 +03:00

774 lines
26 KiB
Dart
Executable File

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:local_auth/local_auth.dart';
import 'dart:ui'; // Required for BackdropFilter
// --- KEEPING ALL ORIGINAL IMPORTS AND CONTROLLERS ---
import 'package:sefer_driver/constant/links.dart';
import 'package:sefer_driver/controller/functions/crud.dart';
import 'package:sefer_driver/controller/home/payment/paymob_payout.dart';
import 'package:sefer_driver/views/home/my_wallet/bank_account_egypt.dart';
import 'package:sefer_driver/views/home/my_wallet/payment_history_driver_page.dart';
import 'package:sefer_driver/views/widgets/error_snakbar.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart';
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/info.dart';
import 'package:sefer_driver/controller/home/payment/captain_wallet_controller.dart';
import 'package:sefer_driver/main.dart';
import 'package:sefer_driver/views/widgets/elevated_btn.dart';
import 'package:sefer_driver/views/widgets/my_textField.dart';
import '../../../controller/payment/driver_payment_controller.dart';
import 'card_wallet_widget.dart';
import 'points_captain.dart';
import 'transfer_budget_page.dart';
import 'weekly_payment_page.dart';
// --- END OF ORIGINAL IMPORTS ---
// --- 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
// Accent colors remain the same for status indicators
const kRedAccent = Color(0xFFFF4D6D);
const kYellowAccent = Color(0xFFFFD166);
const kGreenAccent = Color(0xFF06D6A0);
class WalletCaptain extends StatelessWidget {
WalletCaptain({super.key});
// Controller remains unchanged as requested.
final CaptainWalletController captainWalletController =
Get.put(CaptainWalletController());
@override
Widget build(BuildContext context) {
// Logic to refresh data remains unchanged.
captainWalletController.refreshCaptainWallet();
// The main scaffold is replaced with a custom-themed one.
return Scaffold(
backgroundColor: kLightBackgroundColor,
appBar: AppBar(
title: Text(
'Driver Wallet'.tr,
style: const TextStyle(
color: kPrimaryTextColor,
fontWeight: FontWeight.bold,
letterSpacing: 1.2,
),
),
backgroundColor: kCardBackgroundColor,
elevation: 0.5,
centerTitle: true,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios, color: kPrimaryBlue),
onPressed: () => Get.back(),
),
actions: [
IconButton(
onPressed: () async {
captainWalletController.refreshCaptainWallet();
},
icon: const Icon(Icons.refresh, color: kPrimaryBlue),
),
],
),
body: GetBuilder<CaptainWalletController>(
builder: (controller) {
// AnimatedSwitcher provides a smooth transition between loading and content.
return AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
child: controller.isLoading
? _buildLoadingState()
: _buildContentState(context, controller),
);
},
),
);
}
/// Builds the loading state UI with a custom futuristic indicator.
Widget _buildLoadingState() {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(kPrimaryBlue),
),
SizedBox(height: 20),
Text(
"Fetching Wallet Data...",
style: TextStyle(color: kSecondaryTextColor, fontSize: 16),
),
],
),
);
}
/// Builds the main content when data is loaded.
Widget _buildContentState(
BuildContext context, CaptainWalletController controller) {
return ListView(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
children: [
// Each section is separated for clarity and wrapped in an animation widget.
_AnimatedFadeIn(delay: 100, child: _buildPointsHeader(controller)),
const SizedBox(height: 20),
if (double.parse(controller.totalPoints.toString()) < -300)
_AnimatedFadeIn(
delay: 200,
child: _DynamicButton(
title: 'Charge your Account'.tr,
onPressed: () {
// Add your charge account logic here
},
gradient: const LinearGradient(
colors: [kRedAccent, Color(0xFFF72585)],
),
),
),
_AnimatedFadeIn(
delay: 300, child: const CardSeferWalletDriver()), // Kept as is.
const SizedBox(height: 20),
_AnimatedFadeIn(delay: 400, child: _buildBudgetSection(controller)),
const SizedBox(height: 30),
_AnimatedFadeIn(
delay: 500, child: _buildSectionTitle("Daily Promos".tr)),
_AnimatedFadeIn(delay: 600, child: _buildPromoSection(controller)),
const SizedBox(height: 30),
_AnimatedFadeIn(delay: 700, child: _buildPurchaseInstructions()),
const SizedBox(height: 16),
_AnimatedFadeIn(delay: 800, child: _buildPointsOptions()),
const SizedBox(height: 30),
_AnimatedFadeIn(delay: 900, child: _buildActionButtons()),
],
);
}
/// A revolutionary header for displaying total points with dynamic background.
Widget _buildPointsHeader(CaptainWalletController controller) {
// Logic for color determination remains the same.
final double points = double.parse(controller.totalPoints.toString());
final Color indicatorColor = points < -300
? kRedAccent
: points < 0
? kYellowAccent
: kGreenAccent;
return GestureDetector(
onTap: () {
// This functionality is preserved.
Get.dialog(
CupertinoAlertDialog(
title: Text('Info'.tr),
content: Text(
'The 300 points equal 300 L.E for you \nSo go and gain your money'
.tr,
),
actions: <Widget>[
CupertinoDialogAction(
child: Text("OK".tr),
onPressed: () => Get.back(),
),
],
),
);
},
child: Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: kCardBackgroundColor,
borderRadius: BorderRadius.circular(20),
border: Border.all(color: Colors.grey.shade200, width: 1),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 5,
blurRadius: 10,
)
],
),
child: Column(
children: [
Text(
'Total Points'.tr,
style: const TextStyle(
color: kSecondaryTextColor,
fontSize: 18,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
controller.totalPoints.toString(),
style: TextStyle(
color: kPrimaryTextColor,
fontSize: 48,
fontWeight: FontWeight.bold,
),
),
const SizedBox(width: 10),
Icon(Icons.diamond_outlined, color: indicatorColor, size: 40),
],
),
],
),
),
);
}
/// A modern card container for the budget details.
Widget _buildBudgetSection(CaptainWalletController controller) {
return Card(
elevation: 4,
shadowColor: Colors.grey.withOpacity(0.2),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
color: kCardBackgroundColor,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
_buildBudgetRow(
icon: Icons.account_balance_wallet_outlined,
title: 'Total Budget from trips is'.tr,
amount: controller.totalAmount,
onTap: () {
// Original onTap logic is preserved.
Get.snackbar(
'${'Total Amount:'.tr} ${controller.totalAmount} ${'L.E'.tr}',
'This amount for all trip I get from Passengers'.tr,
backgroundColor: kYellowAccent.withOpacity(0.8),
snackPosition: SnackPosition.BOTTOM);
},
),
const Divider(color: kLightBackgroundColor, height: 30),
_buildBudgetRow(
icon: Icons.credit_card,
title: 'Total Budget from trips by\nCredit card is '.tr,
amount: controller.totalAmountVisa,
onTap: () {
// Original onTap logic is preserved.
Get.snackbar(
'${'Total Amount:'.tr} ${controller.totalAmountVisa} ${'L.E'.tr}',
'This amount for all trip I get from Passengers and Collected For me in'
.tr +
' ${AppInformation.appName} Wallet'.tr,
backgroundColor: kRedAccent.withOpacity(0.8),
snackPosition: SnackPosition.BOTTOM);
},
),
const SizedBox(height: 20),
_DynamicButton(
title: 'You can buy points from your budget'.tr,
onPressed: () => _showBuyFromBudgetDialog(controller),
),
const SizedBox(height: 12),
_DynamicButton(
title: 'Transfer budget'.tr,
onPressed: () => _handleTransferBudget(controller),
gradient: LinearGradient(
colors: [
kSecondaryTextColor,
kSecondaryTextColor.withOpacity(0.7)
],
),
),
],
),
),
);
}
/// A redesigned row for displaying budget information.
Widget _buildBudgetRow(
{required IconData icon,
required String title,
required String amount,
required VoidCallback onTap}) {
return Row(
children: [
Icon(icon, color: kPrimaryBlue, size: 28),
const SizedBox(width: 15),
Expanded(
child: Text(
title,
style: const TextStyle(
color: kSecondaryTextColor,
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: kSecondaryBlue,
borderRadius: BorderRadius.circular(12),
),
child: Text(
'$amount ${'L.E'.tr}',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: kPrimaryTextColor),
),
),
),
],
);
}
/// A section to display daily promo progress.
Widget _buildPromoSection(CaptainWalletController controller) {
return Column(
children: [
_buildPromoCard(
icon: Icons.wb_sunny_outlined,
title: 'Morning Promo'.tr,
timePromo: 'Morning Promo',
count: controller.walletDate['message'][0]['morning_count'],
maxCount: 5,
description:
"this is count of your all trips in the morning promo today from 7:00am-10:00am"
.tr,
controller: controller,
),
const SizedBox(height: 16),
_buildPromoCard(
icon: Icons.brightness_4_outlined,
title: 'Afternoon Promo'.tr,
timePromo: 'Afternoon Promo',
count: controller.walletDate['message'][0]['afternoon_count'],
maxCount: 5,
description:
"this is count of your all trips in the Afternoon promo today from 3:00pm-6:00 pm"
.tr,
controller: controller,
),
],
);
}
/// A redesigned, more visual promo card.
Widget _buildPromoCard(
{required IconData icon,
required String title,
required String timePromo,
required int count,
required int maxCount,
required String description,
required CaptainWalletController controller}) {
double progress = count / maxCount;
return GestureDetector(
onTap: () {
// Original onTap logic is preserved.
MyDialog().getDialog(title, description, () async {
if (count == 5) {
controller.addDriverWalletFromPromo(timePromo, 50);
}
Get.back();
});
},
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: kCardBackgroundColor,
borderRadius: BorderRadius.circular(15),
border: Border.all(color: Colors.grey.shade200),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Icon(icon, color: kPrimaryBlue, size: 24),
const SizedBox(width: 10),
Text(title,
style: const TextStyle(
color: kPrimaryTextColor,
fontSize: 18,
fontWeight: FontWeight.bold)),
],
),
Text('$count / $maxCount',
style: const TextStyle(
color: kSecondaryTextColor,
fontSize: 16,
fontWeight: FontWeight.w500)),
],
),
const SizedBox(height: 12),
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: LinearProgressIndicator(
value: progress,
minHeight: 10,
backgroundColor: kSecondaryBlue,
valueColor: const AlwaysStoppedAnimation<Color>(kPrimaryBlue),
),
),
],
),
),
);
}
/// Re-implementation of original purchase instructions with new styling.
Widget _buildPurchaseInstructions() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
"You can purchase a budget to enable online access through the options listed below."
.tr,
textAlign: TextAlign.center,
style: const TextStyle(color: kSecondaryTextColor, fontSize: 16),
),
);
}
/// RESTORED: This widget uses the original PointsCaptain widget to ensure payment logic is identical.
Widget _buildPointsOptions() {
return Container(
height: Get.height * 0.19,
child: ListView(
scrollDirection: Axis.horizontal,
children: [
PointsCaptain(
kolor: AppColor.greyColor,
pricePoint: box.read(BoxName.countryCode) == 'Jordan' ? 5 : 80,
countPoint:
box.read(BoxName.countryCode) == 'Jordan' ? '3000' : '80',
),
PointsCaptain(
kolor: AppColor.bronze,
pricePoint: box.read(BoxName.countryCode) == 'Jordan' ? 10 : 200,
countPoint:
box.read(BoxName.countryCode) == 'Jordan' ? '1040' : '210',
),
PointsCaptain(
kolor: AppColor.goldenBronze,
pricePoint: box.read(BoxName.countryCode) == 'Jordan' ? 22 : 400,
countPoint:
box.read(BoxName.countryCode) == 'Jordan' ? '23000' : '450',
),
PointsCaptain(
kolor: AppColor.gold,
pricePoint: box.read(BoxName.countryCode) == 'Jordan' ? 50 : 1000,
countPoint:
box.read(BoxName.countryCode) == 'Jordan' ? '55000' : '1100',
),
],
),
);
}
/// A row of action buttons for navigation.
Widget _buildActionButtons() {
return Row(
children: [
Expanded(
child: _DynamicButton(
title: 'Payment History'.tr,
onPressed: () async {
// Original logic is preserved.
await Get.put(DriverWalletHistoryController())
.getArchivePayment();
Get.to(() => const PaymentHistoryDriverPage(),
transition: Transition.size);
},
gradient: LinearGradient(
colors: [
kSecondaryTextColor,
kSecondaryTextColor.withOpacity(0.7)
],
),
),
),
const SizedBox(width: 16),
Expanded(
child: _DynamicButton(
title: 'Weekly Budget'.tr,
onPressed: () async {
// Original logic is preserved.
await Get.put(DriverWalletHistoryController())
.getWeekllyArchivePayment();
Get.to(() => const WeeklyPaymentPage(),
transition: Transition.size);
},
gradient: LinearGradient(
colors: [
kSecondaryTextColor,
kSecondaryTextColor.withOpacity(0.7)
],
),
),
),
],
);
}
/// A helper for creating section titles.
Widget _buildSectionTitle(String title) {
return Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: Text(
title,
style: const TextStyle(
color: kPrimaryTextColor,
fontSize: 22,
fontWeight: FontWeight.bold),
),
);
}
/// Handles the complex logic for buying points from budget, including authentication.
void _showBuyFromBudgetDialog(CaptainWalletController controller) {
Get.defaultDialog(
title: 'Pay from my budget'.tr,
titleStyle: const TextStyle(color: kPrimaryTextColor),
backgroundColor: kCardBackgroundColor,
content: Form(
key: controller.formKey,
child: MyTextForm(
// Assuming MyTextForm can be styled or is generic enough
controller: controller.amountFromBudgetController,
label: '${'You have in account'.tr} ${controller.totalAmountVisa}',
hint: '${'You have in account'.tr} ${controller.totalAmountVisa}',
type: TextInputType.number,
),
),
confirm: _DynamicButton(
title: 'Confirm & Pay'.tr,
onPressed: () async {
// All original logic, including biometric auth, is preserved.
bool isAvailable = await LocalAuthentication().isDeviceSupported();
if (isAvailable) {
bool didAuthenticate = await LocalAuthentication().authenticate(
localizedReason: 'Use Touch ID or Face ID to confirm payment',
options: const AuthenticationOptions(
biometricOnly: true, sensitiveTransaction: true));
if (didAuthenticate) {
if (double.parse(controller.amountFromBudgetController.text) <
double.parse(controller.totalAmountVisa)) {
await controller.payFromBudget();
} else {
Get.back();
mySnackeBarError('Your Budget less than needed'.tr);
}
}
}
},
),
cancel: TextButton(
onPressed: () => Get.back(),
child: Text('Cancel'.tr,
style: const TextStyle(color: kSecondaryTextColor)),
),
);
}
/// Handles the logic for transferring budget, including authentication.
void _handleTransferBudget(CaptainWalletController controller) async {
bool isAvailable = await LocalAuthentication().isDeviceSupported();
if (isAvailable) {
bool didAuthenticate = await LocalAuthentication().authenticate(
localizedReason: 'Use Touch ID or Face ID to confirm transfer',
options: const AuthenticationOptions(
biometricOnly: true, sensitiveTransaction: true));
if (didAuthenticate) {
if (double.parse(controller.totalAmountVisa) > 15) {
Get.to(() => const TransferBudgetPage());
} else {
mySnackeBarError(
"You don't have enough money in your Tripz wallet".tr);
}
}
}
}
}
// --- NEW DYNAMIC AND REUSABLE WIDGETS ---
/// A custom button with gradient and dynamic feel.
class _DynamicButton extends StatelessWidget {
final String title;
final VoidCallback onPressed;
final Gradient? gradient;
const _DynamicButton({
required this.title,
required this.onPressed,
this.gradient,
});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
gradient: gradient ??
const LinearGradient(
colors: [kPrimaryBlue, Color(0xFF1A91DA)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(
color: (gradient?.colors.first ?? kPrimaryBlue).withOpacity(0.3),
blurRadius: 10,
offset: const Offset(0, 5),
),
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: onPressed,
borderRadius: BorderRadius.circular(15),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Center(
child: Text(
title,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
),
),
),
);
}
}
/// A simple animation widget to fade in content dynamically.
class _AnimatedFadeIn extends StatefulWidget {
final int delay;
final Widget child;
const _AnimatedFadeIn({required this.delay, required this.child});
@override
State<_AnimatedFadeIn> createState() => _AnimatedFadeInState();
}
class _AnimatedFadeInState extends State<_AnimatedFadeIn> {
bool _isVisible = false;
@override
void initState() {
super.initState();
Future.delayed(Duration(milliseconds: widget.delay), () {
if (mounted) {
setState(() => _isVisible = true);
}
});
}
@override
Widget build(BuildContext context) {
return AnimatedOpacity(
duration: const Duration(milliseconds: 500),
opacity: _isVisible ? 1.0 : 0.0,
child: AnimatedContainer(
duration: const Duration(milliseconds: 500),
transform: Matrix4.translationValues(0, _isVisible ? 0 : 20, 0),
child: widget.child,
),
);
}
}
// --- THE FOLLOWING ORIGINAL CODE IS KEPT FOR CONTEXT AND UNCHANGED AS REQUESTED ---
// I did not include the original helper functions like _buildBuyPointsButton because their
// logic has been integrated into the new, redesigned widgets above (e.g., inside _buildBudgetSection).
// The functions below are external to the main widget and are kept as is.
Future<dynamic> addBankCodeEgypt(
CaptainWalletController captainWalletController) {
return Get.defaultDialog(
title: "Insert Account Bank".tr,
content: Form(
key: captainWalletController.formKeyAccount,
child: Column(
children: [
Text("Insert Card Bank Details to Receive Your Visa Money Weekly"
.tr),
MyTextForm(
controller: captainWalletController.cardBank,
label: "Insert card number".tr,
hint: '4123 4567 8910 1235',
type: TextInputType.number),
const SizedBox(
height: 10,
),
BankDropdown()
],
)),
confirm: MyElevatedButton(
title: 'Ok'.tr,
onPressed: () async {
if (captainWalletController.formKeyAccount.currentState!
.validate()) {
Get.back();
var res =
await CRUD().post(link: AppLink.updateAccountBank, payload: {
"bankCode": Get.find<BankController>().selectedBank.toString(),
"accountBank": captainWalletController.cardBank.text.toString(),
"id": box.read(BoxName.driverID).toString()
});
print('res: ${res}');
if (res != 'failure') {
mySnackbarSuccess('bank account added succesfly'.tr);
}
}
}));
}
class MyDropDown1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
Get.put(PaymobPayout());
return GetBuilder<PaymobPayout>(builder: (controller) {
return DropdownButton<String>(
value: controller.dropdownValue,
icon: const Icon(Icons.arrow_drop_down),
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String? newValue) {
controller.dropdownValue = newValue!;
controller.update();
},
items: <String>['etisalat', 'aman', 'orange', 'vodafone']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
});
}
}