Update: 2026-06-26 17:29:23

This commit is contained in:
Hamza-Ayed
2026-06-26 17:29:23 +03:00
parent a323da29aa
commit 9ded734e38
139 changed files with 1815 additions and 2676 deletions

View File

@@ -830,7 +830,7 @@ class ShareAppPage extends StatelessWidget {
rewardsController.linkInviteCode(manualCodeController.text.trim());
manualCodeController.clear();
} else {
mySnackeBarError('Please enter a referral code'.tr);
mySnackbarError('Please enter a referral code'.tr);
}
},
child: Text('Link'.tr, style: const TextStyle(color: Colors.white, fontSize: 14)),

View File

@@ -56,8 +56,8 @@ class GoogleMapPassengerWidget extends StatelessWidget {
Log.print('⚠️ onCameraIdle: mapController is NULL');
}
},
markers: controller.markers,
polylines: controller.polyLines,
markers: Set.of(controller.markers),
polylines: Set.of(controller.polyLines),
polygons: controller.polygons,
circles: controller.circles,
initialCameraPosition: CameraPosition(

View File

@@ -387,10 +387,10 @@ class MapMenuWidget extends StatelessWidget {
if (await canLaunchUrl(url)) {
await launchUrl(url);
} else {
mySnackeBarError('Could not launch driver app store.');
mySnackbarError('Could not launch driver app store.');
}
} catch (e) {
mySnackeBarError('Could not open the link.');
mySnackbarError('Could not open the link.');
}
}
}

View File

@@ -64,7 +64,7 @@ class _PaymentScreenCliqState extends State<PaymentScreenCliq> with SingleTicker
Future<void> _submitProof() async {
if (_proofController.text.trim().isEmpty) {
mySnackeBarError('Please paste the transfer message'.tr);
mySnackbarError('Please paste the transfer message'.tr);
return;
}
setState(() => _status = 'verifying');

View File

@@ -65,7 +65,7 @@ class _PaymentScreenMtnState extends State<PaymentScreenMtn> with SingleTickerPr
Future<void> _submitProof() async {
if (_proofController.text.trim().isEmpty) {
mySnackeBarError('Please paste the transfer message'.tr);
mySnackbarError('Please paste the transfer message'.tr);
return;
}
setState(() => _status = 'verifying');

View File

@@ -100,7 +100,7 @@ class _PassengerProfilePageState extends State<PassengerProfilePage> {
}
void _showUploadError() {
mySnackeBarError('Failed to upload photo'.tr);
mySnackbarError('Failed to upload photo'.tr);
}
Future<ImageSource?> _showImageSourceSheet() async {

View File

@@ -2,11 +2,7 @@ import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import '../../constant/colors.dart';
// ─────────────────────────────────────────────────────────────────────────────
// Snackbar variant definition
// ─────────────────────────────────────────────────────────────────────────────
enum _SnackVariant { success, error, info, warning }
extension _VariantProps on _SnackVariant {
@@ -37,20 +33,8 @@ extension _VariantProps on _SnackVariant {
_SnackVariant.info => 'Info',
_SnackVariant.warning => 'Warning',
};
HapticFeedbackType get haptic => switch (this) {
_SnackVariant.error => HapticFeedbackType.medium,
_SnackVariant.warning => HapticFeedbackType.medium,
_SnackVariant.success => HapticFeedbackType.light,
_SnackVariant.info => HapticFeedbackType.selection,
};
}
enum HapticFeedbackType { light, medium, selection }
// ─────────────────────────────────────────────────────────────────────────────
// Core snackbar widget
// ─────────────────────────────────────────────────────────────────────────────
class _SnackContent extends StatefulWidget {
final String message;
final _SnackVariant variant;
@@ -74,21 +58,17 @@ class _SnackContentState extends State<_SnackContent>
void initState() {
super.initState();
_ctrl = AnimationController(vsync: this, duration: _displayDuration);
_scaleCtrl = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 500),
);
_scaleAnim = CurvedAnimation(
parent: _scaleCtrl,
curve: Curves.elasticOut,
);
_progressAnim = Tween<double>(begin: 1.0, end: 0.0).animate(
CurvedAnimation(parent: _ctrl, curve: Curves.linear),
);
_scaleCtrl.forward();
_ctrl.forward();
}
@@ -111,16 +91,16 @@ class _SnackContentState extends State<_SnackContent>
decoration: BoxDecoration(
color: surface,
borderRadius: BorderRadius.circular(18),
border: Border.all(color: accent.withOpacity(0.18), width: 1.2),
border: Border.all(color: accent.withAlpha(46), width: 1.2),
boxShadow: [
BoxShadow(
color: accent.withOpacity(0.12),
color: accent.withAlpha(31),
blurRadius: 20,
spreadRadius: -2,
offset: const Offset(0, 6),
),
BoxShadow(
color: Colors.black.withOpacity(0.06),
color: Colors.black.withAlpha(15),
blurRadius: 10,
offset: const Offset(0, 2),
),
@@ -131,28 +111,24 @@ class _SnackContentState extends State<_SnackContent>
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// ── Main row ──────────────────────────────────────────────────
Padding(
padding: const EdgeInsets.fromLTRB(14, 14, 10, 12),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Animated icon badge
ScaleTransition(
scale: _scaleAnim,
child: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: accent.withOpacity(0.12),
color: accent.withAlpha(31),
shape: BoxShape.circle,
),
child: Icon(v.icon, color: accent, size: 22),
),
),
const SizedBox(width: 12),
// Text content
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -170,8 +146,8 @@ class _SnackContentState extends State<_SnackContent>
const SizedBox(height: 3),
Text(
widget.message,
style: TextStyle(
color: Colors.grey[800],
style: const TextStyle(
color: Color(0xFF424242),
fontSize: 13.5,
height: 1.4,
fontWeight: FontWeight.w400,
@@ -182,19 +158,17 @@ class _SnackContentState extends State<_SnackContent>
],
),
),
// Close button
GestureDetector(
onTap: () {
HapticFeedback.lightImpact();
Get.closeCurrentSnackbar();
_closeSnackbar(context);
},
child: Container(
width: 30,
height: 30,
margin: const EdgeInsets.only(left: 6),
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.1),
color: Colors.grey.withAlpha(25),
shape: BoxShape.circle,
),
child: Icon(
@@ -207,24 +181,17 @@ class _SnackContentState extends State<_SnackContent>
],
),
),
// ── Thin progress strip ───────────────────────────────────────
AnimatedBuilder(
animation: _progressAnim,
builder: (_, __) => Stack(
children: [
// Track
Container(
height: 3,
color: accent.withOpacity(0.08),
),
// Fill
Container(height: 3, color: accent.withAlpha(20)),
FractionallySizedBox(
widthFactor: _progressAnim.value,
child: Container(
height: 3,
decoration: BoxDecoration(
color: accent.withOpacity(0.45),
color: accent.withAlpha(115),
borderRadius: const BorderRadius.only(
topRight: Radius.circular(4),
bottomRight: Radius.circular(4),
@@ -240,84 +207,57 @@ class _SnackContentState extends State<_SnackContent>
),
);
}
void _closeSnackbar(BuildContext context) {
ScaffoldMessenger.maybeOf(context)?.hideCurrentSnackBar();
}
}
// ─────────────────────────────────────────────────────────────────────────────
// Internal dispatcher — single source of truth
// ─────────────────────────────────────────────────────────────────────────────
int _retryCount = 0;
SnackbarController? _show(_SnackVariant variant, String message) {
// Prevent crash if Navigator or Overlay context is not yet initialized at early startup
if (Get.context == null || Get.overlayContext == null || Get.key.currentState?.overlay == null) {
debugPrint("⚠️ Cannot show snackbar: Overlay/Navigator is not ready yet. Message: $message");
// Retry up to 3 times after the next frame (handles race condition during route transitions)
void _show(_SnackVariant variant, String message) {
if (Get.context == null) {
if (_retryCount < 3) {
_retryCount++;
WidgetsBinding.instance.addPostFrameCallback((_) => _show(variant, message));
}
return null;
return;
}
_retryCount = 0;
try {
// Removed Get.closeCurrentSnackbar() because it causes async LateInitializationError in GetX at early startup
final context = Get.context;
if (context == null) return;
// We use ScaffoldMessenger instead of Get.snackbar because GetX's snackbar
// throws synchronous "No Overlay widget found" FlutterErrors when the
// Overlay isn't perfectly mounted, crashing the app globally.
final context = Get.context;
if (context == null) return null;
final messenger = ScaffoldMessenger.maybeOf(context);
if (messenger == null) return;
final messenger = ScaffoldMessenger.maybeOf(context);
if (messenger == null) {
debugPrint("⚠️ Cannot show snackbar: ScaffoldMessenger not found. Message: $message");
return null;
}
messenger.clearSnackBars();
messenger.clearSnackBars(); // Prevent stacking
switch (variant.haptic) {
case HapticFeedbackType.light:
HapticFeedback.lightImpact();
case HapticFeedbackType.medium:
HapticFeedback.mediumImpact();
case HapticFeedbackType.selection:
HapticFeedback.selectionClick();
}
messenger.showSnackBar(
SnackBar(
content: _SnackContent(message: message, variant: variant),
backgroundColor: Colors.transparent,
elevation: 0,
margin: EdgeInsets.zero,
padding: EdgeInsets.zero,
behavior: SnackBarBehavior.floating,
duration: const Duration(seconds: 4),
dismissDirection: DismissDirection.up,
),
);
return null; // We return null since we no longer use Get.snackbar's controller
} catch (e) {
debugPrint("⚠️ Exception caught showing snackbar: $e");
return null;
switch (variant) {
case _SnackVariant.error:
case _SnackVariant.warning:
HapticFeedback.mediumImpact();
case _SnackVariant.success:
HapticFeedback.lightImpact();
case _SnackVariant.info:
HapticFeedback.selectionClick();
}
messenger.showSnackBar(
SnackBar(
content: _SnackContent(message: message, variant: variant),
backgroundColor: Colors.transparent,
elevation: 0,
margin: EdgeInsets.zero,
padding: EdgeInsets.zero,
behavior: SnackBarBehavior.floating,
duration: const Duration(seconds: 4),
dismissDirection: DismissDirection.up,
),
);
}
// ─────────────────────────────────────────────────────────────────────────────
// Public API — drop-in replacements for the old functions
// ─────────────────────────────────────────────────────────────────────────────
SnackbarController? mySnackbarSuccess(String message) =>
_show(_SnackVariant.success, message);
SnackbarController? mySnackeBarError(String message) =>
_show(_SnackVariant.error, message);
SnackbarController? mySnackbarInfo(String message) =>
_show(_SnackVariant.info, message);
SnackbarController? mySnackbarWarning(String message) =>
_show(_SnackVariant.warning, message);
void mySnackbarSuccess(String message) => _show(_SnackVariant.success, message);
void mySnackbarError(String message) => _show(_SnackVariant.error, message);
void mySnackbarInfo(String message) => _show(_SnackVariant.info, message);
void mySnackbarWarning(String message) => _show(_SnackVariant.warning, message);