POLISHING STUFF and Smarter AI, and alot i mean.. ALOT OF BUG FIX AND OPTIMIZE
This commit is contained in:
@@ -14,6 +14,10 @@ import '../../core/errors/friendly_error.dart';
|
||||
import '../../core/network/api_client.dart';
|
||||
import '../../core/services/haptic_service.dart';
|
||||
import '../../core/services/tts_service.dart';
|
||||
import '../../core/theme/app_colors.dart';
|
||||
import '../../core/theme/app_decorations.dart';
|
||||
import '../../core/theme/app_text_styles.dart';
|
||||
import '../../shared/widgets/animations/animations.dart';
|
||||
import 'application/sos_cubit.dart';
|
||||
|
||||
Dio get _api => sl<ApiClient>().dio;
|
||||
@@ -252,106 +256,120 @@ class _SosScreenState extends State<SosScreen>
|
||||
: compact
|
||||
? 12.0
|
||||
: 24.0;
|
||||
return SafeArea(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(pagePadding),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// Header
|
||||
Row(
|
||||
return DecoratedBox(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [AppColors.softBlueBg, Colors.white],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: FadeSlideWrapper(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(pagePadding),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'SOS',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.headlineSmall
|
||||
?.copyWith(fontWeight: FontWeight.w800),
|
||||
),
|
||||
const Text(
|
||||
'Emergency alert ke Guardian',
|
||||
style: TextStyle(color: Color(0xFF64748B)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: _loadHistory,
|
||||
icon: const Icon(Icons.refresh),
|
||||
tooltip: 'Refresh riwayat',
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(height: sectionGap),
|
||||
|
||||
// Active SOS banner
|
||||
if (_hasActiveSos)
|
||||
_ActiveSosBanner(
|
||||
event: _events.first, onRefresh: _loadHistory),
|
||||
|
||||
SizedBox(height: sectionGap),
|
||||
|
||||
// SOS Button
|
||||
Center(
|
||||
child: sending
|
||||
? const _SendingIndicator()
|
||||
: AnimatedBuilder(
|
||||
animation: _pulseAnim,
|
||||
builder: (_, child) => Transform.scale(
|
||||
scale: _hasActiveSos ? _pulseAnim.value : 1.0,
|
||||
child: child,
|
||||
),
|
||||
child: _SosButton(
|
||||
active: _hasActiveSos,
|
||||
onPressed: _confirmAndSend,
|
||||
// Header
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'SOS',
|
||||
style: AppTextStyles.heading,
|
||||
),
|
||||
Text(
|
||||
'Emergency alert ke Guardian',
|
||||
style: AppTextStyles.body.copyWith(
|
||||
color: AppColors.textMuted,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// Hint text
|
||||
Text(
|
||||
_hasActiveSos
|
||||
? 'SOS aktif — Guardian sudah mendapat notifikasi'
|
||||
: 'Tekan tombol untuk kirim SOS darurat ke Guardian',
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: _hasActiveSos
|
||||
? const Color(0xFFDC2626)
|
||||
: const Color(0xFF64748B),
|
||||
fontWeight:
|
||||
_hasActiveSos ? FontWeight.w700 : FontWeight.normal,
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: sectionGap),
|
||||
|
||||
// History section
|
||||
if (!landscapeTight) ...[
|
||||
const Text(
|
||||
'Riwayat SOS',
|
||||
style: TextStyle(fontWeight: FontWeight.w800, fontSize: 16),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Expanded(
|
||||
child: _SosHistory(
|
||||
loading: _historyLoading,
|
||||
error: _historyError,
|
||||
events: _events,
|
||||
onRefresh: _loadHistory,
|
||||
IconButton(
|
||||
onPressed: _loadHistory,
|
||||
icon: const Icon(Icons.refresh),
|
||||
tooltip: 'Refresh riwayat',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
] else
|
||||
const Spacer(),
|
||||
],
|
||||
|
||||
SizedBox(height: sectionGap),
|
||||
|
||||
// Active SOS banner
|
||||
if (_hasActiveSos)
|
||||
_ActiveSosBanner(
|
||||
event: _events.first, onRefresh: _loadHistory),
|
||||
|
||||
SizedBox(height: sectionGap),
|
||||
|
||||
// SOS Button
|
||||
Center(
|
||||
child: sending
|
||||
? const _SendingIndicator()
|
||||
: AnimatedBuilder(
|
||||
animation: _pulseAnim,
|
||||
builder: (_, child) => Transform.scale(
|
||||
scale: _hasActiveSos ? _pulseAnim.value : 1.0,
|
||||
child: child,
|
||||
),
|
||||
child: _SosButton(
|
||||
active: _hasActiveSos,
|
||||
onPressed: _confirmAndSend,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// Hint text
|
||||
Text(
|
||||
_hasActiveSos
|
||||
? 'SOS aktif — Guardian sudah mendapat notifikasi'
|
||||
: 'Tekan tombol untuk kirim SOS darurat ke Guardian',
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: _hasActiveSos
|
||||
? const Color(0xFFDC2626)
|
||||
: const Color(0xFF64748B),
|
||||
fontWeight:
|
||||
_hasActiveSos ? FontWeight.w700 : FontWeight.normal,
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: sectionGap),
|
||||
|
||||
// History section
|
||||
if (!landscapeTight) ...[
|
||||
const Text(
|
||||
'Riwayat SOS',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w800,
|
||||
fontSize: 16,
|
||||
color: AppColors.textDark,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Expanded(
|
||||
child: _SosHistory(
|
||||
loading: _historyLoading,
|
||||
error: _historyError,
|
||||
events: _events,
|
||||
onRefresh: _loadHistory,
|
||||
),
|
||||
),
|
||||
] else
|
||||
const Spacer(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -383,36 +401,52 @@ class _SosButton extends StatelessWidget {
|
||||
: compact
|
||||
? 154.0
|
||||
: 200.0;
|
||||
return SizedBox.square(
|
||||
dimension: dimension,
|
||||
child: FilledButton(
|
||||
style: FilledButton.styleFrom(
|
||||
shape: const CircleBorder(),
|
||||
backgroundColor:
|
||||
active ? const Color(0xFFB91C1C) : const Color(0xFFDC2626),
|
||||
elevation: active ? 12 : 4,
|
||||
shadowColor: const Color(0xFFDC2626).withValues(alpha: 0.5),
|
||||
),
|
||||
onPressed: onPressed,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
active ? Icons.emergency : Icons.emergency_outlined,
|
||||
size: dimension < 150 ? 34 : 48,
|
||||
color: Colors.white,
|
||||
return BounceTap(
|
||||
onTap: onPressed,
|
||||
child: Semantics(
|
||||
button: true,
|
||||
label: 'Kirim SOS',
|
||||
child: Container(
|
||||
width: dimension,
|
||||
height: dimension,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: LinearGradient(
|
||||
colors: active
|
||||
? const [Color(0xFFDC2626), Color(0xFF991B1B)]
|
||||
: const [Color(0xFFFF5A5A), Color(0xFFDC2626)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
SizedBox(height: dimension < 150 ? 3 : 6),
|
||||
Text(
|
||||
'SOS',
|
||||
style: TextStyle(
|
||||
fontSize: dimension < 150 ? 28 : 38,
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Colors.white,
|
||||
letterSpacing: 2,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColors.danger.withValues(alpha: active ? 0.34 : 0.22),
|
||||
blurRadius: active ? 28 : 18,
|
||||
offset: const Offset(0, 10),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
active ? Icons.emergency : Icons.emergency_outlined,
|
||||
size: dimension < 150 ? 34 : 48,
|
||||
color: Colors.white,
|
||||
),
|
||||
SizedBox(height: dimension < 150 ? 3 : 6),
|
||||
Text(
|
||||
'SOS',
|
||||
style: TextStyle(
|
||||
fontSize: dimension < 150 ? 28 : 38,
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Colors.white,
|
||||
letterSpacing: 2,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -439,6 +473,7 @@ class _SendingIndicator extends StatelessWidget {
|
||||
color: const Color(0xFFDC2626).withValues(alpha: 0.15),
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(color: const Color(0xFFDC2626), width: 3),
|
||||
boxShadow: AppDecorations.cardShadow,
|
||||
),
|
||||
child: const Center(
|
||||
child: Column(
|
||||
@@ -470,8 +505,9 @@ class _ActiveSosBanner extends StatelessWidget {
|
||||
padding: const EdgeInsets.all(14),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFEE2E2),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderRadius: AppDecorations.cardRadius,
|
||||
border: Border.all(color: const Color(0xFFFCA5A5), width: 1.5),
|
||||
boxShadow: AppDecorations.cardShadow,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
@@ -531,10 +567,21 @@ class _SosHistory extends StatelessWidget {
|
||||
if (events.isEmpty) {
|
||||
return _HistoryEmpty(onRefresh: onRefresh);
|
||||
}
|
||||
return ListView.separated(
|
||||
itemCount: events.length,
|
||||
separatorBuilder: (_, __) => const SizedBox(height: 8),
|
||||
itemBuilder: (_, i) => _SosEventTile(event: events[i]),
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async => onRefresh(),
|
||||
child: ListView(
|
||||
children: [
|
||||
StaggerWrapper(
|
||||
children: [
|
||||
for (final event in events)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: _SosEventTile(event: event),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -570,8 +617,9 @@ class _SosEventTile extends StatelessWidget {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderRadius: AppDecorations.cardRadius,
|
||||
border: Border.all(color: const Color(0xFFE2E8F0)),
|
||||
boxShadow: AppDecorations.cardShadow,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
@@ -662,9 +710,10 @@ class _HistoryEmpty extends StatelessWidget {
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(24),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF8FAFC),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: AppColors.cardWhite,
|
||||
borderRadius: AppDecorations.cardRadius,
|
||||
border: Border.all(color: const Color(0xFFE2E8F0)),
|
||||
boxShadow: AppDecorations.cardShadow,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
|
||||
Reference in New Issue
Block a user