import 'package:wanisdriver/utils/colors.dart'; import 'package:wanisdriver/widgets/custom_text.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/physics.dart'; // Import for SpringSimulation class CompleteWidget extends StatefulWidget { final String duration; final String clientname; final double price; final VoidCallback onchangestate; final bool canChangeState; final int remainingSeconds; final bool havestops; final bool isChangeStatusLoading; const CompleteWidget({ Key? key, required this.havestops, required this.duration, required this.clientname, required this.price, required this.onchangestate, required this.canChangeState, required this.isChangeStatusLoading, required this.remainingSeconds, }) : super(key: key); @override State createState() => _CompleteWidgetState(); } class _CompleteWidgetState extends State with SingleTickerProviderStateMixin { double _dragPosition = 0.0; bool _isSliding = false; late AnimationController _animationController; late Animation _animation; @override void initState() { super.initState(); _animationController = AnimationController( vsync: this, duration: const Duration(milliseconds: 300), ); _animationController.addListener(() { setState(() { _dragPosition = _animation.value; }); }); } @override void didUpdateWidget(covariant CompleteWidget oldWidget) { super.didUpdateWidget(oldWidget); // If the loading state changes from true to false, // it means the operation has completed, so we reset the slider. if (oldWidget.isChangeStatusLoading && !widget.isChangeStatusLoading) { _resetSlider(); } // Also, if canChangeState becomes false while a drag is active, reset. // This handles scenarios where the timer runs out during a drag. if (oldWidget.canChangeState && !widget.canChangeState && _isSliding) { _resetSlider(); } } @override void dispose() { _animationController.dispose(); super.dispose(); } void _resetSlider() { // This uses a Tween animation to bring the slider back to the start. _animation = Tween(begin: _dragPosition, end: 0.0).animate( CurvedAnimation( parent: _animationController, curve: Curves.easeOutCubic, // Provides a smooth, decelerating return ), ); _animationController.forward(from: 0.0); _isSliding = false; // Ensure sliding state is false after reset } void _handleSlideEnd() { final double successThreshold = 0.8; // 80% of the way across if (_dragPosition >= successThreshold) { if (widget.canChangeState && !widget.isChangeStatusLoading) { widget.onchangestate(); // Call the action defined by the parent } else { // Cannot change state (e.g., due to timer, or already finished) or already loading, // so reset the slider immediately. _resetSlider(); } } else { // Not past threshold, reset immediately. _resetSlider(); } } @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Row( children: [ Text( widget.duration, style: TextStyle( fontFamily: 'Poppins', fontSize: 18, fontWeight: FontWeight.bold, color: Theme.of(context).brightness == Brightness.dark ? AppColors.white : AppColors.black, ), ), const SizedBox(width: 5), const Icon( Icons.timer, size: 20, // color: Colors.black, ), ], ), Row( children: [ Text( "${widget.price.toStringAsFixed(2)} TND", style: TextStyle( fontFamily: 'Poppins', fontSize: 18, fontWeight: FontWeight.bold, color: Theme.of(context).brightness == Brightness.dark ? AppColors.white : AppColors.black, ), ), const SizedBox(width: 5), const Icon( Icons.monetization_on, size: 20, // color: Colors.black, ), ], ), ], ), const SizedBox(height: 8.0), const Txt( text: "Don't forget to pick money from the client", style: TextStyle( fontFamily: 'Poppins', fontSize: 16, fontWeight: FontWeight.normal, color: Colors.grey, ), textAlign: TextAlign.center, ), const SizedBox(height: 16.0), Padding( padding: const EdgeInsets.only(left: 15, right: 15), child: widget.havestops ? _buildNextStopSlider() // Now uses a slider for Next Stop : _buildDropOffSlider(), ), ], ); } Widget _buildNextStopSlider() { return LayoutBuilder( builder: (context, constraints) { final double containerWidth = constraints.maxWidth; final double sliderButtonWidth = 50.0; String sliderText; if (widget.isChangeStatusLoading) { sliderText = "Loading..."; } else if (_dragPosition < 0.8) { sliderText = "SWIPE FOR NEXT STOP"; } else { sliderText = "RELEASE FOR NEXT STOP"; } return IgnorePointer( ignoring: widget.isChangeStatusLoading, // Ignore input when loading child: GestureDetector( onHorizontalDragStart: (details) { if (widget.canChangeState && !widget.isChangeStatusLoading) { _animationController.stop(); setState(() { _isSliding = true; }); } }, onHorizontalDragUpdate: (details) { if (widget.canChangeState && _isSliding && !widget.isChangeStatusLoading) { setState(() { // Increased sensitivity by dividing by a smaller value (0.6) // Adjust 0.6 to make it more/less sensitive (smaller = more sensitive) _dragPosition += details.primaryDelta! / (containerWidth * 0.6); _dragPosition = _dragPosition.clamp(0.0, 1.0); // Keep between 0 and 1 }); } }, onHorizontalDragEnd: (details) { if (_isSliding && widget.canChangeState) { _handleSlideEnd(); } }, child: Container( width: double.infinity, height: 60, padding: const EdgeInsets.symmetric(horizontal: 5), decoration: BoxDecoration( color: !widget.canChangeState ? Colors.grey : AppColors.secondaryColor.withOpacity(.2), border: Border.all( color: !widget.canChangeState ? Colors.grey : AppColors.secondaryColor, width: 1.3, ), borderRadius: BorderRadius.circular(30.0), ), child: Stack( alignment: Alignment.centerLeft, children: [ Align( alignment: Alignment.center, child: Row( mainAxisSize: MainAxisSize.min, children: [ Txt( text: sliderText, style: TextStyle( color: !widget.canChangeState ? Colors.white : AppColors.secondaryColor, fontSize: 16, fontFamily: 'Poppins', fontWeight: FontWeight.w400, ), ), // Only show timer if not loading and not yet past threshold if (widget.remainingSeconds != 0 && !widget.isChangeStatusLoading && _dragPosition < 0.8) Txt( text: " (${widget.remainingSeconds.toString()}s)", style: TextStyle( color: !widget.canChangeState ? Colors.white : AppColors.secondaryColor, fontSize: 16, fontFamily: 'Poppins', fontWeight: FontWeight.w400, ), ), ], ), ), Positioned( left: _dragPosition * (containerWidth - sliderButtonWidth - 10), child: Container( width: sliderButtonWidth, height: sliderButtonWidth, decoration: BoxDecoration( color: !widget.canChangeState ? Colors.grey : AppColors.secondaryColor, borderRadius: BorderRadius.circular(30.0), ), child: widget.isChangeStatusLoading && _dragPosition >= 0.8 ? const Center( child: SizedBox( width: 25, height: 25, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2, ), ), ) : const Icon( Icons.arrow_forward, color: Colors.white, size: 25, ), ), ), ], ), ), ), ); }, ); } Widget _buildDropOffSlider() { return LayoutBuilder( builder: (context, constraints) { final double containerWidth = constraints.maxWidth; final double sliderButtonWidth = 50.0; // Determine the text based on drag position and loading state String sliderText; if (widget.isChangeStatusLoading) { sliderText = "Dropping Off..."; } else if (_dragPosition < 0.8) { sliderText = "SWIPE TO DROP OFF"; } else { sliderText = "RELEASE TO DROP OFF"; } return IgnorePointer( ignoring: widget.isChangeStatusLoading, // Ignore input when loading child: GestureDetector( onHorizontalDragStart: (details) { if (widget.canChangeState && !widget.isChangeStatusLoading) { _animationController.stop(); setState(() { _isSliding = true; }); } }, onHorizontalDragUpdate: (details) { if (widget.canChangeState && _isSliding && !widget.isChangeStatusLoading) { setState(() { // Increased sensitivity by dividing by a smaller value (0.6) // Adjust 0.6 to make it more/less sensitive (smaller = more sensitive) _dragPosition += details.primaryDelta! / (containerWidth * 0.6); _dragPosition = _dragPosition.clamp(0.0, 1.0); // Keep between 0 and 1 }); } }, onHorizontalDragEnd: (details) { if (_isSliding && widget.canChangeState) { _handleSlideEnd(); } }, child: Container( width: double.infinity, height: 60, padding: const EdgeInsets.symmetric(horizontal: 5), decoration: BoxDecoration( color: !widget.canChangeState ? Colors.grey : AppColors.secondaryColor.withOpacity(.2), border: Border.all( color: !widget.canChangeState ? Colors.grey : AppColors.secondaryColor, width: 1.3, ), borderRadius: BorderRadius.circular(30.0), ), child: Stack( alignment: Alignment.centerLeft, children: [ Align( alignment: Alignment.center, child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ Txt( text: sliderText, style: TextStyle( color: !widget.canChangeState ? Colors.white : AppColors.secondaryColor, fontSize: 16, fontFamily: 'Poppins', fontWeight: FontWeight.w400, ), ), // Only show timer if not loading and not yet past threshold if (widget.remainingSeconds != 0 && !widget.isChangeStatusLoading && _dragPosition < 0.8) Txt( text: " (${widget.remainingSeconds.toString()}s)", style: TextStyle( color: !widget.canChangeState ? Colors.white : AppColors.secondaryColor, fontSize: 16, fontFamily: 'Poppins', fontWeight: FontWeight.w400, ), ), ], ), ), Positioned( left: _dragPosition * (containerWidth - sliderButtonWidth - 10), child: Container( width: sliderButtonWidth, height: sliderButtonWidth, decoration: BoxDecoration( color: !widget.canChangeState ? Colors.grey : AppColors.secondaryColor, borderRadius: BorderRadius.circular(30.0), ), child: widget.isChangeStatusLoading && _dragPosition >= 0.8 ? const Center( child: SizedBox( width: 25, height: 25, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2, ), ), ) : const Icon( Icons.arrow_forward, color: Colors.white, size: 25, ), ), ), ], ), ), ), ); }, ); } }