api update and flutter update

This commit is contained in:
Wowieee4
2026-05-07 10:22:06 +07:00
parent c5d716248e
commit 53e612e221
25 changed files with 4423 additions and 142 deletions
@@ -0,0 +1,137 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import '../../core/services/voice_command_handler.dart';
import '../../core/services/stt_service.dart';
import '../../core/services/tts_service.dart';
import '../../injection_container.dart';
class UserShell extends StatefulWidget {
final Widget child;
const UserShell({super.key, required this.child});
@override
State<UserShell> createState() => _UserShellState();
}
class _UserShellState extends State<UserShell> {
@override
void initState() {
super.initState();
_startVoiceListening();
_setupVoiceCommands();
}
void _startVoiceListening() {
sl<SttService>().startListening();
}
void _setupVoiceCommands() {
sl<VoiceCommandHandler>().onCommand = (key) {
if (!mounted) return;
switch (key) {
case VoiceCommandKey.openWalkguide:
context.go('/user/walkguide');
sl<TtsService>().speak('Walkguide menu opened');
break;
case VoiceCommandKey.openNotification:
context.go('/user/notifications');
sl<TtsService>().speak('Notifications opened');
break;
case VoiceCommandKey.openSos:
context.go('/user/sos');
sl<TtsService>().speak('SOS menu opened');
break;
case VoiceCommandKey.openActivity:
context.go('/user/activity');
sl<TtsService>().speak('Activity log opened');
break;
case VoiceCommandKey.openNavigation:
context.go('/user/navigation');
sl<TtsService>().speak('Navigation mode opened');
break;
case VoiceCommandKey.openSettings:
context.go('/user/settings');
sl<TtsService>().speak('Settings opened');
break;
case VoiceCommandKey.callGuardian:
context.go('/user/call');
break;
case VoiceCommandKey.sendSos:
case VoiceCommandKey.whereAmI:
case VoiceCommandKey.startWalkguide:
case VoiceCommandKey.stopWalkguide:
case VoiceCommandKey.readAllNotif:
// These are handled by individual screens
break;
default:
break;
}
};
}
int _tabFromLocation(String location) {
if (location.startsWith('/user/walkguide')) return 0;
if (location.startsWith('/user/sos')) return 1;
if (location.startsWith('/user/activity')) return 2;
if (location.startsWith('/user/notifications')) return 3;
if (location.startsWith('/user/navigation')) return 4;
return 5;
}
@override
Widget build(BuildContext context) {
final location = GoRouterState.of(context).matchedLocation;
final currentTab = _tabFromLocation(location);
// Hide bottom nav during full-screen screens
final hideNav = location.startsWith('/user/call') || location.startsWith('/user/pairing');
return Scaffold(
body: widget.child,
bottomNavigationBar: hideNav ? null : Container(
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.08), blurRadius: 20, offset: const Offset(0, -4))],
),
child: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_navItem(context, 0, currentTab, Icons.remove_red_eye_outlined, Icons.remove_red_eye, 'Guide', '/user/walkguide'),
_navItem(context, 1, currentTab, Icons.warning_amber_outlined, Icons.warning_amber, 'SOS', '/user/sos'),
_navItem(context, 2, currentTab, Icons.list_alt_outlined, Icons.list_alt, 'Activity', '/user/activity'),
_navItem(context, 3, currentTab, Icons.notifications_outlined, Icons.notifications, 'Notif', '/user/notifications'),
_navItem(context, 4, currentTab, Icons.map_outlined, Icons.map, 'Navigate', '/user/navigation'),
_navItem(context, 5, currentTab, Icons.settings_outlined, Icons.settings, 'Settings', '/user/settings'),
],
),
),
),
),
);
}
Widget _navItem(BuildContext ctx, int idx, int current, IconData icon, IconData activeIcon, String label, String route) {
final active = idx == current;
return GestureDetector(
onTap: () => ctx.go(route),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: active ? const Color(0xFFEFF6FF) : Colors.transparent,
borderRadius: BorderRadius.circular(10),
),
child: Column(mainAxisSize: MainAxisSize.min, children: [
Icon(active ? activeIcon : icon, size: 22, color: active ? const Color(0xFF1A56DB) : const Color(0xFF94A3B8)),
const SizedBox(height: 3),
Text(label, style: GoogleFonts.inter(fontSize: 10, fontWeight: active ? FontWeight.w600 : FontWeight.w400,
color: active ? const Color(0xFF1A56DB) : const Color(0xFF94A3B8))),
]),
),
);
}
}