clear toast on tap

this adds a generator showDismissibleSnackBar which by default allows
tapping to clear snack bar toasts. all SnackBar properties are still
available and the

all callers should now use showDismissibleSnackBar() instead of calling
ScaffoldMessenger.of(context).showSnackBar(SnackBar())
chore/offband-rebrand
Enot (ded) Skelly 3 months ago
parent 0757c8e53a
commit aa2d0f1927
No known key found for this signature in database
GPG Key ID: 2FE5B19B03656304

@ -3,6 +3,7 @@ import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import '../l10n/l10n.dart'; import '../l10n/l10n.dart';
import '../utils/platform_info.dart'; import '../utils/platform_info.dart';
import '../helpers/snack_bar_builder.dart';
class LinkHandler { class LinkHandler {
static TextStyle defaultLinkStyle(BuildContext context, TextStyle base) { static TextStyle defaultLinkStyle(BuildContext context, TextStyle base) {
@ -93,21 +94,19 @@ class LinkHandler {
final uri = Uri.parse(url); final uri = Uri.parse(url);
if (!await launchUrl(uri, mode: LaunchMode.externalApplication)) { if (!await launchUrl(uri, mode: LaunchMode.externalApplication)) {
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.chat_couldNotOpenLink(url)), content: Text(context.l10n.chat_couldNotOpenLink(url)),
backgroundColor: Colors.red, backgroundColor: Colors.red,
),
); );
} }
} }
} catch (e) { } catch (e) {
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.chat_invalidLink), content: Text(context.l10n.chat_invalidLink),
backgroundColor: Colors.red, backgroundColor: Colors.red,
),
); );
} }
} }

@ -0,0 +1,56 @@
import 'package:flutter/material.dart';
// showDismissibleSnackBar shows a [SnackBar] with tap to dismiss
// all other properties are default and optional
void showDismissibleSnackBar(
BuildContext context, {
Key? key,
required Widget content,
Color? backgroundColor,
double? elevation,
EdgeInsetsGeometry? margin,
EdgeInsetsGeometry? padding,
double? width,
ShapeBorder? shape,
HitTestBehavior? hitTestBehavior,
SnackBarBehavior? behavior,
SnackBarAction? action,
double? actionOverflowThreshold,
bool? showCloseIcon,
Color? closeIconColor,
Duration? duration,
bool? persist,
Animation<double>? animation,
void Function()? onVisible,
DismissDirection? dismissDirection,
Clip? clipBehavior,
}) {
final messenger = ScaffoldMessenger.of(context);
messenger.showSnackBar(
SnackBar(
key: key,
content: GestureDetector(
onTap: () => messenger.hideCurrentSnackBar(),
child: content,
),
backgroundColor: backgroundColor,
elevation: elevation,
margin: margin,
padding: padding,
width: width,
shape: shape,
hitTestBehavior: hitTestBehavior,
behavior: behavior,
action: action,
actionOverflowThreshold: actionOverflowThreshold,
showCloseIcon: showCloseIcon,
closeIconColor: closeIconColor,
duration: duration ?? const Duration(seconds: 4),
persist: persist,
animation: animation,
onVisible: onVisible,
dismissDirection: dismissDirection ?? DismissDirection.down,
clipBehavior: clipBehavior ?? Clip.hardEdge,
),
);
}

@ -5,6 +5,7 @@ import 'package:provider/provider.dart';
import '../l10n/l10n.dart'; import '../l10n/l10n.dart';
import '../services/app_debug_log_service.dart'; import '../services/app_debug_log_service.dart';
import '../widgets/adaptive_app_bar_title.dart'; import '../widgets/adaptive_app_bar_title.dart';
import '../helpers/snack_bar_builder.dart';
class AppDebugLogScreen extends StatelessWidget { class AppDebugLogScreen extends StatelessWidget {
const AppDebugLogScreen({super.key}); const AppDebugLogScreen({super.key});
@ -34,8 +35,9 @@ class AppDebugLogScreen extends StatelessWidget {
.join('\n'); .join('\n');
await Clipboard.setData(ClipboardData(text: text)); await Clipboard.setData(ClipboardData(text: text));
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(context.l10n.debugLog_copied)), context,
content: Text(context.l10n.debugLog_copied),
); );
} }
: null, : null,

@ -10,6 +10,7 @@ import '../services/app_settings_service.dart';
import '../services/notification_service.dart'; import '../services/notification_service.dart';
import '../services/translation_service.dart'; import '../services/translation_service.dart';
import '../widgets/adaptive_app_bar_title.dart'; import '../widgets/adaptive_app_bar_title.dart';
import '../helpers/snack_bar_builder.dart';
import 'map_cache_screen.dart'; import 'map_cache_screen.dart';
class AppSettingsScreen extends StatelessWidget { class AppSettingsScreen extends StatelessWidget {
@ -151,13 +152,12 @@ class AppSettingsScreen extends StatelessWidget {
.requestPermissions(); .requestPermissions();
if (!granted) { if (!granted) {
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text( content: Text(
context.l10n.appSettings_notificationPermissionDenied, context.l10n.appSettings_notificationPermissionDenied,
),
duration: const Duration(seconds: 2),
), ),
duration: const Duration(seconds: 2),
); );
} }
return; return;
@ -166,15 +166,14 @@ class AppSettingsScreen extends StatelessWidget {
await settingsService.setNotificationsEnabled(value); await settingsService.setNotificationsEnabled(value);
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text( content: Text(
value value
? context.l10n.appSettings_notificationsEnabled ? context.l10n.appSettings_notificationsEnabled
: context.l10n.appSettings_notificationsDisabled, : context.l10n.appSettings_notificationsDisabled,
),
duration: const Duration(seconds: 2),
), ),
duration: const Duration(seconds: 2),
); );
} }
}, },
@ -301,15 +300,14 @@ class AppSettingsScreen extends StatelessWidget {
value: settingsService.settings.clearPathOnMaxRetry, value: settingsService.settings.clearPathOnMaxRetry,
onChanged: (value) { onChanged: (value) {
settingsService.setClearPathOnMaxRetry(value); settingsService.setClearPathOnMaxRetry(value);
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text( content: Text(
value value
? context.l10n.appSettings_pathsWillBeCleared ? context.l10n.appSettings_pathsWillBeCleared
: context.l10n.appSettings_pathsWillNotBeCleared, : context.l10n.appSettings_pathsWillNotBeCleared,
),
duration: const Duration(seconds: 2),
), ),
duration: const Duration(seconds: 2),
); );
}, },
), ),
@ -329,15 +327,14 @@ class AppSettingsScreen extends StatelessWidget {
value: settingsService.settings.autoRouteRotationEnabled, value: settingsService.settings.autoRouteRotationEnabled,
onChanged: (value) { onChanged: (value) {
settingsService.setAutoRouteRotationEnabled(value); settingsService.setAutoRouteRotationEnabled(value);
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text( content: Text(
value value
? context.l10n.appSettings_autoRouteRotationEnabled ? context.l10n.appSettings_autoRouteRotationEnabled
: context.l10n.appSettings_autoRouteRotationDisabled, : context.l10n.appSettings_autoRouteRotationDisabled,
),
duration: const Duration(seconds: 2),
), ),
duration: const Duration(seconds: 2),
); );
}, },
), ),
@ -1164,8 +1161,9 @@ class AppSettingsScreen extends StatelessWidget {
String? id, String? id,
}) async { }) async {
if (sourceUrl.isEmpty) { if (sourceUrl.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(context.l10n.translation_enterUrlFirst)), context,
content: Text(context.l10n.translation_enterUrlFirst),
); );
return; return;
} }
@ -1176,22 +1174,23 @@ class AppSettingsScreen extends StatelessWidget {
id: id, id: id,
); );
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(context.l10n.translation_modelDownloaded)), context,
content: Text(context.l10n.translation_modelDownloaded),
); );
await settingsService.setTranslationEnabled(true); await settingsService.setTranslationEnabled(true);
} on TranslationDownloadCancelled { } on TranslationDownloadCancelled {
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(context.l10n.translation_downloadStopped)), context,
content: Text(context.l10n.translation_downloadStopped),
); );
} catch (error) { } catch (error) {
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text( content: Text(
context.l10n.translation_downloadFailed(error.toString()), context.l10n.translation_downloadFailed(error.toString()),
),
), ),
); );
} }
@ -1236,16 +1235,16 @@ class AppSettingsScreen extends StatelessWidget {
try { try {
await translationService.removeModel(model); await translationService.removeModel(model);
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
// TODO: l10n // TODO: l10n
content: Text('Deleted ${translationModelFriendlyName(model)}.'), content: Text('Deleted ${translationModelFriendlyName(model)}.'),
),
); );
} catch (error) { } catch (error) {
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text('Delete failed: $error')), context,
content: Text('Delete failed: $error'),
); // TODO: l10n ); // TODO: l10n
} }
} }
@ -1279,15 +1278,14 @@ class AppSettingsScreen extends StatelessWidget {
onChanged: (value) async { onChanged: (value) async {
await settingsService.setAppDebugLogEnabled(value); await settingsService.setAppDebugLogEnabled(value);
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text( content: Text(
value value
? context.l10n.appSettings_appDebugLoggingEnabled ? context.l10n.appSettings_appDebugLoggingEnabled
: context.l10n.appSettings_appDebugLoggingDisabled, : context.l10n.appSettings_appDebugLoggingDisabled,
),
duration: const Duration(seconds: 2),
), ),
duration: const Duration(seconds: 2),
); );
}, },
), ),

@ -5,6 +5,7 @@ import '../l10n/l10n.dart';
import '../services/ble_debug_log_service.dart'; import '../services/ble_debug_log_service.dart';
import '../connector/meshcore_protocol.dart'; import '../connector/meshcore_protocol.dart';
import '../widgets/adaptive_app_bar_title.dart'; import '../widgets/adaptive_app_bar_title.dart';
import '../helpers/snack_bar_builder.dart';
enum _BleLogView { frames, rawLogRx } enum _BleLogView { frames, rawLogRx }
@ -52,10 +53,9 @@ class _BleDebugLogScreenState extends State<BleDebugLogScreen> {
.join('\n'); .join('\n');
await Clipboard.setData(ClipboardData(text: text)); await Clipboard.setData(ClipboardData(text: text));
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.debugLog_bleCopied), content: Text(context.l10n.debugLog_bleCopied),
),
); );
} }
: null, : null,

@ -14,6 +14,7 @@ import '../connector/meshcore_protocol.dart';
import '../helpers/gif_helper.dart'; import '../helpers/gif_helper.dart';
import '../helpers/reaction_helper.dart'; import '../helpers/reaction_helper.dart';
import '../helpers/utf8_length_limiter.dart'; import '../helpers/utf8_length_limiter.dart';
import '../helpers/snack_bar_builder.dart';
import '../l10n/l10n.dart'; import '../l10n/l10n.dart';
import '../models/channel.dart'; import '../models/channel.dart';
import '../models/channel_message.dart'; import '../models/channel_message.dart';
@ -144,11 +145,10 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
Future<void> _scrollToMessage(String messageId) async { Future<void> _scrollToMessage(String messageId) async {
final key = _messageKeys[messageId]; final key = _messageKeys[messageId];
if (key == null) { if (key == null) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.chat_originalMessageNotFound), content: Text(context.l10n.chat_originalMessageNotFound),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
),
); );
return; return;
} }
@ -1151,9 +1151,10 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
final now = DateTime.now(); final now = DateTime.now();
if (_lastChannelSendAt != null && if (_lastChannelSendAt != null &&
now.difference(_lastChannelSendAt!) < const Duration(seconds: 1)) { now.difference(_lastChannelSendAt!) < const Duration(seconds: 1)) {
ScaffoldMessenger.of( showDismissibleSnackBar(
context, context,
).showSnackBar(SnackBar(content: Text(context.l10n.chat_sendCooldown))); content: Text(context.l10n.chat_sendCooldown),
);
return; return;
} }
_lastChannelSendAt = now; _lastChannelSendAt = now;
@ -1195,8 +1196,9 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
final maxBytes = maxChannelMessageBytes(connector.selfName); final maxBytes = maxChannelMessageBytes(connector.selfName);
if (utf8.encode(messageText).length > maxBytes) { if (utf8.encode(messageText).length > maxBytes) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(context.l10n.chat_messageTooLong(maxBytes))), context,
content: Text(context.l10n.chat_messageTooLong(maxBytes)),
); );
return; return;
} }
@ -1323,17 +1325,19 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
void _copyMessageText(String text) { void _copyMessageText(String text) {
Clipboard.setData(ClipboardData(text: text)); Clipboard.setData(ClipboardData(text: text));
ScaffoldMessenger.of( showDismissibleSnackBar(
context, context,
).showSnackBar(SnackBar(content: Text(context.l10n.chat_messageCopied))); content: Text(context.l10n.chat_messageCopied),
);
} }
Future<void> _deleteMessage(ChannelMessage message) async { Future<void> _deleteMessage(ChannelMessage message) async {
await context.read<MeshCoreConnector>().deleteChannelMessage(message); await context.read<MeshCoreConnector>().deleteChannelMessage(message);
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of( showDismissibleSnackBar(
context, context,
).showSnackBar(SnackBar(content: Text(context.l10n.chat_messageDeleted))); content: Text(context.l10n.chat_messageDeleted),
);
} }
String _formatPathPrefixes(Uint8List pathBytes) { String _formatPathPrefixes(Uint8List pathBytes) {

@ -24,6 +24,7 @@ import '../widgets/empty_state.dart';
import '../widgets/qr_code_display.dart'; import '../widgets/qr_code_display.dart';
import '../widgets/quick_switch_bar.dart'; import '../widgets/quick_switch_bar.dart';
import '../widgets/unread_badge.dart'; import '../widgets/unread_badge.dart';
import '../helpers/snack_bar_builder.dart';
import 'channel_chat_screen.dart'; import 'channel_chat_screen.dart';
import 'community_qr_scanner_screen.dart'; import 'community_qr_scanner_screen.dart';
import 'contacts_screen.dart'; import 'contacts_screen.dart';
@ -809,15 +810,12 @@ class _ChannelsScreenState extends State<ChannelsScreen>
onPressed: () async { onPressed: () async {
final name = nameController.text.trim(); final name = nameController.text.trim();
if (name.isEmpty) { if (name.isEmpty) {
ScaffoldMessenger.of( showDismissibleSnackBar(
dialogContext, context,
).showSnackBar( content: Text(
SnackBar( dialogContext
content: Text( .l10n
dialogContext .channels_enterChannelName,
.l10n
.channels_enterChannelName,
),
), ),
); );
return; return;
@ -837,13 +835,10 @@ class _ChannelsScreenState extends State<ChannelsScreen>
nextIndex, nextIndex,
); );
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text( content: Text(
context.l10n.channels_channelAdded( context.l10n.channels_channelAdded(name),
name,
),
),
), ),
); );
} }
@ -897,15 +892,12 @@ class _ChannelsScreenState extends State<ChannelsScreen>
final name = nameController.text.trim(); final name = nameController.text.trim();
final pskHex = pskController.text.trim(); final pskHex = pskController.text.trim();
if (name.isEmpty) { if (name.isEmpty) {
ScaffoldMessenger.of( showDismissibleSnackBar(
dialogContext, context,
).showSnackBar( content: Text(
SnackBar( dialogContext
content: Text( .l10n
dialogContext .channels_enterChannelName,
.l10n
.channels_enterChannelName,
),
), ),
); );
return; return;
@ -914,15 +906,12 @@ class _ChannelsScreenState extends State<ChannelsScreen>
try { try {
psk = Channel.parsePskHex(pskHex); psk = Channel.parsePskHex(pskHex);
} on FormatException { } on FormatException {
ScaffoldMessenger.of( showDismissibleSnackBar(
dialogContext, context,
).showSnackBar( content: Text(
SnackBar( dialogContext
content: Text( .l10n
dialogContext .channels_pskMustBe32Hex,
.l10n
.channels_pskMustBe32Hex,
),
), ),
); );
return; return;
@ -930,13 +919,10 @@ class _ChannelsScreenState extends State<ChannelsScreen>
Navigator.pop(dialogContext); Navigator.pop(dialogContext);
connector.setChannel(nextIndex, name, psk); connector.setChannel(nextIndex, name, psk);
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text( content: Text(
context.l10n.channels_channelAdded( context.l10n.channels_channelAdded(name),
name,
),
),
), ),
); );
} }
@ -967,11 +953,10 @@ class _ChannelsScreenState extends State<ChannelsScreen>
Navigator.pop(dialogContext); Navigator.pop(dialogContext);
connector.setChannel(nextIndex, 'Public', psk); connector.setChannel(nextIndex, 'Public', psk);
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text( content: Text(
context.l10n.channels_publicChannelAdded, context.l10n.channels_publicChannelAdded,
),
), ),
); );
} }
@ -1097,15 +1082,12 @@ class _ChannelsScreenState extends State<ChannelsScreen>
onPressed: () async { onPressed: () async {
var hashtag = hashtagController.text.trim(); var hashtag = hashtagController.text.trim();
if (hashtag.isEmpty) { if (hashtag.isEmpty) {
ScaffoldMessenger.of( showDismissibleSnackBar(
dialogContext, context,
).showSnackBar( content: Text(
SnackBar( dialogContext
content: Text( .l10n
dialogContext .channels_enterChannelName,
.l10n
.channels_enterChannelName,
),
), ),
); );
return; return;
@ -1125,15 +1107,12 @@ class _ChannelsScreenState extends State<ChannelsScreen>
} else { } else {
// Community hashtag - HMAC derivation from community secret // Community hashtag - HMAC derivation from community secret
if (selectedCommunity == null) { if (selectedCommunity == null) {
ScaffoldMessenger.of( showDismissibleSnackBar(
dialogContext, dialogContext,
).showSnackBar( content: Text(
SnackBar( dialogContext
content: Text( .l10n
dialogContext .community_selectCommunity,
.l10n
.community_selectCommunity,
),
), ),
); );
return; return;
@ -1159,12 +1138,11 @@ class _ChannelsScreenState extends State<ChannelsScreen>
psk, psk,
); );
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text( content: Text(
context.l10n.channels_channelAdded( context.l10n.channels_channelAdded(
channelName, channelName,
),
), ),
), ),
); );
@ -1259,13 +1237,10 @@ class _ChannelsScreenState extends State<ChannelsScreen>
onPressed: () async { onPressed: () async {
final name = nameController.text.trim(); final name = nameController.text.trim();
if (name.isEmpty) { if (name.isEmpty) {
ScaffoldMessenger.of( showDismissibleSnackBar(
dialogContext, context,
).showSnackBar( content: Text(
SnackBar( dialogContext.l10n.community_enterName,
content: Text(
dialogContext.l10n.community_enterName,
),
), ),
); );
return; return;
@ -1301,11 +1276,10 @@ class _ChannelsScreenState extends State<ChannelsScreen>
_loadCommunities(); _loadCommunities();
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text( content: Text(
context.l10n.community_created(name), context.l10n.community_created(name),
),
), ),
); );
@ -1494,10 +1468,9 @@ class _ChannelsScreenState extends State<ChannelsScreen>
try { try {
psk = Channel.parsePskHex(pskHex); psk = Channel.parsePskHex(pskHex);
} on FormatException { } on FormatException {
ScaffoldMessenger.of(dialogContext).showSnackBar( showDismissibleSnackBar(
SnackBar( dialogContext,
content: Text(dialogContext.l10n.channels_pskMustBe32Hex), content: Text(dialogContext.l10n.channels_pskMustBe32Hex),
),
); );
return; return;
} }
@ -1510,16 +1483,16 @@ class _ChannelsScreenState extends State<ChannelsScreen>
smazEnabled, smazEnabled,
); );
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.channels_channelUpdated(name)), content: Text(context.l10n.channels_channelUpdated(name)),
),
); );
} catch (e, st) { } catch (e, st) {
debugPrint(st.toString()); debugPrint(st.toString());
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text('Failed to update channel: $e')), context,
content: Text('Failed to update channel: $e'),
); );
} }
}, },
@ -1559,21 +1532,19 @@ class _ChannelsScreenState extends State<ChannelsScreen>
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text( content: Text(
context.l10n.channels_channelDeleted(channel.name), context.l10n.channels_channelDeleted(channel.name),
),
), ),
); );
} catch (e, st) { } catch (e, st) {
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text( content: Text(
context.l10n.channels_channelDeleteFailed(channel.name), context.l10n.channels_channelDeleteFailed(channel.name),
),
), ),
); );
@ -1594,8 +1565,9 @@ class _ChannelsScreenState extends State<ChannelsScreen>
void _addPublicChannel(BuildContext context, MeshCoreConnector connector) { void _addPublicChannel(BuildContext context, MeshCoreConnector connector) {
final psk = Channel.parsePskHex(Channel.publicChannelPsk); final psk = Channel.parsePskHex(Channel.publicChannelPsk);
connector.setChannel(0, 'Public', psk); connector.setChannel(0, 'Public', psk);
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(context.l10n.channels_publicChannelAdded)), context,
content: Text(context.l10n.channels_publicChannelAdded),
); );
} }
@ -1810,12 +1782,9 @@ class _ChannelsScreenState extends State<ChannelsScreen>
_loadCommunities(); _loadCommunities();
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text( content: Text(context.l10n.community_deleted(community.name)),
context.l10n.community_deleted(community.name),
),
),
); );
} }
}, },

@ -43,6 +43,7 @@ import '../widgets/radio_stats_entry.dart';
import '../widgets/translated_message_content.dart'; import '../widgets/translated_message_content.dart';
import '../utils/app_logger.dart'; import '../utils/app_logger.dart';
import '../l10n/l10n.dart'; import '../l10n/l10n.dart';
import '../helpers/snack_bar_builder.dart';
import 'telemetry_screen.dart'; import 'telemetry_screen.dart';
class ChatScreen extends StatefulWidget { class ChatScreen extends StatefulWidget {
@ -633,9 +634,10 @@ class _ChatScreenState extends State<ChatScreen> {
final now = DateTime.now(); final now = DateTime.now();
if (_lastTextSendAt != null && if (_lastTextSendAt != null &&
now.difference(_lastTextSendAt!) < const Duration(seconds: 1)) { now.difference(_lastTextSendAt!) < const Duration(seconds: 1)) {
ScaffoldMessenger.of( showDismissibleSnackBar(
context, context,
).showSnackBar(SnackBar(content: Text(context.l10n.chat_sendCooldown))); content: Text(context.l10n.chat_sendCooldown),
);
return; return;
} }
_lastTextSendAt = now; _lastTextSendAt = now;
@ -671,8 +673,9 @@ class _ChatScreenState extends State<ChatScreen> {
} }
final maxBytes = maxContactMessageBytes(); final maxBytes = maxContactMessageBytes();
if (utf8.encode(outgoingText).length > maxBytes) { if (utf8.encode(outgoingText).length > maxBytes) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(context.l10n.chat_messageTooLong(maxBytes))), context,
content: Text(context.l10n.chat_messageTooLong(maxBytes)),
); );
return; return;
} }
@ -860,15 +863,12 @@ class _ChatScreenState extends State<ChatScreen> {
_showFullPathDialog(context, path.pathBytes), _showFullPathDialog(context, path.pathBytes),
onTap: () async { onTap: () async {
if (path.pathBytes.isEmpty) { if (path.pathBytes.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text( content: Text(
context context.l10n.chat_pathDetailsNotAvailable,
.l10n
.chat_pathDetailsNotAvailable,
),
duration: const Duration(seconds: 2),
), ),
duration: const Duration(seconds: 2),
); );
return; return;
} }
@ -952,11 +952,10 @@ class _ChatScreenState extends State<ChatScreen> {
_resolveContact(connector), _resolveContact(connector),
); );
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.chat_pathCleared), content: Text(context.l10n.chat_pathCleared),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
),
); );
Navigator.pop(context); Navigator.pop(context);
}, },
@ -982,11 +981,10 @@ class _ChatScreenState extends State<ChatScreen> {
pathLen: -1, pathLen: -1,
); );
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.chat_floodModeEnabled), content: Text(context.l10n.chat_floodModeEnabled),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
),
); );
Navigator.pop(context); Navigator.pop(context);
}, },
@ -1020,11 +1018,10 @@ class _ChatScreenState extends State<ChatScreen> {
void _showFullPathDialog(BuildContext context, List<int> pathBytes) { void _showFullPathDialog(BuildContext context, List<int> pathBytes) {
if (pathBytes.isEmpty) { if (pathBytes.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.chat_pathDetailsNotAvailable), content: Text(context.l10n.chat_pathDetailsNotAvailable),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
),
); );
return; return;
} }
@ -1137,11 +1134,10 @@ class _ChatScreenState extends State<ChatScreen> {
: (verified : (verified
? context.l10n.chat_pathDeviceConfirmed ? context.l10n.chat_pathDeviceConfirmed
: context.l10n.chat_pathDeviceNotConfirmed); : context.l10n.chat_pathDeviceNotConfirmed);
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.chat_pathSetHops(hopCount, status)), content: Text(context.l10n.chat_pathSetHops(hopCount, status)),
duration: const Duration(seconds: 3), duration: const Duration(seconds: 3),
),
); );
} }
@ -1490,26 +1486,29 @@ class _ChatScreenState extends State<ChatScreen> {
void _copyMessageText(String text) { void _copyMessageText(String text) {
Clipboard.setData(ClipboardData(text: text)); Clipboard.setData(ClipboardData(text: text));
ScaffoldMessenger.of( showDismissibleSnackBar(
context, context,
).showSnackBar(SnackBar(content: Text(context.l10n.chat_messageCopied))); content: Text(context.l10n.chat_messageCopied),
);
} }
Future<void> _deleteMessage(Message message) async { Future<void> _deleteMessage(Message message) async {
await context.read<MeshCoreConnector>().deleteMessage(message); await context.read<MeshCoreConnector>().deleteMessage(message);
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of( showDismissibleSnackBar(
context, context,
).showSnackBar(SnackBar(content: Text(context.l10n.chat_messageDeleted))); content: Text(context.l10n.chat_messageDeleted),
);
} }
void _retryMessage(Message message) { void _retryMessage(Message message) {
final connector = Provider.of<MeshCoreConnector>(context, listen: false); final connector = Provider.of<MeshCoreConnector>(context, listen: false);
// Retry using the contact's current path override setting // Retry using the contact's current path override setting
connector.sendMessage(_resolveContact(connector), message.text); connector.sendMessage(_resolveContact(connector), message.text);
ScaffoldMessenger.of( showDismissibleSnackBar(
context, context,
).showSnackBar(SnackBar(content: Text(context.l10n.chat_retryingMessage))); content: Text(context.l10n.chat_retryingMessage),
);
} }
void _showEmojiPicker(Message message, Contact senderContact) { void _showEmojiPicker(Message message, Contact senderContact) {

@ -8,6 +8,7 @@ import '../models/community.dart';
import '../storage/community_store.dart'; import '../storage/community_store.dart';
import '../widgets/adaptive_app_bar_title.dart'; import '../widgets/adaptive_app_bar_title.dart';
import '../widgets/qr_scanner_widget.dart'; import '../widgets/qr_scanner_widget.dart';
import '../helpers/snack_bar_builder.dart';
/// Screen for scanning community QR codes to join communities. /// Screen for scanning community QR codes to join communities.
/// ///
@ -76,11 +77,10 @@ class _CommunityQrScannerScreenState extends State<CommunityQrScannerScreen> {
} }
} catch (e) { } catch (e) {
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.community_invalidQrCode), content: Text(context.l10n.community_invalidQrCode),
backgroundColor: Colors.red, backgroundColor: Colors.red,
),
); );
} }
} finally { } finally {
@ -93,12 +93,11 @@ class _CommunityQrScannerScreenState extends State<CommunityQrScannerScreen> {
} }
void _showInvalidQrError(BuildContext context) { void _showInvalidQrError(BuildContext context) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.community_invalidQrCode), content: Text(context.l10n.community_invalidQrCode),
backgroundColor: Colors.orange, backgroundColor: Colors.orange,
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
),
); );
} }
@ -229,11 +228,10 @@ class _CommunityQrScannerScreenState extends State<CommunityQrScannerScreen> {
} }
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.community_joined(community.name)), content: Text(context.l10n.community_joined(community.name)),
backgroundColor: Colors.green, backgroundColor: Colors.green,
),
); );
// Return to previous screen // Return to previous screen

@ -27,6 +27,7 @@ import '../widgets/quick_switch_bar.dart';
import '../widgets/repeater_login_dialog.dart'; import '../widgets/repeater_login_dialog.dart';
import '../widgets/room_login_dialog.dart'; import '../widgets/room_login_dialog.dart';
import '../widgets/unread_badge.dart'; import '../widgets/unread_badge.dart';
import '../helpers/snack_bar_builder.dart';
import 'channels_screen.dart'; import 'channels_screen.dart';
import 'chat_screen.dart'; import 'chat_screen.dart';
import 'discovery_screen.dart'; import 'discovery_screen.dart';
@ -150,9 +151,10 @@ class _ContactsScreenState extends State<ContactsScreen>
} }
void _showGroupsUnavailableMessage(BuildContext context) { void _showGroupsUnavailableMessage(BuildContext context) {
ScaffoldMessenger.of( showDismissibleSnackBar(
context, context,
).showSnackBar(SnackBar(content: Text(context.l10n.common_loading))); content: Text(context.l10n.common_loading),
);
} }
void _setupFrameListener() { void _setupFrameListener() {
@ -169,10 +171,9 @@ class _ContactsScreenState extends State<ContactsScreen>
// Validate packet has expected minimum size (98+ bytes per protocol) // Validate packet has expected minimum size (98+ bytes per protocol)
if (advertPacket.length < 98) { if (advertPacket.length < 98) {
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.contacts_invalidAdvertFormat), content: Text(context.l10n.contacts_invalidAdvertFormat),
),
); );
} }
_pendingOperations.remove(ContactOperationType.export); _pendingOperations.remove(ContactOperationType.export);
@ -187,24 +188,23 @@ class _ContactsScreenState extends State<ContactsScreen>
if (!mounted) return; if (!mounted) return;
if (_pendingOperations.contains(ContactOperationType.import)) { if (_pendingOperations.contains(ContactOperationType.import)) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(context.l10n.contacts_contactImported)), context,
content: Text(context.l10n.contacts_contactImported),
); );
} }
if (_pendingOperations.contains(ContactOperationType.zeroHopShare)) { if (_pendingOperations.contains(ContactOperationType.zeroHopShare)) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.contacts_zeroHopContactAdvertSent), content: Text(context.l10n.contacts_zeroHopContactAdvertSent),
),
); );
} }
if (_pendingOperations.contains(ContactOperationType.export)) { if (_pendingOperations.contains(ContactOperationType.export)) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.contacts_contactAdvertCopied), content: Text(context.l10n.contacts_contactAdvertCopied),
),
); );
} }
@ -216,25 +216,22 @@ class _ContactsScreenState extends State<ContactsScreen>
if (!mounted) return; if (!mounted) return;
if (_pendingOperations.contains(ContactOperationType.import)) { if (_pendingOperations.contains(ContactOperationType.import)) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.contacts_contactImportFailed), content: Text(context.l10n.contacts_contactImportFailed),
),
); );
} }
if (_pendingOperations.contains(ContactOperationType.zeroHopShare)) { if (_pendingOperations.contains(ContactOperationType.zeroHopShare)) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.contacts_zeroHopContactAdvertFailed), content: Text(context.l10n.contacts_zeroHopContactAdvertFailed),
),
); );
} }
if (_pendingOperations.contains(ContactOperationType.export)) { if (_pendingOperations.contains(ContactOperationType.export)) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.contacts_contactAdvertCopyFailed), content: Text(context.l10n.contacts_contactAdvertCopyFailed),
),
); );
} }
@ -271,8 +268,9 @@ class _ContactsScreenState extends State<ContactsScreen>
final clipboardData = await Clipboard.getData('text/plain'); final clipboardData = await Clipboard.getData('text/plain');
if (clipboardData == null || clipboardData.text == null) { if (clipboardData == null || clipboardData.text == null) {
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(context.l10n.contacts_clipboardEmpty)), context,
content: Text(context.l10n.contacts_clipboardEmpty),
); );
} }
return; return;
@ -280,8 +278,9 @@ class _ContactsScreenState extends State<ContactsScreen>
final text = clipboardData.text!.trim(); final text = clipboardData.text!.trim();
if (!text.startsWith('meshcore://')) { if (!text.startsWith('meshcore://')) {
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(context.l10n.contacts_invalidAdvertFormat)), context,
content: Text(context.l10n.contacts_invalidAdvertFormat),
); );
} }
return; return;
@ -294,8 +293,9 @@ class _ContactsScreenState extends State<ContactsScreen>
connector.importContact(importContactFrame); connector.importContact(importContactFrame);
} catch (e) { } catch (e) {
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(context.l10n.contacts_invalidAdvertFormat)), context,
content: Text(context.l10n.contacts_invalidAdvertFormat),
); );
} }
} }
@ -330,10 +330,9 @@ class _ContactsScreenState extends State<ContactsScreen>
), ),
onTap: () => { onTap: () => {
connector.sendSelfAdvert(flood: false), connector.sendSelfAdvert(flood: false),
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.settings_advertisementSent), content: Text(context.l10n.settings_advertisementSent),
),
), ),
}, },
), ),
@ -347,10 +346,9 @@ class _ContactsScreenState extends State<ContactsScreen>
), ),
onTap: () => { onTap: () => {
connector.sendSelfAdvert(flood: true), connector.sendSelfAdvert(flood: true),
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.settings_advertisementSent), content: Text(context.l10n.settings_advertisementSent),
),
), ),
}, },
), ),
@ -1146,19 +1144,17 @@ class _ContactsScreenState extends State<ContactsScreen>
onPressed: () async { onPressed: () async {
final name = nameController.text.trim(); final name = nameController.text.trim();
if (name.isEmpty) { if (name.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.contacts_groupNameRequired), content: Text(context.l10n.contacts_groupNameRequired),
),
); );
return; return;
} }
if (name.toLowerCase() == if (name.toLowerCase() ==
contactsAllGroupsValue.toLowerCase()) { contactsAllGroupsValue.toLowerCase()) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.contacts_groupNameReserved), content: Text(context.l10n.contacts_groupNameReserved),
),
); );
return; return;
} }
@ -1167,11 +1163,10 @@ class _ContactsScreenState extends State<ContactsScreen>
return g.name.toLowerCase() == name.toLowerCase(); return g.name.toLowerCase() == name.toLowerCase();
}); });
if (exists) { if (exists) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text( content: Text(
context.l10n.contacts_groupAlreadyExists(name), context.l10n.contacts_groupAlreadyExists(name),
),
), ),
); );
return; return;

@ -12,6 +12,7 @@ import '../utils/contact_search.dart';
import '../utils/platform_info.dart'; import '../utils/platform_info.dart';
import '../widgets/app_bar.dart'; import '../widgets/app_bar.dart';
import '../widgets/list_filter_widget.dart'; import '../widgets/list_filter_widget.dart';
import '../helpers/snack_bar_builder.dart';
enum DiscoverySortOption { lastSeen, name, type } enum DiscoverySortOption { lastSeen, name, type }
@ -234,8 +235,9 @@ class _DiscoveryScreenState extends State<DiscoveryScreen> {
final hexString = pubKeyToHex(contact.rawPacket!); final hexString = pubKeyToHex(contact.rawPacket!);
Clipboard.setData(ClipboardData(text: "meshcore://$hexString")); Clipboard.setData(ClipboardData(text: "meshcore://$hexString"));
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(context.l10n.contacts_contactAdvertCopied)), context,
content: Text(context.l10n.contacts_contactAdvertCopied),
); );
break; break;
case 'delete_contact': case 'delete_contact':

@ -8,6 +8,7 @@ import '../l10n/l10n.dart';
import '../services/app_settings_service.dart'; import '../services/app_settings_service.dart';
import '../services/map_tile_cache_service.dart'; import '../services/map_tile_cache_service.dart';
import '../widgets/adaptive_app_bar_title.dart'; import '../widgets/adaptive_app_bar_title.dart';
import '../helpers/snack_bar_builder.dart';
class MapCacheScreen extends StatefulWidget { class MapCacheScreen extends StatefulWidget {
const MapCacheScreen({super.key}); const MapCacheScreen({super.key});
@ -112,15 +113,17 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
Future<void> _startDownload() async { Future<void> _startDownload() async {
final bounds = _selectedBounds; final bounds = _selectedBounds;
if (bounds == null) { if (bounds == null) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(context.l10n.mapCache_selectAreaFirst)), context,
content: Text(context.l10n.mapCache_selectAreaFirst),
); );
return; return;
} }
if (_estimatedTiles == 0) { if (_estimatedTiles == 0) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(context.l10n.mapCache_noTilesToDownload)), context,
content: Text(context.l10n.mapCache_noTilesToDownload),
); );
return; return;
} }
@ -182,9 +185,7 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
result.failed, result.failed,
) )
: context.l10n.mapCache_cachedTiles(result.downloaded); : context.l10n.mapCache_cachedTiles(result.downloaded);
ScaffoldMessenger.of( showDismissibleSnackBar(context, content: Text(message));
context,
).showSnackBar(SnackBar(content: Text(message)));
} }
Future<void> _clearCache() async { Future<void> _clearCache() async {
@ -210,8 +211,9 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
final cacheService = context.read<MapTileCacheService>(); final cacheService = context.read<MapTileCacheService>();
await cacheService.clearCache(); await cacheService.clearCache();
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(context.l10n.mapCache_offlineCacheCleared)), context,
content: Text(context.l10n.mapCache_offlineCacheCleared),
); );
} }

@ -29,6 +29,7 @@ import 'chat_screen.dart';
import 'contacts_screen.dart'; import 'contacts_screen.dart';
import '../widgets/repeater_login_dialog.dart'; import '../widgets/repeater_login_dialog.dart';
import '../widgets/room_login_dialog.dart'; import '../widgets/room_login_dialog.dart';
import '../helpers/snack_bar_builder.dart';
import 'repeater_hub_screen.dart'; import 'repeater_hub_screen.dart';
import 'settings_screen.dart'; import 'settings_screen.dart';
import 'line_of_sight_map_screen.dart'; import 'line_of_sight_map_screen.dart';
@ -1659,7 +1660,10 @@ class _MapScreenState extends State<MapScreen> {
); );
await connector.refreshDeviceInfo(); await connector.refreshDeviceInfo();
if (!mounted) return; if (!mounted) return;
messenger.showSnackBar(SnackBar(content: Text(successMsg))); showDismissibleSnackBar(
messenger.context,
content: Text(successMsg),
);
}, },
), ),
ListTile( ListTile(
@ -1681,8 +1685,9 @@ class _MapScreenState extends State<MapScreen> {
required String flags, required String flags,
}) async { }) async {
if (!connector.isConnected) { if (!connector.isConnected) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(context.l10n.map_connectToShareMarkers)), context,
content: Text(context.l10n.map_connectToShareMarkers),
); );
return; return;
} }
@ -2271,8 +2276,9 @@ class _MapScreenState extends State<MapScreen> {
_points.clear(); _points.clear();
_polylines.clear(); _polylines.clear();
}); });
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(l10n.map_pathTraceCancelled)), context,
content: Text(l10n.map_pathTraceCancelled),
); );
}, },
tooltip: l10n.common_cancel, tooltip: l10n.common_cancel,

@ -11,6 +11,7 @@ import '../connector/meshcore_protocol.dart';
import '../services/repeater_command_service.dart'; import '../services/repeater_command_service.dart';
import '../widgets/path_management_dialog.dart'; import '../widgets/path_management_dialog.dart';
import '../widgets/snr_indicator.dart'; import '../widgets/snr_indicator.dart';
import '../helpers/snack_bar_builder.dart';
class NeighborsScreen extends StatefulWidget { class NeighborsScreen extends StatefulWidget {
final Contact repeater; final Contact repeater;
@ -163,11 +164,10 @@ class _NeighborsScreenState extends State<NeighborsScreen> {
_neighborCount = neighborCount; _neighborCount = neighborCount;
}); });
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.neighbors_receivedData), content: Text(context.l10n.neighbors_receivedData),
backgroundColor: Colors.green, backgroundColor: Colors.green,
),
); );
_statusTimeout?.cancel(); _statusTimeout?.cancel();
if (!mounted) return; if (!mounted) return;
@ -224,11 +224,10 @@ class _NeighborsScreenState extends State<NeighborsScreen> {
_isLoading = false; _isLoading = false;
_isLoaded = false; _isLoaded = false;
}); });
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.neighbors_requestTimedOut), content: Text(context.l10n.neighbors_requestTimedOut),
backgroundColor: Colors.red, backgroundColor: Colors.red,
),
); );
_recordStatusResult(false); _recordStatusResult(false);
}); });
@ -239,11 +238,10 @@ class _NeighborsScreenState extends State<NeighborsScreen> {
_isLoaded = false; _isLoaded = false;
}); });
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.neighbors_errorLoading(e.toString())), content: Text(context.l10n.neighbors_errorLoading(e.toString())),
backgroundColor: Colors.red, backgroundColor: Colors.red,
),
); );
} }
} }

@ -9,6 +9,7 @@ import '../connector/meshcore_protocol.dart';
import '../widgets/debug_frame_viewer.dart'; import '../widgets/debug_frame_viewer.dart';
import '../services/repeater_command_service.dart'; import '../services/repeater_command_service.dart';
import '../widgets/path_management_dialog.dart'; import '../widgets/path_management_dialog.dart';
import '../helpers/snack_bar_builder.dart';
class RepeaterCliScreen extends StatefulWidget { class RepeaterCliScreen extends StatefulWidget {
final Contact repeater; final Contact repeater;
@ -336,8 +337,9 @@ class _RepeaterCliScreenState extends State<RepeaterCliScreen> {
if (_commandController.text.trim().isNotEmpty) { if (_commandController.text.trim().isNotEmpty) {
_sendCommand(showDebug: true); _sendCommand(showDebug: true);
} else { } else {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(l10n.repeater_enterCommandFirst)), context,
content: Text(l10n.repeater_enterCommandFirst),
); );
} }
}, },

@ -10,6 +10,7 @@ import '../services/app_debug_log_service.dart';
import '../services/repeater_command_service.dart'; import '../services/repeater_command_service.dart';
import '../services/storage_service.dart'; import '../services/storage_service.dart';
import '../widgets/path_management_dialog.dart'; import '../widgets/path_management_dialog.dart';
import '../helpers/snack_bar_builder.dart';
class RepeaterSettingsScreen extends StatefulWidget { class RepeaterSettingsScreen extends StatefulWidget {
final Contact repeater; final Contact repeater;
@ -468,18 +469,16 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
if (mounted) { if (mounted) {
if (successCount > 0) { if (successCount > 0) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(l10n.repeater_refreshed(label)), content: Text(l10n.repeater_refreshed(label)),
backgroundColor: Colors.green, backgroundColor: Colors.green,
),
); );
} else { } else {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(l10n.repeater_errorRefreshing(label)), content: Text(l10n.repeater_errorRefreshing(label)),
backgroundColor: Colors.red, backgroundColor: Colors.red,
),
); );
} }
@ -666,11 +665,10 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
}); });
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.repeater_settingsSaved), content: Text(context.l10n.repeater_settingsSaved),
backgroundColor: Colors.green, backgroundColor: Colors.green,
),
); );
} }
} catch (e) { } catch (e) {
@ -679,13 +677,12 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
}); });
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text( content: Text(
context.l10n.repeater_errorSavingSettings(e.toString()), context.l10n.repeater_errorSavingSettings(e.toString()),
),
backgroundColor: Colors.red,
), ),
backgroundColor: Colors.red,
); );
} }
} }
@ -1429,9 +1426,10 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
if (command == 'erase') { if (command == 'erase') {
if (mounted) { if (mounted) {
ScaffoldMessenger.of( showDismissibleSnackBar(
context, context,
).showSnackBar(SnackBar(content: Text(l10n.repeater_eraseSerialOnly))); content: Text(l10n.repeater_eraseSerialOnly),
);
} }
return; return;
} }
@ -1453,17 +1451,17 @@ class _RepeaterSettingsScreenState extends State<RepeaterSettingsScreen> {
await connector.sendFrame(frame); await connector.sendFrame(frame);
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(l10n.repeater_commandSent(command))), context,
content: Text(l10n.repeater_commandSent(command)),
); );
} }
} catch (e) { } catch (e) {
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(l10n.repeater_errorSendingCommand(e.toString())), content: Text(l10n.repeater_errorSendingCommand(e.toString())),
backgroundColor: Colors.red, backgroundColor: Colors.red,
),
); );
} }
} }

@ -12,6 +12,7 @@ import '../services/app_settings_service.dart';
import '../services/repeater_command_service.dart'; import '../services/repeater_command_service.dart';
import '../utils/battery_utils.dart'; import '../utils/battery_utils.dart';
import '../widgets/path_management_dialog.dart'; import '../widgets/path_management_dialog.dart';
import '../helpers/snack_bar_builder.dart';
class RepeaterStatusScreen extends StatefulWidget { class RepeaterStatusScreen extends StatefulWidget {
final Contact repeater; final Contact repeater;
@ -309,11 +310,10 @@ class _RepeaterStatusScreenState extends State<RepeaterStatusScreen> {
setState(() { setState(() {
_isLoading = false; _isLoading = false;
}); });
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.repeater_statusRequestTimeout), content: Text(context.l10n.repeater_statusRequestTimeout),
backgroundColor: Colors.red, backgroundColor: Colors.red,
),
); );
_recordStatusResult(false); _recordStatusResult(false);
}); });
@ -323,13 +323,10 @@ class _RepeaterStatusScreenState extends State<RepeaterStatusScreen> {
_isLoading = false; _isLoading = false;
}); });
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text( content: Text(context.l10n.repeater_errorLoadingStatus(e.toString())),
context.l10n.repeater_errorLoadingStatus(e.toString()), backgroundColor: Colors.red,
),
backgroundColor: Colors.red,
),
); );
} }
_recordStatusResult(false); _recordStatusResult(false);

@ -10,6 +10,7 @@ import '../services/linux_ble_error_classifier.dart';
import '../utils/app_logger.dart'; import '../utils/app_logger.dart';
import '../widgets/adaptive_app_bar_title.dart'; import '../widgets/adaptive_app_bar_title.dart';
import '../widgets/device_tile.dart'; import '../widgets/device_tile.dart';
import '../helpers/snack_bar_builder.dart';
import 'contacts_screen.dart'; import 'contacts_screen.dart';
import 'tcp_screen.dart'; import 'tcp_screen.dart';
import 'usb_screen.dart'; import 'usb_screen.dart';
@ -317,11 +318,10 @@ class _ScannerScreenState extends State<ScannerScreen> {
return; return;
} }
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.scanner_connectionFailed(e.toString())), content: Text(context.l10n.scanner_connectionFailed(e.toString())),
backgroundColor: Colors.red, backgroundColor: Colors.red,
),
); );
} }
} }

@ -11,6 +11,7 @@ import '../l10n/l10n.dart';
import '../models/radio_settings.dart'; import '../models/radio_settings.dart';
import '../services/app_debug_log_service.dart'; import '../services/app_debug_log_service.dart';
import '../widgets/app_bar.dart'; import '../widgets/app_bar.dart';
import '../helpers/snack_bar_builder.dart';
import 'app_settings_screen.dart'; import 'app_settings_screen.dart';
import 'app_debug_log_screen.dart'; import 'app_debug_log_screen.dart';
import 'ble_debug_log_screen.dart'; import 'ble_debug_log_screen.dart';
@ -513,8 +514,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
await connector.setNodeName(controller.text); await connector.setNodeName(controller.text);
await connector.refreshDeviceInfo(); await connector.refreshDeviceInfo();
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(l10n.settings_nodeNameUpdated)), context,
content: Text(l10n.settings_nodeNameUpdated),
); );
}, },
child: Text(l10n.common_save), child: Text(l10n.common_save),
@ -628,10 +630,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
final interval = int.tryParse(intervalText); final interval = int.tryParse(intervalText);
if (interval == null || interval < 60 || interval >= 86400) { if (interval == null || interval < 60 || interval >= 86400) {
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(l10n.settings_locationIntervalInvalid), content: Text(l10n.settings_locationIntervalInvalid),
),
); );
return; return;
} }
@ -639,8 +640,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
await connector.setCustomVar("gps_interval:$interval"); await connector.setCustomVar("gps_interval:$interval");
await connector.refreshDeviceInfo(); await connector.refreshDeviceInfo();
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(l10n.settings_locationUpdated)), context,
content: Text(l10n.settings_locationUpdated),
); );
} }
@ -660,15 +662,17 @@ class _SettingsScreenState extends State<SettingsScreen> {
: currentLon; : currentLon;
if (lat == null || lon == null) { if (lat == null || lon == null) {
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(l10n.settings_locationBothRequired)), context,
content: Text(l10n.settings_locationBothRequired),
); );
return; return;
} }
if (lat < -90 || lat > 90 || lon < -180 || lon > 180) { if (lat < -90 || lat > 90 || lon < -180 || lon > 180) {
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(l10n.settings_locationInvalid)), context,
content: Text(l10n.settings_locationInvalid),
); );
return; return;
} }
@ -676,8 +680,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
await connector.setNodeLocation(lat: lat, lon: lon); await connector.setNodeLocation(lat: lat, lon: lon);
await connector.refreshDeviceInfo(); await connector.refreshDeviceInfo();
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(l10n.settings_locationUpdated)), context,
content: Text(l10n.settings_locationUpdated),
); );
}, },
child: Text(l10n.common_save), child: Text(l10n.common_save),
@ -691,9 +696,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
void _syncTime(BuildContext context, MeshCoreConnector connector) { void _syncTime(BuildContext context, MeshCoreConnector connector) {
final l10n = context.l10n; final l10n = context.l10n;
connector.syncTime(); connector.syncTime();
ScaffoldMessenger.of( showDismissibleSnackBar(
context, context,
).showSnackBar(SnackBar(content: Text(l10n.settings_timeSynchronized))); content: Text(l10n.settings_timeSynchronized),
);
} }
void _confirmReboot(BuildContext context, MeshCoreConnector connector) { void _confirmReboot(BuildContext context, MeshCoreConnector connector) {
@ -758,23 +764,27 @@ class _SettingsScreenState extends State<SettingsScreen> {
if (!mounted) return; if (!mounted) return;
switch (result) { switch (result) {
case gpxExportSuccess: case gpxExportSuccess:
ScaffoldMessenger.of( showDismissibleSnackBar(
context, context,
).showSnackBar(SnackBar(content: Text(l10n.settings_gpxExportSuccess))); content: Text(l10n.settings_gpxExportSuccess),
);
case gpxExportNoContacts: case gpxExportNoContacts:
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(l10n.settings_gpxExportNoContacts)), context,
content: Text(l10n.settings_gpxExportNoContacts),
); );
break; break;
case gpxExportNotAvailable: case gpxExportNotAvailable:
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(l10n.settings_gpxExportNotAvailable)), context,
content: Text(l10n.settings_gpxExportNotAvailable),
); );
break; break;
case gpxExportFailed: case gpxExportFailed:
ScaffoldMessenger.of( showDismissibleSnackBar(
context, context,
).showSnackBar(SnackBar(content: Text(l10n.settings_gpxExportError))); content: Text(l10n.settings_gpxExportError),
);
break; break;
} }
} }
@ -1077,8 +1087,9 @@ void _privacySettings(BuildContext context, MeshCoreConnector connector) {
); );
await connector.refreshDeviceInfo(); await connector.refreshDeviceInfo();
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(l10n.settings_telemetryModeUpdated)), context,
content: Text(l10n.settings_telemetryModeUpdated),
); );
}, },
child: Text(l10n.common_save), child: Text(l10n.common_save),
@ -1410,18 +1421,18 @@ class _RadioSettingsDialogState extends State<_RadioSettingsDialog> {
final txPower = int.tryParse(_txPowerController.text); final txPower = int.tryParse(_txPowerController.text);
if (freqMHz == null || freqMHz < 300 || freqMHz > 2500) { if (freqMHz == null || freqMHz < 300 || freqMHz > 2500) {
ScaffoldMessenger.of( showDismissibleSnackBar(
context, context,
).showSnackBar(SnackBar(content: Text(l10n.settings_frequencyInvalid))); content: Text(l10n.settings_frequencyInvalid),
);
return; return;
} }
final maxTxPower = widget.connector.maxTxPower ?? 22; final maxTxPower = widget.connector.maxTxPower ?? 22;
if (txPower == null || txPower < 0 || txPower > maxTxPower) { if (txPower == null || txPower < 0 || txPower > maxTxPower) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text('${l10n.settings_txPowerInvalid} (0-$maxTxPower dBm)'), content: Text('${l10n.settings_txPowerInvalid} (0-$maxTxPower dBm)'),
),
); );
return; return;
} }
@ -1441,8 +1452,9 @@ class _RadioSettingsDialogState extends State<_RadioSettingsDialog> {
if (knownRepeat) { if (knownRepeat) {
const validRepeatFreqsKHz = {433000, 869000, 918000}; const validRepeatFreqsKHz = {433000, 869000, 918000};
if (_clientRepeat && !validRepeatFreqsKHz.contains(freqHz)) { if (_clientRepeat && !validRepeatFreqsKHz.contains(freqHz)) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(l10n.settings_clientRepeatFreqWarning)), context,
content: Text(l10n.settings_clientRepeatFreqWarning),
); );
return; return;
} }
@ -1472,14 +1484,16 @@ class _RadioSettingsDialogState extends State<_RadioSettingsDialog> {
if (!mounted) return; if (!mounted) return;
_logRadioSettingsState('Radio settings saved successfully'); _logRadioSettingsState('Radio settings saved successfully');
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(l10n.settings_radioSettingsUpdated)), context,
content: Text(l10n.settings_radioSettingsUpdated),
); );
} catch (e) { } catch (e) {
_appLog.warn('Radio settings save failed: $e', tag: 'RadioSettings'); _appLog.warn('Radio settings save failed: $e', tag: 'RadioSettings');
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(l10n.settings_error(e.toString()))), context,
content: Text(l10n.settings_error(e.toString())),
); );
} }
Navigator.pop(context); Navigator.pop(context);

@ -8,6 +8,7 @@ import '../l10n/l10n.dart';
import '../services/app_settings_service.dart'; import '../services/app_settings_service.dart';
import '../utils/platform_info.dart'; import '../utils/platform_info.dart';
import '../widgets/adaptive_app_bar_title.dart'; import '../widgets/adaptive_app_bar_title.dart';
import '../helpers/snack_bar_builder.dart';
import 'contacts_screen.dart'; import 'contacts_screen.dart';
import 'usb_screen.dart'; import 'usb_screen.dart';
@ -270,8 +271,10 @@ class _TcpScreenState extends State<TcpScreen> {
void _showError(String message) { void _showError(String message) {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar(content: Text(message), backgroundColor: Colors.red), context,
content: Text(message),
backgroundColor: Colors.red,
); );
} }

@ -14,6 +14,7 @@ import '../utils/app_logger.dart';
import '../widgets/path_management_dialog.dart'; import '../widgets/path_management_dialog.dart';
import '../helpers/cayenne_lpp.dart'; import '../helpers/cayenne_lpp.dart';
import '../utils/battery_utils.dart'; import '../utils/battery_utils.dart';
import '../helpers/snack_bar_builder.dart';
class TelemetryScreen extends StatefulWidget { class TelemetryScreen extends StatefulWidget {
final Contact contact; final Contact contact;
@ -86,11 +87,10 @@ class _TelemetryScreenState extends State<TelemetryScreen> {
_isLoading = false; _isLoading = false;
_isLoaded = false; _isLoaded = false;
}); });
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.telemetry_requestTimeout), content: Text(context.l10n.telemetry_requestTimeout),
backgroundColor: Colors.red, backgroundColor: Colors.red,
),
); );
_recordTelemetryResult(false); _recordTelemetryResult(false);
}); });
@ -137,11 +137,10 @@ class _TelemetryScreenState extends State<TelemetryScreen> {
_parsedTelemetry = parsedTelemetry; _parsedTelemetry = parsedTelemetry;
}); });
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.telemetry_receivedData), content: Text(context.l10n.telemetry_receivedData),
backgroundColor: Colors.green, backgroundColor: Colors.green,
),
); );
_statusTimeout?.cancel(); _statusTimeout?.cancel();
if (!mounted) return; if (!mounted) return;
@ -182,11 +181,10 @@ class _TelemetryScreenState extends State<TelemetryScreen> {
_isLoaded = false; _isLoaded = false;
}); });
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.telemetry_errorLoading(e.toString())), content: Text(context.l10n.telemetry_errorLoading(e.toString())),
backgroundColor: Colors.red, backgroundColor: Colors.red,
),
); );
} }
} }

@ -10,6 +10,7 @@ import '../utils/app_logger.dart';
import '../utils/platform_info.dart'; import '../utils/platform_info.dart';
import '../utils/usb_port_labels.dart'; import '../utils/usb_port_labels.dart';
import '../widgets/adaptive_app_bar_title.dart'; import '../widgets/adaptive_app_bar_title.dart';
import '../helpers/snack_bar_builder.dart';
import 'contacts_screen.dart'; import 'contacts_screen.dart';
import 'scanner_screen.dart'; import 'scanner_screen.dart';
import 'tcp_screen.dart'; import 'tcp_screen.dart';
@ -383,11 +384,10 @@ class _UsbScreenState extends State<UsbScreen> {
void _showError(Object error) { void _showError(Object error) {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(_friendlyErrorMessage(error)), content: Text(_friendlyErrorMessage(error)),
backgroundColor: Colors.red, backgroundColor: Colors.red,
),
); );
} }

@ -11,6 +11,7 @@ import '../l10n/l10n.dart';
import '../models/contact.dart'; import '../models/contact.dart';
import '../helpers/path_helper.dart'; import '../helpers/path_helper.dart';
import '../services/path_history_service.dart'; import '../services/path_history_service.dart';
import '../helpers/snack_bar_builder.dart';
import 'path_selection_dialog.dart'; import 'path_selection_dialog.dart';
class PathManagementDialog { class PathManagementDialog {
@ -65,11 +66,10 @@ class _PathManagementDialogState extends State<_PathManagementDialog> {
void _showFullPathDialog(BuildContext context, List<int> pathBytes) { void _showFullPathDialog(BuildContext context, List<int> pathBytes) {
final l10n = context.l10n; final l10n = context.l10n;
if (pathBytes.isEmpty) { if (pathBytes.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(l10n.chat_pathDetailsNotAvailable), content: Text(l10n.chat_pathDetailsNotAvailable),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
),
); );
return; return;
} }
@ -159,11 +159,10 @@ class _PathManagementDialogState extends State<_PathManagementDialog> {
); );
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(l10n.chat_hopsCount(result.length)), content: Text(l10n.chat_hopsCount(result.length)),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
),
); );
} }
} }
@ -337,13 +336,12 @@ class _PathManagementDialogState extends State<_PathManagementDialog> {
_showFullPathDialog(context, path.pathBytes), _showFullPathDialog(context, path.pathBytes),
onTap: () async { onTap: () async {
if (path.pathBytes.isEmpty) { if (path.pathBytes.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text( content: Text(
l10n.chat_pathDetailsNotAvailable, l10n.chat_pathDetailsNotAvailable,
),
duration: const Duration(seconds: 2),
), ),
duration: const Duration(seconds: 2),
); );
return; return;
} }
@ -361,13 +359,12 @@ class _PathManagementDialogState extends State<_PathManagementDialog> {
if (!context.mounted) return; if (!context.mounted) return;
Navigator.pop(context); Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text( content: Text(
l10n.path_usingHopsPath(path.hopCount), l10n.path_usingHopsPath(path.hopCount),
),
duration: const Duration(seconds: 2),
), ),
duration: const Duration(seconds: 2),
); );
}, },
), ),
@ -459,11 +456,10 @@ class _PathManagementDialogState extends State<_PathManagementDialog> {
onTap: () async { onTap: () async {
await connector.clearContactPath(currentContact); await connector.clearContactPath(currentContact);
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(l10n.chat_pathCleared), content: Text(l10n.chat_pathCleared),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
),
); );
Navigator.pop(context); Navigator.pop(context);
}, },
@ -489,11 +485,10 @@ class _PathManagementDialogState extends State<_PathManagementDialog> {
pathLen: -1, pathLen: -1,
); );
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(l10n.chat_floodModeEnabled), content: Text(l10n.chat_floodModeEnabled),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
),
); );
Navigator.pop(context); Navigator.pop(context);
}, },

@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:meshcore_open/connector/meshcore_protocol.dart'; import 'package:meshcore_open/connector/meshcore_protocol.dart';
import '../l10n/l10n.dart'; import '../l10n/l10n.dart';
import '../models/contact.dart'; import '../models/contact.dart';
import '../helpers/snack_bar_builder.dart';
class PathSelectionDialog extends StatefulWidget { class PathSelectionDialog extends StatefulWidget {
final List<Contact> availableContacts; final List<Contact> availableContacts;
@ -138,26 +139,22 @@ class _PathSelectionDialogState extends State<PathSelectionDialog> {
// Show error for invalid prefixes // Show error for invalid prefixes
if (invalidPrefixes.isNotEmpty) { if (invalidPrefixes.isNotEmpty) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text( content: Text(l10n.path_invalidHexPrefixes(invalidPrefixes.join(", "))),
l10n.path_invalidHexPrefixes(invalidPrefixes.join(", ")), duration: const Duration(seconds: 3),
), backgroundColor: Colors.red,
duration: const Duration(seconds: 3),
backgroundColor: Colors.red,
),
); );
return; return;
} }
// Check max path length (64 hops) // Check max path length (64 hops)
if (pathBytesList.length > 64) { if (pathBytesList.length > 64) {
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(l10n.path_tooLong), content: Text(l10n.path_tooLong),
duration: const Duration(seconds: 3), duration: const Duration(seconds: 3),
backgroundColor: Colors.red, backgroundColor: Colors.red,
),
); );
return; return;
} }

@ -10,6 +10,7 @@ import '../services/storage_service.dart';
import '../connector/meshcore_connector.dart'; import '../connector/meshcore_connector.dart';
import '../connector/meshcore_protocol.dart'; import '../connector/meshcore_protocol.dart';
import '../utils/app_logger.dart'; import '../utils/app_logger.dart';
import '../helpers/snack_bar_builder.dart';
import 'path_management_dialog.dart'; import 'path_management_dialog.dart';
class RoomLoginDialog extends StatefulWidget { class RoomLoginDialog extends StatefulWidget {
@ -175,11 +176,10 @@ class _RoomLoginDialogState extends State<RoomLoginDialog> {
setState(() { setState(() {
_isLoggingIn = false; _isLoggingIn = false;
}); });
ScaffoldMessenger.of(context).showSnackBar( showDismissibleSnackBar(
SnackBar( context,
content: Text(context.l10n.login_failed(e.toString())), content: Text(context.l10n.login_failed(e.toString())),
backgroundColor: Colors.red, backgroundColor: Colors.red,
),
); );
} }
} }

Loading…
Cancel
Save

Powered by TurnKey Linux.