457 lines
15 KiB
Dart
457 lines
15 KiB
Dart
import 'dart:developer';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter/services.dart';
|
||
import 'package:get/get.dart';
|
||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||
import 'package:onufitness/constants/api_enum_constant.dart';
|
||
import 'package:onufitness/constants/asset_constants.dart';
|
||
import 'package:onufitness/constants/color_constant.dart';
|
||
import 'package:onufitness/constants/string_constant.dart';
|
||
import 'package:onufitness/controller/get_agora_token_controller.dart';
|
||
import 'package:onufitness/screens/accounts/views/coach_account/coach_my_account_screen.dart';
|
||
import 'package:onufitness/screens/accounts/views/trainee_account/trainee_account.dart';
|
||
import 'package:onufitness/screens/chat/controllers/chat_controller.dart';
|
||
import 'package:onufitness/screens/goals/screens/goal_screen.dart';
|
||
import 'package:onufitness/screens/echoboard/controllers/echoboard_controller.dart';
|
||
import 'package:onufitness/screens/echoboard/views/echoboard_view_screen.dart';
|
||
import 'package:onufitness/screens/home/view/home_screen.dart';
|
||
import 'package:onufitness/services/agora/agora_chat_service.dart';
|
||
import 'package:onufitness/services/agora/call_services.dart';
|
||
import 'package:onufitness/services/local_storage_services/shared_services.dart';
|
||
import 'package:onufitness/services/notification_services/notification_service.dart';
|
||
import 'package:permission_handler/permission_handler.dart';
|
||
|
||
class NavigationController extends GetxController {
|
||
var currentIndex = 0.obs;
|
||
|
||
void changeIndex(int index) {
|
||
if (index >= 0 && index < 4) {
|
||
currentIndex.value = index;
|
||
} else {
|
||
currentIndex.value = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
class DashboardScreen extends StatefulWidget {
|
||
const DashboardScreen({super.key});
|
||
|
||
@override
|
||
State<DashboardScreen> createState() => _DashboardScreenState();
|
||
}
|
||
|
||
class _DashboardScreenState extends State<DashboardScreen>
|
||
with WidgetsBindingObserver {
|
||
final notificationService = Get.find<NotificationService>();
|
||
|
||
final AgoraTokenController agoraTokenController = Get.put(
|
||
AgoraTokenController(),
|
||
permanent: true,
|
||
);
|
||
|
||
final ChatController agoraChatController = Get.put(
|
||
ChatController(),
|
||
permanent: true,
|
||
);
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
WidgetsBinding.instance.addObserver(this);
|
||
|
||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||
// Request all permissions sequentially
|
||
requestAllPermissionsSequentially();
|
||
agoraChatTokenAPIcall();
|
||
});
|
||
}
|
||
|
||
@override
|
||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||
super.didChangeAppLifecycleState(state);
|
||
|
||
if (state == AppLifecycleState.resumed) {
|
||
log("App resumed – re-checking Agora login...");
|
||
agoraChatTokenAPIcall();
|
||
} else if (state == AppLifecycleState.paused) {
|
||
log("App paused – you can handle disconnects here if needed");
|
||
}
|
||
}
|
||
|
||
/// Request all permissions sequentially to ensure all popups appear
|
||
Future<void> requestAllPermissionsSequentially() async {
|
||
try {
|
||
log("Starting sequential permission requests...");
|
||
await WidgetsBinding.instance.endOfFrame;
|
||
|
||
// Add a small initial delay to ensure UI is ready
|
||
await Future.delayed(const Duration(milliseconds: 500));
|
||
|
||
// 1. Request Notification permission first
|
||
log("Requesting notification permission...");
|
||
await notificationService.requestPermissions();
|
||
|
||
// Small delay between permissions
|
||
await Future.delayed(const Duration(milliseconds: 300));
|
||
|
||
// 2. Request Microphone permission
|
||
log("Requesting microphone permission...");
|
||
await requestMicrophonePermission();
|
||
|
||
// Small delay between permissions
|
||
await Future.delayed(const Duration(milliseconds: 300));
|
||
|
||
// 3. Request Camera permission
|
||
log("Requesting camera permission...");
|
||
await requestCameraPermission();
|
||
|
||
log("All permission requests completed");
|
||
} catch (e, st) {
|
||
log("Error requesting permissions: $e", stackTrace: st);
|
||
}
|
||
}
|
||
|
||
// Request microphone permission
|
||
Future<void> requestMicrophonePermission() async {
|
||
try {
|
||
final status = await Permission.microphone.status;
|
||
log("Microphone permission: $status");
|
||
|
||
if (status.isDenied) {
|
||
final result = await Permission.microphone.request();
|
||
log("Microphone permission result: $result");
|
||
|
||
if (result.isPermanentlyDenied) {
|
||
await openAppSettings();
|
||
}
|
||
} else if (status.isPermanentlyDenied) {
|
||
await openAppSettings();
|
||
}
|
||
} catch (e) {
|
||
log("Error requesting microphone permission: $e");
|
||
}
|
||
}
|
||
|
||
// Request camera permission
|
||
Future<void> requestCameraPermission() async {
|
||
try {
|
||
final status = await Permission.camera.status;
|
||
log("Camera permission: $status");
|
||
|
||
if (status.isDenied) {
|
||
final result = await Permission.camera.request();
|
||
log("Camera permission result: $result");
|
||
|
||
if (result.isPermanentlyDenied) {
|
||
await openAppSettings();
|
||
}
|
||
} else if (status.isPermanentlyDenied) {
|
||
await openAppSettings();
|
||
}
|
||
} catch (e) {
|
||
log("Error requesting camera permission: $e");
|
||
}
|
||
}
|
||
|
||
Future<void> agoraChatTokenAPIcall() async {
|
||
try {
|
||
if (!Get.isRegistered<AgoraTokenController>()) {
|
||
Get.put(AgoraTokenController());
|
||
}
|
||
final agoraTokenController = Get.find<AgoraTokenController>();
|
||
|
||
final success = await agoraTokenController.getAgoraUserAndRrmToken();
|
||
if (success) {
|
||
await agoraChatCallServicesLogin();
|
||
} else {
|
||
log("Agora token fetch failed.");
|
||
}
|
||
} catch (e, st) {
|
||
log("Error in agoraChatTokenAPIcall: $e", stackTrace: st);
|
||
}
|
||
}
|
||
|
||
Future<void> agoraChatCallServicesLogin() async {
|
||
// Agora Chat Service Initialization
|
||
await chatLoginToAgora();
|
||
//Small delay
|
||
await Future.delayed(Duration(milliseconds: 500));
|
||
// Agora Call Service Initialization
|
||
|
||
await initializeCallAgora();
|
||
}
|
||
|
||
chatLoginToAgora() async {
|
||
if (!Get.isRegistered<AgoraChatService>()) {
|
||
Get.put(await AgoraChatService().init(), permanent: true);
|
||
} else {
|
||
final chatService = Get.find<AgoraChatService>();
|
||
await chatService.initChatSdk();
|
||
await chatService.loginToAgora();
|
||
}
|
||
}
|
||
|
||
initializeCallAgora() async {
|
||
// No need to re-initialize, just ensure it's available
|
||
if (!Get.isRegistered<AgoraCallService>()) {
|
||
await Get.putAsync<AgoraCallService>(() async {
|
||
return await AgoraCallService().init();
|
||
}, permanent: true);
|
||
} else {
|
||
Get.find<AgoraCallService>().agoraRTMlogin();
|
||
}
|
||
}
|
||
|
||
final NavigationController navController = Get.put(
|
||
NavigationController(),
|
||
permanent: true,
|
||
);
|
||
|
||
final EchoBoardController echoBoardController = Get.put(
|
||
EchoBoardController(),
|
||
permanent: true,
|
||
);
|
||
|
||
final List<Widget> pages = [
|
||
FitnessDashboard(),
|
||
EchoBoardViewScreen(),
|
||
GoalScreen(),
|
||
SharedServices.getLoginDetails()?.data?.userRole == ApiEnum.traineeUserRole
|
||
? TraineeMyAccountScreen()
|
||
: CoachMyAccountScreen(),
|
||
];
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||
final int? initialIndex = Get.arguments;
|
||
if (initialIndex != null &&
|
||
initialIndex != navController.currentIndex.value) {
|
||
navController.changeIndex(initialIndex);
|
||
}
|
||
});
|
||
|
||
return LayoutBuilder(
|
||
builder: (context, constraints) {
|
||
if (constraints.maxWidth >= 600) {
|
||
return navBarLayout(context);
|
||
} else {
|
||
return navBarLayout(context);
|
||
}
|
||
},
|
||
);
|
||
}
|
||
|
||
Widget navBarLayout(BuildContext context) {
|
||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||
value: SystemUiOverlayStyle(
|
||
systemNavigationBarColor: Colors.white,
|
||
statusBarColor: Colors.white,
|
||
systemNavigationBarIconBrightness: Brightness.dark,
|
||
statusBarIconBrightness: Brightness.dark,
|
||
),
|
||
child: Obx(
|
||
() => Scaffold(
|
||
// ✅ Add this to ensure proper system UI styling
|
||
extendBody: true,
|
||
extendBodyBehindAppBar: true,
|
||
body: SafeArea(child: pages[navController.currentIndex.value]),
|
||
bottomNavigationBar: Stack(
|
||
children: [
|
||
Container(
|
||
decoration: BoxDecoration(
|
||
borderRadius: BorderRadius.only(
|
||
topRight: Radius.circular(30),
|
||
topLeft: Radius.circular(30),
|
||
),
|
||
boxShadow: [
|
||
BoxShadow(
|
||
color: Colors.black38,
|
||
spreadRadius: 0,
|
||
blurRadius: 10,
|
||
),
|
||
],
|
||
),
|
||
child: ClipRRect(
|
||
borderRadius: const BorderRadius.only(
|
||
topRight: Radius.circular(24),
|
||
topLeft: Radius.circular(24),
|
||
),
|
||
child: BottomNavigationBar(
|
||
type: BottomNavigationBarType.fixed,
|
||
currentIndex: navController.currentIndex.value,
|
||
onTap: navController.changeIndex,
|
||
selectedItemColor: Color.fromRGBO(79, 84, 104, 1),
|
||
unselectedItemColor: Color.fromRGBO(79, 84, 104, 1),
|
||
showUnselectedLabels: true,
|
||
backgroundColor: Colors.white,
|
||
items: [
|
||
BottomNavigationBarItem(
|
||
icon: SizedBox(
|
||
height: 25.h,
|
||
width: 25.w,
|
||
child: Image.asset(AssetConstants.home),
|
||
),
|
||
label: navHomeText,
|
||
),
|
||
BottomNavigationBarItem(
|
||
icon: SizedBox(
|
||
height: 25.h,
|
||
width: 25.w,
|
||
child: Image.asset(
|
||
AssetConstants.echoBoard,
|
||
fit: BoxFit.contain,
|
||
),
|
||
),
|
||
label: navEchoboardText,
|
||
),
|
||
BottomNavigationBarItem(
|
||
icon: SizedBox(
|
||
height: 25.h,
|
||
width: 25.w,
|
||
child: Image.asset(
|
||
AssetConstants.goal,
|
||
fit: BoxFit.contain,
|
||
),
|
||
),
|
||
label: navGoalText,
|
||
),
|
||
BottomNavigationBarItem(
|
||
icon: SizedBox(
|
||
height: 25.h,
|
||
width: 25.w,
|
||
child: Image.asset(
|
||
AssetConstants.account,
|
||
fit: BoxFit.contain,
|
||
),
|
||
),
|
||
label: navAccountText,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
Positioned(
|
||
left:
|
||
MediaQuery.of(context).size.width /
|
||
4 *
|
||
navController.currentIndex.value +
|
||
MediaQuery.of(context).size.width / 8 -
|
||
15,
|
||
child: Container(
|
||
width: 30,
|
||
height: 4,
|
||
color: Color(primaryColor),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
// Widget navBarLayout(BuildContext context) {
|
||
// return Obx(
|
||
// () => Scaffold(
|
||
// body: pages[navController.currentIndex.value],
|
||
// bottomNavigationBar: Stack(
|
||
// children: [
|
||
// Container(
|
||
// decoration: BoxDecoration(
|
||
// borderRadius: BorderRadius.only(
|
||
// topRight: Radius.circular(30),
|
||
// topLeft: Radius.circular(30),
|
||
// ),
|
||
// boxShadow: [
|
||
// BoxShadow(
|
||
// color: Colors.black38,
|
||
// spreadRadius: 0,
|
||
// blurRadius: 10,
|
||
// ),
|
||
// ],
|
||
// ),
|
||
// child: ClipRRect(
|
||
// borderRadius: const BorderRadius.only(
|
||
// topRight: Radius.circular(24),
|
||
// topLeft: Radius.circular(24),
|
||
// ),
|
||
// child: BottomNavigationBar(
|
||
// type: BottomNavigationBarType.fixed,
|
||
// currentIndex: navController.currentIndex.value,
|
||
// onTap: navController.changeIndex,
|
||
// selectedItemColor: Color.fromRGBO(79, 84, 104, 1),
|
||
// unselectedItemColor: Color.fromRGBO(79, 84, 104, 1),
|
||
// showUnselectedLabels: true,
|
||
// backgroundColor: Colors.white,
|
||
// items: [
|
||
// BottomNavigationBarItem(
|
||
// icon: SizedBox(
|
||
// height: 25.h,
|
||
// width: 25.w,
|
||
// child: Image.asset(AssetConstants.home),
|
||
// ),
|
||
// label: navHomeText,
|
||
// ),
|
||
// BottomNavigationBarItem(
|
||
// icon: SizedBox(
|
||
// height: 25.h,
|
||
// width: 25.w,
|
||
// child: Image.asset(
|
||
// AssetConstants.echoBoard,
|
||
// fit: BoxFit.contain,
|
||
// ),
|
||
// ),
|
||
// label: navEchoboardText,
|
||
// ),
|
||
// BottomNavigationBarItem(
|
||
// icon: SizedBox(
|
||
// height: 25.h,
|
||
// width: 25.w,
|
||
// child: Image.asset(
|
||
// AssetConstants.goal,
|
||
// fit: BoxFit.contain,
|
||
// ),
|
||
// ),
|
||
// label: navGoalText,
|
||
// ),
|
||
// BottomNavigationBarItem(
|
||
// icon: SizedBox(
|
||
// height: 25.h,
|
||
// width: 25.w,
|
||
// child: Image.asset(
|
||
// AssetConstants.account,
|
||
// fit: BoxFit.contain,
|
||
// ),
|
||
// ),
|
||
// label: navAccountText,
|
||
// ),
|
||
// ],
|
||
// ),
|
||
// ),
|
||
// ),
|
||
// Positioned(
|
||
// left:
|
||
// MediaQuery.of(context).size.width /
|
||
// 4 *
|
||
// navController.currentIndex.value +
|
||
// MediaQuery.of(context).size.width / 8 -
|
||
// 15,
|
||
// child: Container(
|
||
// width: 30,
|
||
// height: 4,
|
||
// color: Color(primaryColor),
|
||
// ),
|
||
// ),
|
||
// ],
|
||
// ),
|
||
// ),
|
||
// );
|
||
// }
|
||
|
||
@override
|
||
void dispose() {
|
||
WidgetsBinding.instance.removeObserver(this);
|
||
super.dispose();
|
||
}
|
||
}
|