Files
meshcore-sar/lib/widgets/map/map_message_overlay.dart
Janez T fa2b586ab7 i18n: Add 247 missing translation keys across all 13 languages
Replace hardcoded English strings in 32 Dart source files with l10n
references. Covers settings labels, UI actions, map/SAR features,
device config, notifications, profiles, spectrum scan, and status
messages for sl, de, hr, pl, es, fr, it, el, ru, tr, uk, zh, pt.
2026-03-17 10:36:58 +01:00

147 lines
4.2 KiB
Dart

import 'package:flutter/material.dart';
import '../../models/message.dart';
import '../../l10n/app_localizations.dart';
import '../messages/message_bubble.dart';
/// Message overlay widget for displaying recent messages on the map
/// Only shown in fullscreen mode on large screens (>= 800px width)
class MapMessageOverlay extends StatefulWidget {
final List<Message> messages;
final VoidCallback? onNavigateToMessages;
final Function(String messageId)? onMessageTap;
const MapMessageOverlay({
super.key,
required this.messages,
this.onNavigateToMessages,
this.onMessageTap,
});
@override
State<MapMessageOverlay> createState() => _MapMessageOverlayState();
}
class _MapMessageOverlayState extends State<MapMessageOverlay> {
final ScrollController _scrollController = ScrollController();
@override
void initState() {
super.initState();
// Scroll to bottom on initial build
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollToBottom(animate: false);
});
}
@override
void didUpdateWidget(MapMessageOverlay oldWidget) {
super.didUpdateWidget(oldWidget);
// Auto-scroll to bottom when new messages arrive
if (widget.messages.length > oldWidget.messages.length) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollToBottom(animate: true);
});
}
}
void _scrollToBottom({bool animate = true}) {
if (_scrollController.hasClients) {
if (animate) {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 300),
curve: Curves.easeOut,
);
} else {
_scrollController.jumpTo(_scrollController.position.maxScrollExtent);
}
}
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (widget.messages.isEmpty) {
return const SizedBox.shrink();
}
return Container(
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.75),
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.3),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Column(
children: [
// Header
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.3),
borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
),
child: Row(
children: [
const Icon(
Icons.message,
color: Colors.white,
size: 20,
),
SizedBox(width: 8),
Expanded(
child: Text(
AppLocalizations.of(context)!.recentMessages,
style: const TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
),
Text(
'${widget.messages.length}',
style: TextStyle(
color: Colors.white.withValues(alpha: 0.7),
fontSize: 12,
),
),
],
),
),
// Message list
Expanded(
child: ListView.separated(
controller: _scrollController,
padding: const EdgeInsets.all(8),
itemCount: widget.messages.length,
separatorBuilder: (context, index) => const SizedBox(height: 4),
itemBuilder: (context, index) {
final message = widget.messages[index];
return MessageBubble(
message: message,
isCompact: true,
onTap: () {
widget.onMessageTap?.call(message.id);
},
);
},
),
),
],
),
);
}
}