Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions docs/LOGGING_IMPLEMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,11 @@ Implementation of a comprehensive logging system for MostroP2P mobile app with i
- Folder picker and permissions
- Clear logs with confirmation dialog

### Phase 6: UI Enhancements
- Recording indicator widget
- Statistics and controls
### Phase 6: UI Enhancements (Completed)
- Recording indicator widget (floating red dot, bottom-left)
- Tap to navigate to logs screen
- Only visible when logging is enabled
- Proper lifecycle management

### Phase 7: Remaining Migrations
- 25+ files with logs
Expand Down Expand Up @@ -207,6 +209,6 @@ void backgroundMain(SendPort sendPort) async {

---

**Version**: 6
**Status**: Phase 5 - Completed
**Last Updated**: 2026-01-12
**Version**: 7
**Status**: Phase 6 - Completed
**Last Updated**: 2026-01-21
8 changes: 7 additions & 1 deletion lib/core/app_routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import 'package:mostro_mobile/features/disputes/screens/dispute_chat_screen.dart

import 'package:mostro_mobile/features/notifications/screens/notifications_screen.dart';
import 'package:mostro_mobile/features/logs/screens/logs_screen.dart';
import 'package:mostro_mobile/features/logs/widgets/logs_recording_indicator.dart';

import 'package:mostro_mobile/features/walkthrough/providers/first_run_provider.dart';
import 'package:mostro_mobile/shared/widgets/navigation_listener_widget.dart';
Expand Down Expand Up @@ -88,7 +89,12 @@ GoRouter createRouter(WidgetRef ref) {
builder: (BuildContext context, GoRouterState state, Widget child) {
return NotificationListenerWidget(
child: NavigationListenerWidget(
child: child,
child: Stack(
children: [
child,
const LogsRecordingIndicator(),
],
),
),
);
},
Expand Down
121 changes: 121 additions & 0 deletions lib/features/logs/widgets/logs_recording_indicator.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:mostro_mobile/core/app_theme.dart';
import 'package:mostro_mobile/features/settings/settings_provider.dart';
import 'package:mostro_mobile/shared/providers/drawer_provider.dart';

class LogsRecordingIndicator extends ConsumerStatefulWidget {
const LogsRecordingIndicator({super.key});

@override
ConsumerState<LogsRecordingIndicator> createState() =>
_LogsRecordingIndicatorState();
}

class _LogsRecordingIndicatorState
extends ConsumerState<LogsRecordingIndicator>
with SingleTickerProviderStateMixin {
late AnimationController _animationController;
late Animation<double> _pulseAnimation;

@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: const Duration(milliseconds: 1500),
vsync: this,
)..repeat(reverse: true);

_pulseAnimation = Tween<double>(begin: 0.6, end: 1.0).animate(
CurvedAnimation(
parent: _animationController,
curve: Curves.easeInOut,
),
);
}

@override
void dispose() {
_animationController.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
final settings = ref.watch(settingsProvider);
final isLoggingEnabled = settings.isLoggingEnabled;

if (!isLoggingEnabled) {
return const SizedBox.shrink();
}

final currentRoute = GoRouterState.of(context).uri.path;
final isOnLogsScreen = currentRoute == '/logs';

if (isOnLogsScreen) {
return const SizedBox.shrink();
}

final isDrawerOpen = ref.watch(drawerProvider);
final hasBottomNavBar = currentRoute == '/' ||
currentRoute == '/order_book' ||
currentRoute == '/chat_list';

final bottomOffset = hasBottomNavBar && !isDrawerOpen ? 90.0 : 16.0;


return AnimatedPositioned(
duration: const Duration(milliseconds: 1000),
curve: Curves.easeInOut,
bottom: MediaQuery.of(context).padding.bottom + bottomOffset,
left: 16,
child: GestureDetector(
onTap: () => context.push('/logs'),
child: Material(
color: Colors.transparent,
child: Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: AppTheme.backgroundCard.withValues(alpha: 0.95),
shape: BoxShape.circle,
border: Border.all(
color: AppTheme.statusError.withValues(alpha: 0.3),
width: 1.5,
),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.3),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: AnimatedBuilder(
animation: _pulseAnimation,
builder: (context, child) {
return Container(
width: 10,
height: 10,
decoration: BoxDecoration(
color: AppTheme.statusError
.withValues(alpha: _pulseAnimation.value),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: AppTheme.statusError
.withValues(alpha: _pulseAnimation.value * 0.5),
blurRadius: 6,
spreadRadius: 2,
),
],
),
);
},
),
),
),
),
);
}
}