a bunch of flutter updates
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
import 'app_cubit.dart';
|
||||
import 'router.dart';
|
||||
|
||||
class WalkGuideApp extends StatelessWidget {
|
||||
const WalkGuideApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const seed = Color(0xFF1A56DB);
|
||||
|
||||
return BlocProvider(
|
||||
create: (_) => AppCubit(),
|
||||
child: MaterialApp.router(
|
||||
title: 'WalkGuide',
|
||||
debugShowCheckedModeBanner: false,
|
||||
routerConfig: appRouter,
|
||||
theme: ThemeData(
|
||||
useMaterial3: true,
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: seed),
|
||||
scaffoldBackgroundColor: const Color(0xFFF8FAFC),
|
||||
textTheme: GoogleFonts.interTextTheme(),
|
||||
appBarTheme: const AppBarTheme(
|
||||
centerTitle: false,
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Color(0xFF0F172A),
|
||||
elevation: 0,
|
||||
surfaceTintColor: Colors.white,
|
||||
),
|
||||
filledButtonTheme: FilledButtonThemeData(
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: seed,
|
||||
foregroundColor: Colors.white,
|
||||
minimumSize: const Size(0, 46),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
),
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class AppState {
|
||||
final bool online;
|
||||
final String? role;
|
||||
final String? serverUrl;
|
||||
|
||||
const AppState({required this.online, this.role, this.serverUrl});
|
||||
|
||||
AppState copyWith({bool? online, String? role, String? serverUrl}) {
|
||||
return AppState(
|
||||
online: online ?? this.online,
|
||||
role: role ?? this.role,
|
||||
serverUrl: serverUrl ?? this.serverUrl,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppCubit extends Cubit<AppState> {
|
||||
AppCubit() : super(const AppState(online: true));
|
||||
|
||||
void setSession({required String role, required String serverUrl}) {
|
||||
emit(state.copyWith(role: role, serverUrl: serverUrl, online: true));
|
||||
}
|
||||
|
||||
void setOnline(bool value) => emit(state.copyWith(online: value));
|
||||
|
||||
void clearSession() => emit(const AppState(online: true));
|
||||
}
|
||||
@@ -1,38 +1,60 @@
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'core/constants/app_constants.dart';
|
||||
import 'core/network/api_client.dart';
|
||||
import 'core/storage/secure_storage.dart';
|
||||
import 'core/services/tts_service.dart';
|
||||
import 'core/services/stt_service.dart';
|
||||
import 'core/services/voice_command_handler.dart';
|
||||
import 'core/services/haptic_service.dart';
|
||||
import '../core/constants/app_constants.dart';
|
||||
import '../core/ai/obstacle_analyzer.dart';
|
||||
import '../core/ai/yolo_detector.dart';
|
||||
import '../core/network/api_client.dart';
|
||||
import '../core/services/haptic_service.dart';
|
||||
import '../core/services/call_service.dart';
|
||||
import '../core/services/fcm_service.dart';
|
||||
import '../core/services/location_reporter_service.dart';
|
||||
import '../core/services/offline_queue_service.dart';
|
||||
import '../core/services/stt_service.dart';
|
||||
import '../core/services/tts_service.dart';
|
||||
import '../core/services/voice_command_handler.dart';
|
||||
import '../core/services/websocket_service.dart';
|
||||
import '../core/storage/secure_storage.dart';
|
||||
|
||||
final sl = GetIt.instance;
|
||||
|
||||
Future<void> initDependencies() async {
|
||||
// ── Core singletons ──────────────────────────────────────────────────────
|
||||
sl.registerLazySingleton<SecureStorage>(() => SecureStorage());
|
||||
sl.registerLazySingleton<ApiClient>(() => ApiClient(sl<SecureStorage>()));
|
||||
|
||||
sl.registerLazySingleton<TtsService>(() => TtsService());
|
||||
sl.registerLazySingleton<SttService>(() => SttService());
|
||||
sl.registerLazySingleton<HapticService>(() => HapticService());
|
||||
sl.registerLazySingleton<ObstacleAnalyzer>(() => ObstacleAnalyzer());
|
||||
sl.registerLazySingleton<YoloDetector>(() => YoloDetector(sl<ObstacleAnalyzer>()));
|
||||
sl.registerLazySingleton<OfflineQueueService>(() => OfflineQueueService());
|
||||
sl.registerLazySingleton<FcmService>(() => FcmService(sl<ApiClient>()));
|
||||
sl.registerLazySingleton<WebSocketService>(() => WebSocketService(sl<SecureStorage>()));
|
||||
sl.registerLazySingleton<LocationReporterService>(() => LocationReporterService(sl<ApiClient>(), sl<OfflineQueueService>()));
|
||||
sl.registerLazySingleton<CallService>(() => CallService(sl<ApiClient>()));
|
||||
sl.registerLazySingleton<VoiceCommandHandler>(
|
||||
() => VoiceCommandHandler(sl<SttService>(), sl<TtsService>()),
|
||||
);
|
||||
|
||||
// ── Init ApiClient if serverUrl already saved ─────────────────────────────
|
||||
final serverUrl = await AppConstants.getServerUrl();
|
||||
if (serverUrl != null && serverUrl.isNotEmpty) {
|
||||
await sl<ApiClient>().init(serverUrl);
|
||||
}
|
||||
|
||||
// ── Init TTS ──────────────────────────────────────────────────────────────
|
||||
await sl<TtsService>().init();
|
||||
|
||||
// ── Init STT ──────────────────────────────────────────────────────────────
|
||||
await sl<SttService>().init();
|
||||
try {
|
||||
await sl<TtsService>().init();
|
||||
} catch (e) {
|
||||
debugPrint('TTS init skipped: $e');
|
||||
}
|
||||
await sl<YoloDetector>().init();
|
||||
if (!kIsWeb) {
|
||||
try {
|
||||
await sl<SttService>().init();
|
||||
} catch (e) {
|
||||
debugPrint('STT init skipped: $e');
|
||||
}
|
||||
}
|
||||
sl<VoiceCommandHandler>().loadDefaultCommands();
|
||||
}
|
||||
if (!kIsWeb) {
|
||||
await sl<FcmService>().init();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,110 +1,99 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import 'core/storage/secure_storage.dart';
|
||||
import 'core/constants/app_constants.dart';
|
||||
import 'injection_container.dart';
|
||||
|
||||
// Auth
|
||||
import 'features/server_connect/presentation/screens/server_connect_screen.dart';
|
||||
import 'features/auth/presentation/screens/splash_screen.dart';
|
||||
import 'features/auth/presentation/screens/login_screen.dart';
|
||||
import 'features/auth/presentation/screens/register_screen.dart';
|
||||
|
||||
// User shell + screens
|
||||
import 'shared/widgets/user_shell.dart';
|
||||
import 'features/walk_guide/presentation/screens/walk_guide_screen.dart';
|
||||
import 'features/sos/presentation/screens/sos_screen.dart';
|
||||
import 'features/activity_log/presentation/screens/activity_log_screen.dart';
|
||||
import 'features/notifications/presentation/screens/notification_screen.dart';
|
||||
import 'features/navigation_mode/presentation/screens/navigation_mode_screen.dart';
|
||||
import 'features/settings/presentation/screens/user_settings_screen.dart';
|
||||
|
||||
// Guardian shell + screens
|
||||
import 'shared/widgets/guardian_shell.dart';
|
||||
import 'features/guardian/presentation/screens/guardian_dashboard_screen.dart';
|
||||
import 'features/guardian/presentation/screens/guardian_map_screen.dart';
|
||||
import 'features/guardian/presentation/screens/guardian_activity_log_screen.dart';
|
||||
import 'features/guardian/presentation/screens/guardian_send_notif_screen.dart';
|
||||
import 'features/guardian/presentation/screens/guardian_ai_config_screen.dart';
|
||||
import 'features/guardian/presentation/screens/guardian_voice_cmd_screen.dart';
|
||||
import 'features/guardian/presentation/screens/guardian_shortcut_screen.dart';
|
||||
import 'features/guardian/presentation/screens/guardian_geofence_screen.dart';
|
||||
import 'features/pairing/presentation/screens/guardian_pairing_screen.dart';
|
||||
import 'features/pairing/presentation/screens/user_pairing_screen.dart';
|
||||
|
||||
// Call
|
||||
import 'features/call/presentation/screens/call_screen.dart';
|
||||
import 'features/call/presentation/screens/incoming_call_screen.dart';
|
||||
import '../core/constants/app_constants.dart';
|
||||
import '../features/screens.dart';
|
||||
import '../shared/widgets/app_shells.dart';
|
||||
|
||||
final GoRouter appRouter = GoRouter(
|
||||
initialLocation: '/splash',
|
||||
redirect: (context, state) async {
|
||||
final serverUrl = await AppConstants.getServerUrl();
|
||||
final path = state.matchedLocation;
|
||||
final serverUrl = await AppConstants.getServerUrl();
|
||||
|
||||
// 1. No server URL → must go to server-connect
|
||||
if (serverUrl == null || serverUrl.isEmpty) {
|
||||
if (path != '/server-connect') return '/server-connect';
|
||||
return null;
|
||||
if ((serverUrl == null || serverUrl.isEmpty) && path != '/server-connect') {
|
||||
return '/server-connect';
|
||||
}
|
||||
if (path == '/server-connect' &&
|
||||
serverUrl != null &&
|
||||
serverUrl.isNotEmpty) {
|
||||
return '/splash';
|
||||
}
|
||||
|
||||
// 2. Already on server-connect but has URL → splash
|
||||
if (path == '/server-connect') return '/splash';
|
||||
|
||||
return null;
|
||||
},
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/server-connect',
|
||||
builder: (context, state) => const ServerConnectScreen(),
|
||||
),
|
||||
path: '/server-connect',
|
||||
builder: (_, __) => const ServerConnectScreen()),
|
||||
GoRoute(path: '/splash', builder: (_, __) => const SplashScreen()),
|
||||
GoRoute(path: '/login', builder: (_, __) => const LoginScreen()),
|
||||
GoRoute(path: '/register', builder: (_, __) => const RegisterScreen()),
|
||||
GoRoute(
|
||||
path: '/splash',
|
||||
builder: (context, state) => const SplashScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/login',
|
||||
builder: (context, state) => const LoginScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/register',
|
||||
builder: (context, state) => const RegisterScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/incoming-call',
|
||||
builder: (context, state) => const IncomingCallScreen(),
|
||||
),
|
||||
|
||||
// ── USER SHELL ──────────────────────────────────────────────────────────
|
||||
path: '/incoming-call', builder: (_, __) => const IncomingCallScreen()),
|
||||
ShellRoute(
|
||||
builder: (context, state, child) => UserShell(child: child),
|
||||
builder: (_, __, child) => UserShell(child: child),
|
||||
routes: [
|
||||
GoRoute(path: '/user/walkguide', builder: (c, s) => const WalkGuideScreen()),
|
||||
GoRoute(path: '/user/sos', builder: (c, s) => const SosScreen()),
|
||||
GoRoute(path: '/user/activity', builder: (c, s) => const ActivityLogScreen()),
|
||||
GoRoute(path: '/user/notifications',builder: (c, s) => const NotificationScreen()),
|
||||
GoRoute(path: '/user/navigation', builder: (c, s) => const NavigationModeScreen()),
|
||||
GoRoute(path: '/user/settings', builder: (c, s) => const UserSettingsScreen()),
|
||||
GoRoute(path: '/user/pairing', builder: (c, s) => const UserPairingScreen()),
|
||||
GoRoute(path: '/user/call', builder: (c, s) => const CallScreen()),
|
||||
GoRoute(
|
||||
path: '/user/walkguide',
|
||||
builder: (_, __) => const WalkGuideScreen()),
|
||||
GoRoute(path: '/user/sos', builder: (_, __) => const SosScreen()),
|
||||
GoRoute(
|
||||
path: '/user/activity',
|
||||
builder: (_, __) => const ActivityLogScreen()),
|
||||
GoRoute(
|
||||
path: '/user/notifications',
|
||||
builder: (_, __) => const NotificationScreen()),
|
||||
GoRoute(
|
||||
path: '/user/navigation',
|
||||
builder: (_, __) => const NavigationModeScreen()),
|
||||
GoRoute(
|
||||
path: '/user/settings',
|
||||
builder: (_, __) => const UserSettingsScreen()),
|
||||
GoRoute(
|
||||
path: '/user/pairing',
|
||||
builder: (_, __) => const UserPairingScreen()),
|
||||
GoRoute(path: '/user/call', builder: (_, __) => const CallScreen()),
|
||||
GoRoute(
|
||||
path: '/user/benchmark',
|
||||
builder: (_, __) => const AiBenchmarkScreen()),
|
||||
],
|
||||
),
|
||||
|
||||
// ── GUARDIAN SHELL ──────────────────────────────────────────────────────
|
||||
ShellRoute(
|
||||
builder: (context, state, child) => GuardianShell(child: child),
|
||||
builder: (_, __, child) => GuardianShell(child: child),
|
||||
routes: [
|
||||
GoRoute(path: '/guardian/dashboard', builder: (c, s) => const GuardianDashboardScreen()),
|
||||
GoRoute(path: '/guardian/map', builder: (c, s) => const GuardianMapScreen()),
|
||||
GoRoute(path: '/guardian/logs', builder: (c, s) => const GuardianActivityLogScreen()),
|
||||
GoRoute(path: '/guardian/send-notif', builder: (c, s) => const GuardianSendNotifScreen()),
|
||||
GoRoute(path: '/guardian/ai-config', builder: (c, s) => const GuardianAiConfigScreen()),
|
||||
GoRoute(path: '/guardian/voice-cmd', builder: (c, s) => const GuardianVoiceCmdScreen()),
|
||||
GoRoute(path: '/guardian/shortcuts', builder: (c, s) => const GuardianShortcutScreen()),
|
||||
GoRoute(path: '/guardian/geofence', builder: (c, s) => const GuardianGeofenceScreen()),
|
||||
GoRoute(path: '/guardian/pairing', builder: (c, s) => const GuardianPairingScreen()),
|
||||
GoRoute(
|
||||
path: '/guardian/dashboard',
|
||||
builder: (_, __) => const GuardianDashboardScreen()),
|
||||
GoRoute(
|
||||
path: '/guardian/map',
|
||||
builder: (_, __) => const GuardianMapScreen()),
|
||||
GoRoute(
|
||||
path: '/guardian/logs',
|
||||
builder: (_, __) => const GuardianActivityLogScreen()),
|
||||
GoRoute(
|
||||
path: '/guardian/send-notif',
|
||||
builder: (_, __) => const GuardianSendNotifScreen()),
|
||||
GoRoute(
|
||||
path: '/guardian/ai-config',
|
||||
builder: (_, __) => const GuardianAiConfigScreen()),
|
||||
GoRoute(
|
||||
path: '/guardian/voice-cmd',
|
||||
builder: (_, __) => const GuardianVoiceCmdScreen()),
|
||||
GoRoute(
|
||||
path: '/guardian/shortcuts',
|
||||
builder: (_, __) => const GuardianShortcutScreen()),
|
||||
GoRoute(
|
||||
path: '/guardian/geofence',
|
||||
builder: (_, __) => const GuardianGeofenceScreen()),
|
||||
GoRoute(
|
||||
path: '/guardian/pairing',
|
||||
builder: (_, __) => const GuardianPairingScreen()),
|
||||
GoRoute(
|
||||
path: '/guardian/settings',
|
||||
builder: (_, __) => const GuardianSettingsScreen()),
|
||||
GoRoute(
|
||||
path: '/guardian/benchmark',
|
||||
builder: (_, __) => const AiBenchmarkScreen()),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user