POLISHING STUFF and Smarter AI, and alot i mean.. ALOT OF BUG FIX AND OPTIMIZE
This commit is contained in:
@@ -15,6 +15,9 @@ import '../../core/errors/friendly_error.dart';
|
||||
import '../../core/network/api_client.dart';
|
||||
import '../../core/services/location_reporter_service.dart';
|
||||
import '../../core/services/tts_service.dart';
|
||||
import '../../core/theme/app_colors.dart';
|
||||
import '../../core/theme/app_decorations.dart';
|
||||
import '../../shared/widgets/animations/animations.dart';
|
||||
import 'application/walk_guide_cubit.dart';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -48,7 +51,7 @@ class _WalkGuideScreenState extends State<WalkGuideScreen>
|
||||
_scanCtrl = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 2200),
|
||||
)..repeat();
|
||||
);
|
||||
_loadPairingStatus();
|
||||
}
|
||||
|
||||
@@ -73,8 +76,10 @@ class _WalkGuideScreenState extends State<WalkGuideScreen>
|
||||
await _startCamera();
|
||||
await sl<LocationReporterService>().start(walkGuideActive: true);
|
||||
await _cubit.start();
|
||||
_scanCtrl.repeat();
|
||||
_cubit.updateStatus(_activeStatusText());
|
||||
} else {
|
||||
_scanCtrl.stop();
|
||||
await _stopCamera();
|
||||
await sl<LocationReporterService>().stop();
|
||||
await _cubit.stop();
|
||||
@@ -151,7 +156,7 @@ class _WalkGuideScreenState extends State<WalkGuideScreen>
|
||||
);
|
||||
final controller = CameraController(
|
||||
backCamera,
|
||||
ResolutionPreset.medium,
|
||||
ResolutionPreset.low,
|
||||
enableAudio: false,
|
||||
imageFormatGroup: ImageFormatGroup.yuv420,
|
||||
);
|
||||
@@ -195,7 +200,7 @@ class _WalkGuideScreenState extends State<WalkGuideScreen>
|
||||
void _onCameraImage(CameraImage image) {
|
||||
if (!_cubit.state.active || _processingFrame) return;
|
||||
final now = DateTime.now();
|
||||
if (now.difference(_lastInferenceAt) < const Duration(milliseconds: 900)) {
|
||||
if (now.difference(_lastInferenceAt) < const Duration(milliseconds: 1500)) {
|
||||
return;
|
||||
}
|
||||
_lastInferenceAt = now;
|
||||
@@ -258,58 +263,60 @@ class _WalkGuideScreenState extends State<WalkGuideScreen>
|
||||
onPressed: () => context.go('/user/pairing'),
|
||||
icon: const Icon(Icons.link)),
|
||||
],
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _VisionPanel(
|
||||
state: state,
|
||||
camera: _camera,
|
||||
scanCtrl: _scanCtrl,
|
||||
paired: _paired,
|
||||
pairingLoading: _pairingLoading,
|
||||
onPairingTap: () => context.go('/user/pairing'),
|
||||
child: FadeSlideWrapper(
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _VisionPanel(
|
||||
state: state,
|
||||
camera: _camera,
|
||||
scanCtrl: _scanCtrl,
|
||||
paired: _paired,
|
||||
pairingLoading: _pairingLoading,
|
||||
onPairingTap: () => context.go('/user/pairing'),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 14),
|
||||
_StatusStrip(
|
||||
active: state.active,
|
||||
paired: _paired,
|
||||
latestDetection: state.latestDetection,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: FilledButton.icon(
|
||||
onPressed: _pairingLoading ? null : _toggle,
|
||||
icon: Icon(state.active ? Icons.stop : Icons.play_arrow),
|
||||
label: Text(state.active ? 'Stop Scan' : 'Start Scan'),
|
||||
const SizedBox(height: 14),
|
||||
_StatusStrip(
|
||||
active: state.active,
|
||||
paired: _paired,
|
||||
latestDetection: state.latestDetection,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: FilledButton.icon(
|
||||
onPressed: _pairingLoading ? null : _toggle,
|
||||
icon: Icon(state.active ? Icons.stop : Icons.play_arrow),
|
||||
label: Text(state.active ? 'Stop Scan' : 'Start Scan'),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
_ActionSquare(
|
||||
icon: Icons.sos_outlined,
|
||||
color: const Color(0xFFDC2626),
|
||||
onTap: () async {
|
||||
if (await _ensurePaired() && context.mounted) {
|
||||
context.go('/user/sos');
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
_ActionSquare(
|
||||
icon: Icons.call_outlined,
|
||||
color: const Color(0xFF059669),
|
||||
onTap: () async {
|
||||
if (await _ensurePaired() && context.mounted) {
|
||||
context.go('/user/call');
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
const SizedBox(width: 10),
|
||||
_ActionSquare(
|
||||
icon: Icons.sos_outlined,
|
||||
color: AppColors.danger,
|
||||
onTap: () async {
|
||||
if (await _ensurePaired() && context.mounted) {
|
||||
context.go('/user/sos');
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
_ActionSquare(
|
||||
icon: Icons.call_outlined,
|
||||
color: AppColors.success,
|
||||
onTap: () async {
|
||||
if (await _ensurePaired() && context.mounted) {
|
||||
context.go('/user/call');
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -336,8 +343,12 @@ class _VisionPanel extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final cameraReady = camera != null && camera!.value.isInitialized;
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(28),
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(28),
|
||||
boxShadow: AppDecorations.cardShadow,
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: DecoratedBox(
|
||||
decoration: const BoxDecoration(color: Color(0xFF07111F)),
|
||||
child: Stack(
|
||||
@@ -393,20 +404,21 @@ class _VisionPanel extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 18,
|
||||
left: 18,
|
||||
right: 18,
|
||||
child: Row(
|
||||
top: 14,
|
||||
left: 14,
|
||||
right: 14,
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 6,
|
||||
children: [
|
||||
_Pill(
|
||||
text: state.active ? 'LIVE AI SCAN' : 'STANDBY',
|
||||
text: state.active ? 'LIVE AI' : 'STANDBY',
|
||||
color: state.active
|
||||
? const Color(0xFF22C55E)
|
||||
: const Color(0xFFF59E0B),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
_Pill(
|
||||
text: paired ? 'GUARDIAN LINKED' : 'PAIRING REQUIRED',
|
||||
text: paired ? 'LINKED' : 'PAIRING',
|
||||
color: paired
|
||||
? const Color(0xFF38BDF8)
|
||||
: const Color(0xFFF97316),
|
||||
@@ -444,56 +456,71 @@ class _VisionPanel extends StatelessWidget {
|
||||
),
|
||||
if (!paired && !pairingLoading)
|
||||
Positioned.fill(
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF020617).withValues(alpha: 0.72),
|
||||
),
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 70,
|
||||
height: 70,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFFFBEB)
|
||||
.withValues(alpha: 0.14),
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
border:
|
||||
Border.all(color: const Color(0xFFF59E0B)),
|
||||
),
|
||||
child: const Icon(Icons.link_off,
|
||||
color: Color(0xFFFBBF24), size: 34),
|
||||
),
|
||||
const SizedBox(height: 14),
|
||||
const Text(
|
||||
'Guardian belum terhubung',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
const Text(
|
||||
'Pairing dulu supaya SOS, panggilan, dan pemantauan live punya penerima yang jelas.',
|
||||
textAlign: TextAlign.center,
|
||||
style:
|
||||
TextStyle(color: Colors.white70, height: 1.35),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
FilledButton.icon(
|
||||
onPressed: onPairingTap,
|
||||
icon: const Icon(Icons.link),
|
||||
label: const Text('Buka Pairing'),
|
||||
),
|
||||
],
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final compact = constraints.maxHeight < 270;
|
||||
return DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF020617).withValues(alpha: 0.72),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: compact ? 16 : 24,
|
||||
vertical: compact ? 10 : 20,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: compact ? 46 : 64,
|
||||
height: compact ? 46 : 64,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFFFBEB)
|
||||
.withValues(alpha: 0.14),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
color: const Color(0xFFF59E0B)),
|
||||
),
|
||||
child: Icon(Icons.link_off,
|
||||
color: const Color(0xFFFBBF24),
|
||||
size: compact ? 24 : 32),
|
||||
),
|
||||
SizedBox(height: compact ? 8 : 12),
|
||||
Text(
|
||||
'Guardian belum terhubung',
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: compact ? 16 : 21,
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
),
|
||||
if (!compact) ...[
|
||||
const SizedBox(height: 6),
|
||||
const Text(
|
||||
'Pairing dulu supaya SOS, panggilan, dan pemantauan live punya penerima yang jelas.',
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: Colors.white70, height: 1.35),
|
||||
),
|
||||
],
|
||||
SizedBox(height: compact ? 10 : 14),
|
||||
FilledButton.icon(
|
||||
onPressed: onPairingTap,
|
||||
icon: const Icon(Icons.link),
|
||||
label: const Text('Buka Pairing'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
@@ -624,15 +651,8 @@ class _MetricChip extends StatelessWidget {
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
border: Border.all(color: const Color(0xFFE2E8F0)),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.04),
|
||||
blurRadius: 14,
|
||||
offset: const Offset(0, 8),
|
||||
),
|
||||
],
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
boxShadow: AppDecorations.cardShadow,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
@@ -680,12 +700,20 @@ class _ActionSquare extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: color,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
return BounceTap(
|
||||
onTap: onTap,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: color.withValues(alpha: 0.22),
|
||||
blurRadius: 16,
|
||||
offset: const Offset(0, 8),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: SizedBox(
|
||||
width: 54,
|
||||
height: 50,
|
||||
@@ -816,7 +844,7 @@ class _Page extends StatelessWidget {
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [Color(0xFFF8FAFC), Color(0xFFEFF6FF)],
|
||||
colors: [AppColors.softBlueBg, Colors.white],
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
@@ -835,12 +863,11 @@ class _Page extends StatelessWidget {
|
||||
width: compact ? 38 : 46,
|
||||
height: compact ? 38 : 46,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF2563EB),
|
||||
gradient: AppDecorations.blueGradient,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color:
|
||||
const Color(0xFF2563EB).withValues(alpha: 0.28),
|
||||
color: AppColors.primaryBlue.withValues(alpha: 0.28),
|
||||
blurRadius: 18,
|
||||
offset: const Offset(0, 8),
|
||||
),
|
||||
@@ -862,14 +889,14 @@ class _Page extends StatelessWidget {
|
||||
.headlineSmall
|
||||
?.copyWith(
|
||||
fontWeight: FontWeight.w900,
|
||||
color: const Color(0xFF0F172A),
|
||||
color: AppColors.textDark,
|
||||
fontSize: compact ? 22 : null,
|
||||
)),
|
||||
if (subtitle != null)
|
||||
Text(subtitle!,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(color: Color(0xFF64748B))),
|
||||
style: const TextStyle(color: AppColors.muted)),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user