mirror of
https://github.com/rzuasti/oott.git
synced 2026-07-08 19:21:54 +02:00
Extract snackbar notifications to UISnackbars utility
Centralises all SnackBar calls behind UISnackbars.showError/showSuccess/ showWarning/showInfo, each themed via AppColorExtension. New warning and info colour tokens added to both themes. Calling any method clears any visible snackbar before showing the new one. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
3094753587
commit
07f7d54531
@@ -51,3 +51,4 @@ For Flutter/Dart code:
|
|||||||
- ALWAYS run all tests after making a new change and do not continue until all tests pass
|
- ALWAYS run all tests after making a new change and do not continue until all tests pass
|
||||||
- When adding a new API endpoint, ALWAYS wire it to the OpenAPI generation
|
- When adding a new API endpoint, ALWAYS wire it to the OpenAPI generation
|
||||||
- When adding a significant chunk of new code (either Rust or Dart), run the corresponding linter
|
- When adding a significant chunk of new code (either Rust or Dart), run the corresponding linter
|
||||||
|
- In the frontend, use the UISnackbars component to display messages to the user that do not require action on their part.
|
||||||
|
|||||||
@@ -13,7 +13,8 @@
|
|||||||
- [x] List recorded devices
|
- [x] List recorded devices
|
||||||
- [x] Register a device
|
- [x] Register a device
|
||||||
- [x] Forget a device
|
- [x] Forget a device
|
||||||
- [ ] Extract the snack bar confirmations as a utility widget so it can be reused
|
- [x] Extract the snack bar confirmations as a utility widget so it can be reused
|
||||||
|
- [ ] In the notifications list add an action to register the device if new
|
||||||
- [ ] In the devices list add filters by owner and device type
|
- [ ] In the devices list add filters by owner and device type
|
||||||
- [ ] View detailed log of device activity (based on event log in the backend)
|
- [ ] View detailed log of device activity (based on event log in the backend)
|
||||||
- [ ] Scan process monitor and summary page
|
- [ ] Scan process monitor and summary page
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||||||
import '../model/device.dart';
|
import '../model/device.dart';
|
||||||
import '../utils/friendly_date_formatter.dart';
|
import '../utils/friendly_date_formatter.dart';
|
||||||
import '../utils/oott_api.dart';
|
import '../utils/oott_api.dart';
|
||||||
|
import '../utils/ui_snackbars.dart';
|
||||||
import '../widgets/status_badge.dart';
|
import '../widgets/status_badge.dart';
|
||||||
|
|
||||||
const _deviceTypes = [
|
const _deviceTypes = [
|
||||||
@@ -69,8 +70,7 @@ class _DeviceListState extends State<DeviceList> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _confirmForget(BuildContext context, Device device) async {
|
Future<void> _confirmForget(Device device) async {
|
||||||
final messenger = ScaffoldMessenger.of(context);
|
|
||||||
final colorScheme = Theme.of(context).colorScheme;
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await showDialog<bool>(
|
||||||
@@ -99,28 +99,15 @@ class _DeviceListState extends State<DeviceList> {
|
|||||||
try {
|
try {
|
||||||
await BackendAPI.instance.forgetDevice(device.macAddress);
|
await BackendAPI.instance.forgetDevice(device.macAddress);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
messenger.showSnackBar(
|
UISnackbars.showSuccess(context, 'Device forgotten');
|
||||||
const SnackBar(
|
|
||||||
content: Text('Device forgotten'),
|
|
||||||
behavior: SnackBarBehavior.floating,
|
|
||||||
showCloseIcon: true,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
_loadDevices();
|
_loadDevices();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
messenger.showSnackBar(
|
UISnackbars.showError(context, 'Failed to forget device: $e');
|
||||||
SnackBar(
|
|
||||||
content: Text('Failed to forget device: $e'),
|
|
||||||
behavior: SnackBarBehavior.floating,
|
|
||||||
showCloseIcon: true,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _showRegisterDialog(BuildContext context, Device device) async {
|
Future<void> _showRegisterDialog(Device device) async {
|
||||||
final messenger = ScaffoldMessenger.of(context);
|
|
||||||
final formKey = GlobalKey<FormState>();
|
final formKey = GlobalKey<FormState>();
|
||||||
String owner = '';
|
String owner = '';
|
||||||
String deviceType = _deviceTypes.contains(device.deviceType)
|
String deviceType = _deviceTypes.contains(device.deviceType)
|
||||||
@@ -189,23 +176,11 @@ class _DeviceListState extends State<DeviceList> {
|
|||||||
deviceType,
|
deviceType,
|
||||||
);
|
);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
messenger.showSnackBar(
|
UISnackbars.showSuccess(context, 'Device registered');
|
||||||
const SnackBar(
|
|
||||||
content: Text('Device registered'),
|
|
||||||
behavior: SnackBarBehavior.floating,
|
|
||||||
showCloseIcon: true,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
_loadDevices();
|
_loadDevices();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
messenger.showSnackBar(
|
UISnackbars.showError(context, 'Failed to register device: $e');
|
||||||
SnackBar(
|
|
||||||
content: Text('Failed to register device: $e'),
|
|
||||||
behavior: SnackBarBehavior.floating,
|
|
||||||
showCloseIcon: true,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,12 +310,9 @@ class _DeviceListState extends State<DeviceList> {
|
|||||||
icon: const Icon(Icons.more_vert),
|
icon: const Icon(Icons.more_vert),
|
||||||
onSelected: (value) async {
|
onSelected: (value) async {
|
||||||
if (value == 'forget') {
|
if (value == 'forget') {
|
||||||
await _confirmForget(context, device);
|
await _confirmForget(device);
|
||||||
} else if (value == 'register') {
|
} else if (value == 'register') {
|
||||||
await _showRegisterDialog(
|
await _showRegisterDialog(device);
|
||||||
context,
|
|
||||||
device,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
itemBuilder: (context) => [
|
itemBuilder: (context) => [
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
|||||||
import '../model/notification.dart' as oott_model;
|
import '../model/notification.dart' as oott_model;
|
||||||
import '../utils/friendly_date_formatter.dart';
|
import '../utils/friendly_date_formatter.dart';
|
||||||
import '../utils/oott_api.dart';
|
import '../utils/oott_api.dart';
|
||||||
|
import '../utils/ui_snackbars.dart';
|
||||||
|
|
||||||
class NotificationList extends StatefulWidget {
|
class NotificationList extends StatefulWidget {
|
||||||
const NotificationList({super.key});
|
const NotificationList({super.key});
|
||||||
@@ -48,13 +49,7 @@ class _NotificationListState extends State<NotificationList> {
|
|||||||
await BackendAPI.instance.markAllNotificationsAsRead();
|
await BackendAPI.instance.markAllNotificationsAsRead();
|
||||||
_pagingController.refresh();
|
_pagingController.refresh();
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
UISnackbars.showSuccess(context, 'All notifications marked as read');
|
||||||
const SnackBar(
|
|
||||||
content: Text('All notifications marked as read'),
|
|
||||||
behavior: SnackBarBehavior.floating,
|
|
||||||
showCloseIcon: true,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,24 +58,15 @@ class _NotificationListState extends State<NotificationList> {
|
|||||||
oott_model.Notification item,
|
oott_model.Notification item,
|
||||||
) async {
|
) async {
|
||||||
if (!item.isNew) {
|
if (!item.isNew) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
UISnackbars.showWarning(
|
||||||
SnackBar(
|
context,
|
||||||
content: Text('Notification was already marked as read'),
|
'Notification was already marked as read',
|
||||||
behavior: SnackBarBehavior.floating,
|
|
||||||
showCloseIcon: true,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
await BackendAPI.instance.markNotificationAsRead(item.id);
|
await BackendAPI.instance.markNotificationAsRead(item.id);
|
||||||
if (!context.mounted) return false;
|
if (!context.mounted) return false;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
UISnackbars.showSuccess(context, 'Event marked as read');
|
||||||
SnackBar(
|
|
||||||
content: Text('Event marked as read'),
|
|
||||||
behavior: SnackBarBehavior.floating,
|
|
||||||
showCloseIcon: true,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if (_filterChoice != 3) {
|
if (_filterChoice != 3) {
|
||||||
_pagingController.value = _pagingController.value.filterItems(
|
_pagingController.value = _pagingController.value.filterItems(
|
||||||
(n) => n.id != item.id,
|
(n) => n.id != item.id,
|
||||||
@@ -98,24 +84,15 @@ class _NotificationListState extends State<NotificationList> {
|
|||||||
oott_model.Notification item,
|
oott_model.Notification item,
|
||||||
) async {
|
) async {
|
||||||
if (item.isNew) {
|
if (item.isNew) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
UISnackbars.showWarning(
|
||||||
SnackBar(
|
context,
|
||||||
content: Text('Notification was already marked as unread'),
|
'Notification was already marked as unread',
|
||||||
behavior: SnackBarBehavior.floating,
|
|
||||||
showCloseIcon: true,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
await BackendAPI.instance.markNotificationAsNew(item.id);
|
await BackendAPI.instance.markNotificationAsNew(item.id);
|
||||||
if (!context.mounted) return false;
|
if (!context.mounted) return false;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
UISnackbars.showSuccess(context, 'Event marked as unread');
|
||||||
SnackBar(
|
|
||||||
content: Text('Event marked as unread'),
|
|
||||||
behavior: SnackBarBehavior.floating,
|
|
||||||
showCloseIcon: true,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if (_filterChoice != 3) {
|
if (_filterChoice != 3) {
|
||||||
_pagingController.value = _pagingController.value.filterItems(
|
_pagingController.value = _pagingController.value.filterItems(
|
||||||
(n) => n.id != item.id,
|
(n) => n.id != item.id,
|
||||||
@@ -183,92 +160,86 @@ class _NotificationListState extends State<NotificationList> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PagedSliverList<int, oott_model.Notification>(
|
PagedSliverList<int, oott_model.Notification>(
|
||||||
state: state,
|
state: state,
|
||||||
fetchNextPage: fetchNextPage,
|
fetchNextPage: fetchNextPage,
|
||||||
builderDelegate: PagedChildBuilderDelegate(
|
builderDelegate: PagedChildBuilderDelegate(
|
||||||
itemBuilder: (context, item, index) {
|
itemBuilder: (context, item, index) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Dismissible(
|
Dismissible(
|
||||||
key: UniqueKey(),
|
key: UniqueKey(),
|
||||||
confirmDismiss: (direction) =>
|
confirmDismiss: (direction) =>
|
||||||
direction == DismissDirection.startToEnd
|
direction == DismissDirection.startToEnd
|
||||||
? _markAsNew(context, item)
|
? _markAsNew(context, item)
|
||||||
: _markAsRead(context, item),
|
: _markAsRead(context, item),
|
||||||
background: Container(
|
background: Container(
|
||||||
color: Theme.of(
|
color: Theme.of(
|
||||||
context,
|
context,
|
||||||
).colorScheme.tertiaryContainer,
|
).colorScheme.tertiaryContainer,
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
padding: const EdgeInsets.only(left: 16),
|
padding: const EdgeInsets.only(left: 16),
|
||||||
child: Icon(Icons.mark_email_unread),
|
child: Icon(Icons.mark_email_unread),
|
||||||
),
|
),
|
||||||
secondaryBackground: Container(
|
secondaryBackground: Container(
|
||||||
color: Theme.of(
|
color: Theme.of(context).colorScheme.primaryContainer,
|
||||||
context,
|
alignment: Alignment.centerRight,
|
||||||
).colorScheme.primaryContainer,
|
padding: const EdgeInsets.only(right: 16),
|
||||||
alignment: Alignment.centerRight,
|
child: Icon(Icons.done),
|
||||||
padding: const EdgeInsets.only(right: 16),
|
),
|
||||||
child: Icon(Icons.done),
|
child: ListTile(
|
||||||
),
|
tileColor: item.isNew
|
||||||
child: ListTile(
|
? Theme.of(context).colorScheme.secondaryContainer
|
||||||
tileColor: item.isNew
|
: null,
|
||||||
? Theme.of(
|
leading: Icon(
|
||||||
context,
|
item.notificationType.icon,
|
||||||
).colorScheme.secondaryContainer
|
color: item.isNew
|
||||||
: null,
|
? Theme.of(context).colorScheme.primary
|
||||||
leading: Icon(
|
: null,
|
||||||
item.notificationType.icon,
|
|
||||||
color: item.isNew
|
|
||||||
? Theme.of(context).colorScheme.primary
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
title: Text(
|
|
||||||
'${FriendlyDateFormatter().format(item.createdOn)} - ${item.title}',
|
|
||||||
style: item.isNew
|
|
||||||
? const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
subtitle: Text(item.body, maxLines: 5),
|
|
||||||
trailing: PopupMenuButton<String>(
|
|
||||||
icon: const Icon(Icons.more_vert),
|
|
||||||
onSelected: (value) async {
|
|
||||||
if (value == 'mark_read') {
|
|
||||||
await _markAsRead(context, item);
|
|
||||||
} else if (value == 'mark_new') {
|
|
||||||
await _markAsNew(context, item);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
itemBuilder: (context) => [
|
|
||||||
if (item.isNew)
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 'mark_read',
|
|
||||||
child: Text('Mark as read'),
|
|
||||||
),
|
|
||||||
if (!item.isNew)
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 'mark_new',
|
|
||||||
child: Text('Mark as unread'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
onTap: () {},
|
|
||||||
isThreeLine: true,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Divider(),
|
title: Text(
|
||||||
],
|
'${FriendlyDateFormatter().format(item.createdOn)} - ${item.title}',
|
||||||
);
|
style: item.isNew
|
||||||
},
|
? const TextStyle(fontWeight: FontWeight.bold)
|
||||||
),
|
: null,
|
||||||
),
|
),
|
||||||
],
|
subtitle: Text(item.body, maxLines: 5),
|
||||||
|
trailing: PopupMenuButton<String>(
|
||||||
|
icon: const Icon(Icons.more_vert),
|
||||||
|
onSelected: (value) async {
|
||||||
|
if (value == 'mark_read') {
|
||||||
|
await _markAsRead(context, item);
|
||||||
|
} else if (value == 'mark_new') {
|
||||||
|
await _markAsNew(context, item);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
itemBuilder: (context) => [
|
||||||
|
if (item.isNew)
|
||||||
|
const PopupMenuItem(
|
||||||
|
value: 'mark_read',
|
||||||
|
child: Text('Mark as read'),
|
||||||
|
),
|
||||||
|
if (!item.isNew)
|
||||||
|
const PopupMenuItem(
|
||||||
|
value: 'mark_new',
|
||||||
|
child: Text('Mark as unread'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () {},
|
||||||
|
isThreeLine: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
);
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
// Paginated list end
|
// Paginated list end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,38 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
class AppColorExtension extends ThemeExtension<AppColorExtension> {
|
class AppColorExtension extends ThemeExtension<AppColorExtension> {
|
||||||
const AppColorExtension({required this.success, required this.onSuccess});
|
const AppColorExtension({
|
||||||
|
required this.success,
|
||||||
|
required this.onSuccess,
|
||||||
|
required this.warning,
|
||||||
|
required this.onWarning,
|
||||||
|
required this.info,
|
||||||
|
required this.onInfo,
|
||||||
|
});
|
||||||
|
|
||||||
final Color success;
|
final Color success;
|
||||||
final Color onSuccess;
|
final Color onSuccess;
|
||||||
|
final Color warning;
|
||||||
|
final Color onWarning;
|
||||||
|
final Color info;
|
||||||
|
final Color onInfo;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
AppColorExtension copyWith({Color? success, Color? onSuccess}) =>
|
AppColorExtension copyWith({
|
||||||
AppColorExtension(
|
Color? success,
|
||||||
success: success ?? this.success,
|
Color? onSuccess,
|
||||||
onSuccess: onSuccess ?? this.onSuccess,
|
Color? warning,
|
||||||
);
|
Color? onWarning,
|
||||||
|
Color? info,
|
||||||
|
Color? onInfo,
|
||||||
|
}) => AppColorExtension(
|
||||||
|
success: success ?? this.success,
|
||||||
|
onSuccess: onSuccess ?? this.onSuccess,
|
||||||
|
warning: warning ?? this.warning,
|
||||||
|
onWarning: onWarning ?? this.onWarning,
|
||||||
|
info: info ?? this.info,
|
||||||
|
onInfo: onInfo ?? this.onInfo,
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
AppColorExtension lerp(AppColorExtension? other, double t) {
|
AppColorExtension lerp(AppColorExtension? other, double t) {
|
||||||
@@ -20,6 +41,10 @@ class AppColorExtension extends ThemeExtension<AppColorExtension> {
|
|||||||
return AppColorExtension(
|
return AppColorExtension(
|
||||||
success: Color.lerp(success, other.success, t)!,
|
success: Color.lerp(success, other.success, t)!,
|
||||||
onSuccess: Color.lerp(onSuccess, other.onSuccess, t)!,
|
onSuccess: Color.lerp(onSuccess, other.onSuccess, t)!,
|
||||||
|
warning: Color.lerp(warning, other.warning, t)!,
|
||||||
|
onWarning: Color.lerp(onWarning, other.onWarning, t)!,
|
||||||
|
info: Color.lerp(info, other.info, t)!,
|
||||||
|
onInfo: Color.lerp(onInfo, other.onInfo, t)!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,10 @@ final ThemeData catppuccinMochaDarkTheme = ThemeData(
|
|||||||
const AppColorExtension(
|
const AppColorExtension(
|
||||||
success: CatppuccinMochaColors.green,
|
success: CatppuccinMochaColors.green,
|
||||||
onSuccess: CatppuccinMochaColors.crust,
|
onSuccess: CatppuccinMochaColors.crust,
|
||||||
|
warning: CatppuccinMochaColors.peach,
|
||||||
|
onWarning: CatppuccinMochaColors.crust,
|
||||||
|
info: CatppuccinMochaColors.blue,
|
||||||
|
onInfo: CatppuccinMochaColors.crust,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -81,6 +81,10 @@ final ThemeData gruvboxDarkTheme = ThemeData(
|
|||||||
const AppColorExtension(
|
const AppColorExtension(
|
||||||
success: GruvboxColors.brightGreen,
|
success: GruvboxColors.brightGreen,
|
||||||
onSuccess: GruvboxColors.bgHard,
|
onSuccess: GruvboxColors.bgHard,
|
||||||
|
warning: GruvboxColors.brightYellow,
|
||||||
|
onWarning: GruvboxColors.bgHard,
|
||||||
|
info: GruvboxColors.brightBlue,
|
||||||
|
onInfo: GruvboxColors.bgHard,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,30 +3,39 @@ import '../theme/app_colors.dart';
|
|||||||
|
|
||||||
class UISnackbars {
|
class UISnackbars {
|
||||||
static void showError(BuildContext context, String message) {
|
static void showError(BuildContext context, String message) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
SnackBar(
|
_show(context, message, colorScheme.error, colorScheme.onError);
|
||||||
content: Text(
|
|
||||||
message,
|
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.onError),
|
|
||||||
),
|
|
||||||
behavior: SnackBarBehavior.floating,
|
|
||||||
showCloseIcon: true,
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.error,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void showSuccess(BuildContext context, String message) {
|
static void showSuccess(BuildContext context, String message) {
|
||||||
final appColors = Theme.of(context).extension<AppColorExtension>()!;
|
final appColors = Theme.of(context).extension<AppColorExtension>()!;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
_show(context, message, appColors.success, appColors.onSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void showWarning(BuildContext context, String message) {
|
||||||
|
final appColors = Theme.of(context).extension<AppColorExtension>()!;
|
||||||
|
_show(context, message, appColors.warning, appColors.onWarning);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void showInfo(BuildContext context, String message) {
|
||||||
|
final appColors = Theme.of(context).extension<AppColorExtension>()!;
|
||||||
|
_show(context, message, appColors.info, appColors.onInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _show(
|
||||||
|
BuildContext context,
|
||||||
|
String message,
|
||||||
|
Color background,
|
||||||
|
Color foreground,
|
||||||
|
) {
|
||||||
|
final messenger = ScaffoldMessenger.of(context);
|
||||||
|
messenger.clearSnackBars();
|
||||||
|
messenger.showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(
|
content: Text(message, style: TextStyle(color: foreground)),
|
||||||
message,
|
|
||||||
style: TextStyle(color: appColors.onSuccess),
|
|
||||||
),
|
|
||||||
behavior: SnackBarBehavior.floating,
|
behavior: SnackBarBehavior.floating,
|
||||||
showCloseIcon: true,
|
showCloseIcon: true,
|
||||||
backgroundColor: appColors.success,
|
backgroundColor: background,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user