first commit

This commit is contained in:
Anbarasu
2026-05-26 18:01:57 +05:30
commit 6d59c8daf6
297 changed files with 35238 additions and 0 deletions

1870
lib/view/cart/cart_view.dart Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,282 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../constants/font_constants.dart';
import '../../controllers/cart_controller/cart.dart';
import '../../controllers/order_controller/create_order_controller.dart';
import '../../modules/orders/create_order.dart';
import '../../widgets/text_widget.dart';
import '../orders/order_succes.dart';
class OrderCountdownPage extends StatefulWidget {
final CreateOrder order;
final OrderController orderCtrl;
final CartController cartCtrl;
final String customerName;
const OrderCountdownPage({
super.key,
required this.order,
required this.orderCtrl,
required this.cartCtrl,
required this.customerName,
});
@override
State<OrderCountdownPage> createState() => _OrderCountdownPageState();
}
class _OrderCountdownPageState extends State<OrderCountdownPage>
with SingleTickerProviderStateMixin {
static const int _totalSeconds = 10;
int _remainingSeconds = _totalSeconds;
Timer? _timer;
late AnimationController _animController;
bool _cancelled = false;
@override
void initState() {
super.initState();
_animController = AnimationController(
vsync: this,
duration: const Duration(seconds: _totalSeconds),
)..forward();
_startTimer();
}
void _startTimer() {
_timer?.cancel();
_timer = null;
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
// ✅ Stop immediately if cancelled or unmounted
if (_cancelled || !mounted) {
timer.cancel();
_timer = null;
return;
}
// ✅ Check zero BEFORE setState
if (_remainingSeconds <= 0) {
timer.cancel();
_timer = null;
_placeOrder();
return;
}
setState(() {
_remainingSeconds--;
});
});
}
@override
void dispose() {
_cancelled = true; // ✅ prevent any late callbacks
_timer?.cancel();
_timer = null;
_animController.dispose();
super.dispose();
}
String get _formattedTime {
final minutes = _remainingSeconds ~/ 60;
final seconds = _remainingSeconds % 60;
return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
}
Future<void> _placeOrder() async {
if (_cancelled || !mounted) return;
await widget.orderCtrl.createOrder(CreateOrderRequest(orders: widget.order));
if (!mounted || _cancelled) return;
if (!widget.orderCtrl.isLoading.value) {
Get.offAll(() => OrderSuccessView());
widget.cartCtrl.clearCart();
await widget.cartCtrl.notifyAdmin(
title: 'Nearle Deals - New Order',
body: 'A new order has been placed successfully by ${widget.customerName}!',
);
}
}
void _cancelOrder() {
// ✅ Stop timer immediately before showing dialog
_timer?.cancel();
_timer = null;
_animController.stop();
showDialog(
context: context,
barrierDismissible: false,
builder: (dialogCtx) {
return AlertDialog(
title: const Text('Cancel Order?'),
content: const Text('Are you sure you want to cancel this order?'),
actions: [
// No — resume
TextButton(
onPressed: () {
Navigator.of(dialogCtx).pop();
if (!_cancelled) {
_startTimer();
_animController.forward(from: _animController.value);
}
},
child: const Text('No'),
),
// Yes — go back
TextButton(
onPressed: () {
_cancelled = true; // ✅ set first
_timer?.cancel();
_timer = null;
_animController.stop();
// ✅ close dialog then navigate
Navigator.of(dialogCtx).pop();
Navigator.of(context).pop(); // ✅ use Navigator, not Get.back()
},
child: const Text(
'Yes, Cancel',
style: TextStyle(color: Colors.red),
),
),
],
);
},
);
}
@override
Widget build(BuildContext context) {
final progress = _remainingSeconds / _totalSeconds;
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 32),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Spacer(),
// Icon
Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.green.withOpacity(0.1),
shape: BoxShape.circle,
),
child: const Icon(
Icons.access_time_filled,
size: 64,
color: Colors.green,
),
),
const SizedBox(height: 32),
// Title
ReusableTextWidget(
text: 'Order Pending',
color: Colors.black.withOpacity(0.8),
fontFamily: FontConstants.fontFamily,
fontSize: 22,
fontWeight: FontWeight.bold,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
const SizedBox(height: 12),
// Subtitle
ReusableTextWidget(
text: 'Your order will be placed automatically.\nYou can cancel within the time below.',
color: Colors.black.withOpacity(0.5),
fontFamily: FontConstants.fontFamily,
fontSize: 10,
fontWeight: FontWeight.normal,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 3,
),
const SizedBox(height: 48),
// Progress + Timer
Stack(
alignment: Alignment.center,
children: [
SizedBox(
width: 160,
height: 160,
child: AnimatedBuilder(
animation: _animController,
builder: (_, __) => CircularProgressIndicator(
value: progress,
strokeWidth: 10,
backgroundColor: Colors.grey.shade200,
valueColor: const AlwaysStoppedAnimation<Color>(Colors.green),
),
),
),
ReusableTextWidget(
text: _formattedTime,
color: Colors.black.withOpacity(0.8),
fontFamily: FontConstants.fontFamily,
fontSize: 28,
fontWeight: FontWeight.bold,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
],
),
const Spacer(),
// Cancel Button
SizedBox(
width: double.infinity,
child: OutlinedButton(
onPressed: _cancelOrder,
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Colors.red),
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: ReusableTextWidget(
text: 'Cancel Order',
color: Colors.red,
fontFamily: FontConstants.fontFamily,
fontSize: 10,
fontWeight: FontWeight.bold,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
),
const SizedBox(height: 16),
],
),
),
),
);
}
}