378 lines
11 KiB
Dart
378 lines
11 KiB
Dart
import 'dart:convert';
|
|
import 'dart:ui';
|
|
import 'dart:math' as math;
|
|
|
|
import 'package:bookmywages/consts_widgets/app_assets.dart';
|
|
import 'package:bookmywages/routers/consts_router.dart';
|
|
import 'package:bookmywages/viewmodel/api_controller.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart'; // Add this import
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:http/http.dart' as http;
|
|
|
|
class MainController extends ConsumerStatefulWidget {
|
|
final Widget child;
|
|
final int? initialBottomIndex;
|
|
|
|
const MainController({
|
|
super.key,
|
|
required this.child,
|
|
this.initialBottomIndex,
|
|
});
|
|
|
|
@override
|
|
ConsumerState<MainController> createState() => _MainControllerState();
|
|
}
|
|
|
|
class _MainControllerState extends ConsumerState<MainController>
|
|
with SingleTickerProviderStateMixin {
|
|
int _selectedIndex = 0;
|
|
bool _showMenu = false;
|
|
late AnimationController _animationController;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
// Set bottom navigation index
|
|
if (widget.initialBottomIndex != null) {
|
|
_selectedIndex = widget.initialBottomIndex!;
|
|
} else {
|
|
// Auto-detect based on current route
|
|
final currentRoute = Get.currentRoute;
|
|
if (currentRoute == RouterConts.homescreen) {
|
|
_selectedIndex = 0;
|
|
} else if (currentRoute == RouterConts.packageList) {
|
|
_selectedIndex = 1;
|
|
} else if (currentRoute == RouterConts.categorypage) {
|
|
_selectedIndex = 2;
|
|
} else if (currentRoute == RouterConts.history) {
|
|
_selectedIndex = 3;
|
|
} else if (currentRoute == RouterConts.profilemainscreen ||
|
|
currentRoute == RouterConts.editprofile ||
|
|
currentRoute == RouterConts.changepassword) {
|
|
_selectedIndex = 0; // No tab selected
|
|
}
|
|
}
|
|
|
|
_animationController = AnimationController(
|
|
vsync: this,
|
|
duration: const Duration(milliseconds: 300),
|
|
);
|
|
}
|
|
|
|
void _toggleMenu() {
|
|
setState(() {
|
|
_showMenu = !_showMenu;
|
|
_showMenu
|
|
? _animationController.forward()
|
|
: _animationController.reverse();
|
|
});
|
|
}
|
|
|
|
// 🎯 NEW: Handle back button press
|
|
Future<bool> _onWillPop() async {
|
|
final currentRoute = Get.currentRoute;
|
|
|
|
// If menu is open, close it first
|
|
if (_showMenu) {
|
|
_toggleMenu();
|
|
return false; // Don't exit
|
|
}
|
|
|
|
// Handle back navigation based on current route
|
|
switch (currentRoute) {
|
|
case RouterConts.profilemainscreen:
|
|
case RouterConts.editprofile:
|
|
case RouterConts.changepassword:
|
|
// Navigate back to home when coming from profile screens
|
|
Get.offAllNamed(RouterConts.homescreen);
|
|
return false; // Don't use default back behavior
|
|
|
|
case RouterConts.packageList:
|
|
case RouterConts.categorypage:
|
|
case RouterConts.history:
|
|
// For other main tabs, go to home first
|
|
Get.offAllNamed(RouterConts.homescreen);
|
|
return false;
|
|
|
|
case RouterConts.homescreen:
|
|
// If already on home, allow default back behavior (exit app)
|
|
return true;
|
|
|
|
default:
|
|
// For any other screens, try to go back to home
|
|
try {
|
|
Get.back();
|
|
} catch (e) {
|
|
// If can't go back, navigate to home
|
|
Get.offAllNamed(RouterConts.homescreen);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void _onItemTapped(int index, BuildContext context) {
|
|
if (_selectedIndex == index) return;
|
|
|
|
switch (index) {
|
|
case 0:
|
|
Get.offAllNamed(RouterConts.homescreen);
|
|
break;
|
|
case 1:
|
|
Get.offAllNamed(RouterConts.packageList);
|
|
break;
|
|
case 2:
|
|
Get.offAllNamed(RouterConts.categorypage);
|
|
break;
|
|
case 3:
|
|
// Navigate to history with default tab (Service Booking)
|
|
Get.offAllNamed(
|
|
RouterConts.history,
|
|
arguments: {
|
|
'historyTab': 0, // Default to first tab
|
|
},
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void changeSelectedIndex(int index) {
|
|
setState(() {
|
|
_selectedIndex = index;
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final isKeyboardOpen = MediaQuery.of(context).viewInsets.bottom > 0;
|
|
|
|
// 🎯 Wrap with WillPopScope to handle back button
|
|
return WillPopScope(
|
|
onWillPop: _onWillPop,
|
|
child: Scaffold(
|
|
resizeToAvoidBottomInset: false,
|
|
body: Stack(
|
|
children: [
|
|
InheritedIndexController(
|
|
changeIndex: changeSelectedIndex,
|
|
child: widget.child,
|
|
),
|
|
if (_showMenu && !isKeyboardOpen)
|
|
AnimatedOpacity(
|
|
opacity: 1.0,
|
|
duration: const Duration(milliseconds: 300),
|
|
child: GestureDetector(
|
|
onTap: _toggleMenu,
|
|
child: Container(
|
|
alignment: Alignment.center,
|
|
child: BackdropFilter(
|
|
filter: ImageFilter.blur(sigmaX: 2.0, sigmaY: 2.0),
|
|
child: Container(
|
|
color: Colors.black.withOpacity(0.2),
|
|
width: double.infinity,
|
|
height: double.infinity,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
if (_showMenu && !isKeyboardOpen)
|
|
Positioned(
|
|
bottom: 60,
|
|
left: 0,
|
|
right: 0,
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 110),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
_buildMenuOption(
|
|
icon: Icons.store,
|
|
label: 'Vendor',
|
|
color: const Color(0xFF0066FF),
|
|
onTap: () async {
|
|
_toggleMenu();
|
|
try {
|
|
await ref.read(getvendorIdProvider.future);
|
|
Get.toNamed(RouterConts.vendorwelcome);
|
|
} catch (e) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(
|
|
content: Text('Failed to fetch Vendor ID'),
|
|
),
|
|
);
|
|
}
|
|
},
|
|
),
|
|
_buildMenuOption(
|
|
icon: Icons.person,
|
|
label: 'Profile',
|
|
color: const Color(0xFF0066FF),
|
|
onTap: () {
|
|
_toggleMenu();
|
|
Get.toNamed(RouterConts.profilemainscreen);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
floatingActionButton: isKeyboardOpen
|
|
? null
|
|
: FloatingActionButton(
|
|
heroTag: "main_controller_fab",
|
|
onPressed: _toggleMenu,
|
|
shape: const CircleBorder(),
|
|
backgroundColor: const Color(0xFF0066FF),
|
|
child: AnimatedBuilder(
|
|
animation: _animationController,
|
|
builder: (context, child) {
|
|
return Transform.rotate(
|
|
angle: _animationController.value * 0.75 * math.pi,
|
|
child: Icon(
|
|
_animationController.value > 0.5
|
|
? Icons.close
|
|
: Icons.add,
|
|
color: Colors.white,
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
|
|
bottomNavigationBar: BottomAppBar(
|
|
notchMargin: 8,
|
|
color: Colors.white,
|
|
elevation: 8,
|
|
child: SizedBox(
|
|
height: 60,
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
|
children: [
|
|
_bottomNavItem(0, AppAssets.home),
|
|
_bottomNavItem(1, AppAssets.package),
|
|
const Expanded(child: SizedBox()),
|
|
_bottomNavItem(2, AppAssets.categories),
|
|
_bottomNavItem(3, AppAssets.history),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _bottomNavItem(int index, String assetPath) {
|
|
return Expanded(
|
|
child: InkWell(
|
|
onTap: () => _onItemTapped(index, context),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Image.asset(
|
|
assetPath,
|
|
color: _selectedIndex == index
|
|
? const Color(0xFF0066FF)
|
|
: Colors.grey,
|
|
width: 26,
|
|
height: 29,
|
|
),
|
|
const SizedBox(height: 4),
|
|
Container(
|
|
height: 3,
|
|
width: 30,
|
|
decoration: BoxDecoration(
|
|
color: _selectedIndex == index
|
|
? const Color(0xFF0066FF)
|
|
: Colors.transparent,
|
|
borderRadius: BorderRadius.circular(2),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildMenuOption({
|
|
required IconData icon,
|
|
required String label,
|
|
required Color color,
|
|
required VoidCallback onTap,
|
|
}) {
|
|
return ScaleTransition(
|
|
scale: CurvedAnimation(
|
|
parent: _animationController,
|
|
curve: Curves.easeOut,
|
|
),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Container(
|
|
width: 50,
|
|
height: 50,
|
|
decoration: BoxDecoration(
|
|
color: color,
|
|
shape: BoxShape.circle,
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.2),
|
|
blurRadius: 3,
|
|
spreadRadius: 1,
|
|
offset: const Offset(0, 1),
|
|
),
|
|
],
|
|
),
|
|
child: IconButton(
|
|
icon: Icon(icon, color: Colors.white),
|
|
onPressed: onTap,
|
|
padding: EdgeInsets.zero,
|
|
),
|
|
),
|
|
const SizedBox(height: 5),
|
|
Text(
|
|
label,
|
|
style: const TextStyle(
|
|
color: Color(0xFF0066FF),
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 12,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_animationController.dispose();
|
|
super.dispose();
|
|
}
|
|
}
|
|
|
|
class InheritedIndexController extends InheritedWidget {
|
|
final void Function(int) changeIndex;
|
|
|
|
const InheritedIndexController({
|
|
super.key,
|
|
required this.changeIndex,
|
|
required super.child,
|
|
});
|
|
|
|
static InheritedIndexController? of(BuildContext context) {
|
|
return context
|
|
.dependOnInheritedWidgetOfExactType<InheritedIndexController>();
|
|
}
|
|
|
|
@override
|
|
bool updateShouldNotify(InheritedIndexController oldWidget) {
|
|
return oldWidget.changeIndex != changeIndex;
|
|
}
|
|
}
|