✨ System notification for desktop and android
This commit is contained in:
@@ -223,6 +223,7 @@ class IslandApp extends HookConsumerWidget {
|
|||||||
if (user.value != null) {
|
if (user.value != null) {
|
||||||
final apiClient = ref.read(apiClientProvider);
|
final apiClient = ref.read(apiClientProvider);
|
||||||
subscribePushNotification(apiClient);
|
subscribePushNotification(apiClient);
|
||||||
|
initializeLocalNotifications();
|
||||||
final wsNotifier = ref.read(websocketStateProvider.notifier);
|
final wsNotifier = ref.read(websocketStateProvider.notifier);
|
||||||
wsNotifier.connect();
|
wsNotifier.connect();
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ import 'package:dio/dio.dart';
|
|||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:island/main.dart';
|
import 'package:island/main.dart';
|
||||||
@@ -16,54 +17,159 @@ import 'package:island/widgets/app_notification.dart';
|
|||||||
import 'package:top_snackbar_flutter/top_snack_bar.dart';
|
import 'package:top_snackbar_flutter/top_snack_bar.dart';
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
|
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
|
||||||
|
FlutterLocalNotificationsPlugin();
|
||||||
|
|
||||||
|
AppLifecycleState _appLifecycleState = AppLifecycleState.resumed;
|
||||||
|
|
||||||
|
void _onAppLifecycleChanged(AppLifecycleState state) {
|
||||||
|
_appLifecycleState = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> initializeLocalNotifications() async {
|
||||||
|
const AndroidInitializationSettings initializationSettingsAndroid =
|
||||||
|
AndroidInitializationSettings('@mipmap/ic_launcher');
|
||||||
|
|
||||||
|
const DarwinInitializationSettings initializationSettingsIOS =
|
||||||
|
DarwinInitializationSettings();
|
||||||
|
|
||||||
|
const DarwinInitializationSettings initializationSettingsMacOS =
|
||||||
|
DarwinInitializationSettings();
|
||||||
|
|
||||||
|
const LinuxInitializationSettings initializationSettingsLinux =
|
||||||
|
LinuxInitializationSettings(defaultActionName: 'Open notification');
|
||||||
|
|
||||||
|
const WindowsInitializationSettings initializationSettingsWindows =
|
||||||
|
WindowsInitializationSettings(
|
||||||
|
appName: 'Island',
|
||||||
|
appUserModelId: 'dev.solsynth.solian',
|
||||||
|
guid: 'dev.solsynth.solian',
|
||||||
|
);
|
||||||
|
|
||||||
|
const InitializationSettings initializationSettings = InitializationSettings(
|
||||||
|
android: initializationSettingsAndroid,
|
||||||
|
iOS: initializationSettingsIOS,
|
||||||
|
macOS: initializationSettingsMacOS,
|
||||||
|
linux: initializationSettingsLinux,
|
||||||
|
windows: initializationSettingsWindows,
|
||||||
|
);
|
||||||
|
|
||||||
|
await flutterLocalNotificationsPlugin.initialize(
|
||||||
|
initializationSettings,
|
||||||
|
onDidReceiveNotificationResponse: (NotificationResponse response) async {
|
||||||
|
final payload = response.payload;
|
||||||
|
if (payload != null) {
|
||||||
|
if (payload.startsWith('/')) {
|
||||||
|
// In-app routes
|
||||||
|
rootNavigatorKey.currentContext?.push(payload);
|
||||||
|
} else {
|
||||||
|
// External URLs
|
||||||
|
launchUrlString(payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addObserver(
|
||||||
|
LifecycleEventHandler(onAppLifecycleChanged: _onAppLifecycleChanged),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class LifecycleEventHandler extends WidgetsBindingObserver {
|
||||||
|
final void Function(AppLifecycleState) onAppLifecycleChanged;
|
||||||
|
|
||||||
|
LifecycleEventHandler({required this.onAppLifecycleChanged});
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
|
onAppLifecycleChanged(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
StreamSubscription<WebSocketPacket> setupNotificationListener(
|
StreamSubscription<WebSocketPacket> setupNotificationListener(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
WidgetRef ref,
|
WidgetRef ref,
|
||||||
) {
|
) {
|
||||||
final ws = ref.watch(websocketProvider);
|
final ws = ref.watch(websocketProvider);
|
||||||
return ws.dataStream.listen((pkt) {
|
return ws.dataStream.listen((pkt) async {
|
||||||
if (pkt.type == "notifications.new") {
|
if (pkt.type == "notifications.new") {
|
||||||
final notification = SnNotification.fromJson(pkt.data!);
|
final notification = SnNotification.fromJson(pkt.data!);
|
||||||
showTopSnackBar(
|
if (_appLifecycleState == AppLifecycleState.resumed) {
|
||||||
globalOverlay.currentState!,
|
// App is focused, show in-app notification
|
||||||
Center(
|
log(
|
||||||
child: ConstrainedBox(
|
'[Notification] Showing in-app notification: ${notification.title}',
|
||||||
constraints: const BoxConstraints(maxWidth: 480),
|
);
|
||||||
child: NotificationCard(notification: notification),
|
showTopSnackBar(
|
||||||
|
globalOverlay.currentState!,
|
||||||
|
Center(
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(maxWidth: 480),
|
||||||
|
child: NotificationCard(notification: notification),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
onTap: () {
|
||||||
onTap: () {
|
if (notification.meta['action_uri'] != null) {
|
||||||
if (notification.meta['action_uri'] != null) {
|
var uri = notification.meta['action_uri'] as String;
|
||||||
var uri = notification.meta['action_uri'] as String;
|
if (uri.startsWith('/')) {
|
||||||
if (uri.startsWith('/')) {
|
// In-app routes
|
||||||
// In-app routes
|
rootNavigatorKey.currentContext?.push(
|
||||||
rootNavigatorKey.currentContext?.push(
|
notification.meta['action_uri'],
|
||||||
notification.meta['action_uri'],
|
);
|
||||||
);
|
} else {
|
||||||
} else {
|
// External URLs
|
||||||
// External URLs
|
launchUrlString(uri);
|
||||||
launchUrlString(uri);
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
onDismissed: () {},
|
||||||
onDismissed: () {},
|
dismissType: DismissType.onSwipe,
|
||||||
dismissType: DismissType.onSwipe,
|
displayDuration: const Duration(seconds: 5),
|
||||||
displayDuration: const Duration(seconds: 5),
|
snackBarPosition: SnackBarPosition.top,
|
||||||
snackBarPosition: SnackBarPosition.top,
|
padding: EdgeInsets.only(
|
||||||
padding: EdgeInsets.only(
|
left: 16,
|
||||||
left: 16,
|
right: 16,
|
||||||
right: 16,
|
top:
|
||||||
top:
|
(!kIsWeb &&
|
||||||
(!kIsWeb &&
|
(Platform.isMacOS ||
|
||||||
(Platform.isMacOS ||
|
Platform.isWindows ||
|
||||||
Platform.isWindows ||
|
Platform.isLinux))
|
||||||
Platform.isLinux))
|
? 28
|
||||||
? 28
|
// ignore: use_build_context_synchronously
|
||||||
// ignore: use_build_context_synchronously
|
: MediaQuery.of(context).padding.top + 16,
|
||||||
: MediaQuery.of(context).padding.top + 16,
|
bottom: 16,
|
||||||
bottom: 16,
|
),
|
||||||
),
|
);
|
||||||
);
|
} else {
|
||||||
|
// App is in background, show system notification (only on supported platforms)
|
||||||
|
if (!kIsWeb && !Platform.isIOS) {
|
||||||
|
log(
|
||||||
|
'[Notification] Showing system notification: ${notification.title}',
|
||||||
|
);
|
||||||
|
const AndroidNotificationDetails androidNotificationDetails =
|
||||||
|
AndroidNotificationDetails(
|
||||||
|
'channel_id',
|
||||||
|
'channel_name',
|
||||||
|
channelDescription: 'channel_description',
|
||||||
|
importance: Importance.max,
|
||||||
|
priority: Priority.high,
|
||||||
|
ticker: 'ticker',
|
||||||
|
);
|
||||||
|
const NotificationDetails notificationDetails = NotificationDetails(
|
||||||
|
android: androidNotificationDetails,
|
||||||
|
);
|
||||||
|
await flutterLocalNotificationsPlugin.show(
|
||||||
|
0,
|
||||||
|
notification.title,
|
||||||
|
notification.content,
|
||||||
|
notificationDetails,
|
||||||
|
payload: notification.meta['action_uri'] as String?,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log(
|
||||||
|
'[Notification] Skipping system notification for unsupported platform: ${notification.title}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -72,7 +178,7 @@ Future<void> subscribePushNotification(
|
|||||||
Dio apiClient, {
|
Dio apiClient, {
|
||||||
bool detailedErrors = false,
|
bool detailedErrors = false,
|
||||||
}) async {
|
}) async {
|
||||||
if (Platform.isLinux) {
|
if (!kIsWeb && Platform.isLinux) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await FirebaseMessaging.instance.requestPermission(
|
await FirebaseMessaging.instance.requestPermission(
|
||||||
|
@@ -17,8 +17,8 @@ class NotificationCard extends HookConsumerWidget {
|
|||||||
return Card(
|
return Card(
|
||||||
elevation: 4,
|
elevation: 4,
|
||||||
margin: const EdgeInsets.only(bottom: 8),
|
margin: const EdgeInsets.only(bottom: 8),
|
||||||
shape: RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.vertical(bottom: Radius.circular(8)),
|
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@@ -3,6 +3,7 @@ import 'package:bitsdojo_window/bitsdojo_window.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
@@ -153,7 +154,7 @@ class _WindowSizeObserver extends WidgetsBindingObserver {
|
|||||||
|
|
||||||
final rootScaffoldKey = GlobalKey<ScaffoldState>();
|
final rootScaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
class AppScaffold extends StatelessWidget {
|
class AppScaffold extends HookConsumerWidget {
|
||||||
final Widget? body;
|
final Widget? body;
|
||||||
final PreferredSizeWidget? bottomNavigationBar;
|
final PreferredSizeWidget? bottomNavigationBar;
|
||||||
final PreferredSizeWidget? bottomSheet;
|
final PreferredSizeWidget? bottomSheet;
|
||||||
@@ -186,7 +187,14 @@ class AppScaffold extends StatelessWidget {
|
|||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final focusNode = useFocusNode();
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
focusNode.requestFocus();
|
||||||
|
return null;
|
||||||
|
}, []);
|
||||||
|
|
||||||
final appBarHeight = appBar?.preferredSize.height ?? 0;
|
final appBarHeight = appBar?.preferredSize.height ?? 0;
|
||||||
final safeTop = MediaQuery.of(context).padding.top;
|
final safeTop = MediaQuery.of(context).padding.top;
|
||||||
|
|
||||||
@@ -201,29 +209,59 @@ class AppScaffold extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
return Scaffold(
|
return Shortcuts(
|
||||||
extendBody: extendBody ?? true,
|
shortcuts: <LogicalKeySet, Intent>{
|
||||||
extendBodyBehindAppBar: true,
|
LogicalKeySet(LogicalKeyboardKey.escape): const PopIntent(),
|
||||||
backgroundColor:
|
},
|
||||||
noBackground
|
child: Actions(
|
||||||
? Colors.transparent
|
actions: <Type, Action<Intent>>{PopIntent: PopAction(context)},
|
||||||
: Theme.of(context).scaffoldBackgroundColor,
|
child: Focus(
|
||||||
body:
|
focusNode: focusNode,
|
||||||
noBackground ? content : AppBackground(isRoot: true, child: content),
|
child: Scaffold(
|
||||||
appBar: appBar,
|
extendBody: extendBody ?? true,
|
||||||
bottomNavigationBar: bottomNavigationBar,
|
extendBodyBehindAppBar: true,
|
||||||
bottomSheet: bottomSheet,
|
backgroundColor:
|
||||||
drawer: drawer,
|
noBackground
|
||||||
endDrawer: endDrawer,
|
? Colors.transparent
|
||||||
floatingActionButton: floatingActionButton,
|
: Theme.of(context).scaffoldBackgroundColor,
|
||||||
floatingActionButtonAnimator: floatingActionButtonAnimator,
|
body:
|
||||||
floatingActionButtonLocation: floatingActionButtonLocation,
|
noBackground
|
||||||
onDrawerChanged: onDrawerChanged,
|
? content
|
||||||
onEndDrawerChanged: onEndDrawerChanged,
|
: AppBackground(isRoot: true, child: content),
|
||||||
|
appBar: appBar,
|
||||||
|
bottomNavigationBar: bottomNavigationBar,
|
||||||
|
bottomSheet: bottomSheet,
|
||||||
|
drawer: drawer,
|
||||||
|
endDrawer: endDrawer,
|
||||||
|
floatingActionButton: floatingActionButton,
|
||||||
|
floatingActionButtonAnimator: floatingActionButtonAnimator,
|
||||||
|
floatingActionButtonLocation: floatingActionButtonLocation,
|
||||||
|
onDrawerChanged: onDrawerChanged,
|
||||||
|
onEndDrawerChanged: onEndDrawerChanged,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PopIntent extends Intent {
|
||||||
|
const PopIntent();
|
||||||
|
}
|
||||||
|
|
||||||
|
class PopAction extends Action<PopIntent> {
|
||||||
|
final BuildContext context;
|
||||||
|
|
||||||
|
PopAction(this.context);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void invoke(PopIntent intent) {
|
||||||
|
if (context.canPop()) {
|
||||||
|
context.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class PageBackButton extends StatelessWidget {
|
class PageBackButton extends StatelessWidget {
|
||||||
final Color? color;
|
final Color? color;
|
||||||
final List<Shadow>? shadows;
|
final List<Shadow>? shadows;
|
||||||
|
@@ -16,6 +16,7 @@ import firebase_core
|
|||||||
import firebase_crashlytics
|
import firebase_crashlytics
|
||||||
import firebase_messaging
|
import firebase_messaging
|
||||||
import flutter_inappwebview_macos
|
import flutter_inappwebview_macos
|
||||||
|
import flutter_local_notifications
|
||||||
import flutter_platform_alert
|
import flutter_platform_alert
|
||||||
import flutter_secure_storage_macos
|
import flutter_secure_storage_macos
|
||||||
import flutter_timezone
|
import flutter_timezone
|
||||||
@@ -54,6 +55,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
FLTFirebaseCrashlyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCrashlyticsPlugin"))
|
FLTFirebaseCrashlyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCrashlyticsPlugin"))
|
||||||
FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
|
FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
|
||||||
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
|
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
|
||||||
|
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
||||||
FlutterPlatformAlertPlugin.register(with: registry.registrar(forPlugin: "FlutterPlatformAlertPlugin"))
|
FlutterPlatformAlertPlugin.register(with: registry.registrar(forPlugin: "FlutterPlatformAlertPlugin"))
|
||||||
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||||
FlutterTimezonePlugin.register(with: registry.registrar(forPlugin: "FlutterTimezonePlugin"))
|
FlutterTimezonePlugin.register(with: registry.registrar(forPlugin: "FlutterTimezonePlugin"))
|
||||||
|
@@ -100,6 +100,8 @@ PODS:
|
|||||||
- flutter_inappwebview_macos (0.0.1):
|
- flutter_inappwebview_macos (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- OrderedSet (~> 6.0.3)
|
- OrderedSet (~> 6.0.3)
|
||||||
|
- flutter_local_notifications (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
- flutter_platform_alert (0.0.1):
|
- flutter_platform_alert (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- flutter_secure_storage_macos (6.1.3):
|
- flutter_secure_storage_macos (6.1.3):
|
||||||
@@ -260,6 +262,7 @@ DEPENDENCIES:
|
|||||||
- firebase_crashlytics (from `Flutter/ephemeral/.symlinks/plugins/firebase_crashlytics/macos`)
|
- firebase_crashlytics (from `Flutter/ephemeral/.symlinks/plugins/firebase_crashlytics/macos`)
|
||||||
- firebase_messaging (from `Flutter/ephemeral/.symlinks/plugins/firebase_messaging/macos`)
|
- firebase_messaging (from `Flutter/ephemeral/.symlinks/plugins/firebase_messaging/macos`)
|
||||||
- flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`)
|
- flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`)
|
||||||
|
- flutter_local_notifications (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos`)
|
||||||
- flutter_platform_alert (from `Flutter/ephemeral/.symlinks/plugins/flutter_platform_alert/macos`)
|
- flutter_platform_alert (from `Flutter/ephemeral/.symlinks/plugins/flutter_platform_alert/macos`)
|
||||||
- flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
|
- flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
|
||||||
- flutter_timezone (from `Flutter/ephemeral/.symlinks/plugins/flutter_timezone/macos`)
|
- flutter_timezone (from `Flutter/ephemeral/.symlinks/plugins/flutter_timezone/macos`)
|
||||||
@@ -335,6 +338,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter/ephemeral/.symlinks/plugins/firebase_messaging/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/firebase_messaging/macos
|
||||||
flutter_inappwebview_macos:
|
flutter_inappwebview_macos:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos
|
||||||
|
flutter_local_notifications:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos
|
||||||
flutter_platform_alert:
|
flutter_platform_alert:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/flutter_platform_alert/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/flutter_platform_alert/macos
|
||||||
flutter_secure_storage_macos:
|
flutter_secure_storage_macos:
|
||||||
@@ -411,6 +416,7 @@ SPEC CHECKSUMS:
|
|||||||
FirebaseRemoteConfigInterop: 0896fd52ab72586a355c8f389ff85aaa9e5375e1
|
FirebaseRemoteConfigInterop: 0896fd52ab72586a355c8f389ff85aaa9e5375e1
|
||||||
FirebaseSessions: f4692789e770bec66ce17d772c0e9561c4f11737
|
FirebaseSessions: f4692789e770bec66ce17d772c0e9561c4f11737
|
||||||
flutter_inappwebview_macos: c2d68649f9f8f1831bfcd98d73fd6256366d9d1d
|
flutter_inappwebview_macos: c2d68649f9f8f1831bfcd98d73fd6256366d9d1d
|
||||||
|
flutter_local_notifications: 4bf37a31afde695b56091b4ae3e4d9c7a7e6cda0
|
||||||
flutter_platform_alert: 8fa7a7c21f95b26d08b4a3891936ca27e375f284
|
flutter_platform_alert: 8fa7a7c21f95b26d08b4a3891936ca27e375f284
|
||||||
flutter_secure_storage_macos: 7f45e30f838cf2659862a4e4e3ee1c347c2b3b54
|
flutter_secure_storage_macos: 7f45e30f838cf2659862a4e4e3ee1c347c2b3b54
|
||||||
flutter_timezone: d59eea86178cbd7943cd2431cc2eaa9850f935d8
|
flutter_timezone: d59eea86178cbd7943cd2431cc2eaa9850f935d8
|
||||||
|
40
pubspec.lock
40
pubspec.lock
@@ -902,6 +902,38 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.0"
|
version: "6.0.0"
|
||||||
|
flutter_local_notifications:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_local_notifications
|
||||||
|
sha256: a9966c850de5e445331b854fa42df96a8020066d67f125a5964cbc6556643f68
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "19.4.1"
|
||||||
|
flutter_local_notifications_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_local_notifications_linux
|
||||||
|
sha256: e3c277b2daab8e36ac5a6820536668d07e83851aeeb79c446e525a70710770a5
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.0.0"
|
||||||
|
flutter_local_notifications_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_local_notifications_platform_interface
|
||||||
|
sha256: "277d25d960c15674ce78ca97f57d0bae2ee401c844b6ac80fcd972a9c99d09fe"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.1.0"
|
||||||
|
flutter_local_notifications_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_local_notifications_windows
|
||||||
|
sha256: ed46d7ae4ec9d19e4c8fa2badac5fe27ba87a3fe387343ce726f927af074ec98
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.2"
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -1505,10 +1537,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: material_symbols_icons
|
name: material_symbols_icons
|
||||||
sha256: b1342194e859b2774f920b484c46f54a37a845488e23d570385fbe3ede92ee9f
|
sha256: "2cfd19bf1c3016b0de7298eb3d3444fcb6ef093d934deb870ceb946af89cfa58"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2867.0"
|
version: "4.2872.0"
|
||||||
media_kit:
|
media_kit:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -2310,10 +2342,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: sqlparser
|
name: sqlparser
|
||||||
sha256: "7c859c803cf7e9a84d6db918bac824545045692bbe94a6386bd3a45132235d09"
|
sha256: "57090342af1ce32bb499aa641f4ecdd2d6231b9403cea537ac059e803cc20d67"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.41.1"
|
version: "0.41.2"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@@ -83,7 +83,7 @@ dependencies:
|
|||||||
flutter_udid: ^4.0.0
|
flutter_udid: ^4.0.0
|
||||||
firebase_core: ^4.1.0
|
firebase_core: ^4.1.0
|
||||||
web_socket_channel: ^3.0.3
|
web_socket_channel: ^3.0.3
|
||||||
material_symbols_icons: ^4.2867.0
|
material_symbols_icons: ^4.2872.0
|
||||||
drift: ^2.28.1
|
drift: ^2.28.1
|
||||||
drift_flutter: ^0.2.5
|
drift_flutter: ^0.2.5
|
||||||
path: ^1.9.1
|
path: ^1.9.1
|
||||||
@@ -140,6 +140,7 @@ dependencies:
|
|||||||
file_saver: ^0.3.1
|
file_saver: ^0.3.1
|
||||||
tray_manager: ^0.5.1
|
tray_manager: ^0.5.1
|
||||||
flutter_webrtc: ^1.1.0
|
flutter_webrtc: ^1.1.0
|
||||||
|
flutter_local_notifications: ^19.4.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@@ -32,6 +32,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
croppy
|
croppy
|
||||||
|
flutter_local_notifications_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||||
|
Reference in New Issue
Block a user