GetX异常:应用进入后台时LocalNotificationsController无法找到
- 应用处于前台或终止状态时功能正常
- 环境:Flutter 3.13.9 | GetX 4.6.6 | get_storage 2.1.1
我编写的LocalNotificationsController类继承自GetxService,按照GetX文档说明,该控制器应在应用全生命周期内可用,但实际遇到的问题是:应用进入后台后,无法找到该控制器实例。
在main文件中我已调用initialServies初始化服务:
Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); await initialServies();
initialServies实现:
class MyServices extends GetxService { late GetStorage localStorage; late FirebaseMessaging firebaseMessaging; Future<MyServices> init() async { await GetStorage.init(); localStorage = GetStorage(); firebaseMessaging = FirebaseMessaging.instance; return this; } } initialServies() async { await Get.putAsync(() => MyServices().init()); await Get.putAsync(() => NotificationsService().initNotifications()); Get.put(UsersController()); Get.put(LocalWishlistController()); Get.put(LocalNotificationsController()); }
LocalNotificationsController实现:
class LocalNotificationsController extends GetxService with WidgetsBindingObserver { MyServices myServices = Get.find(); var notifications = <NotificationModel>[].obs; @override void onInit() { WidgetsBinding.instance.addObserver(this); fetchNotifications(); super.onInit(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.resumed) { // fetchNotifications(); } } @override void onClose() { WidgetsBinding.instance.removeObserver(this); super.onClose(); } // 从本地存储获取通知 void fetchNotifications() { List storedNotificationsList = myServices.localStorage.read('notifications') ?? []; notifications.value = storedNotificationsList.map((notification) => NotificationModel.fromJson(notification)).toList(); notifications.sort((a, b) => b.createdAt.compareTo(a.createdAt)); } // 为当前用户添加新通知 void addNotification(RemoteMessage message) { // 检查通知是否已存在 if (notifications.any((notification) => notification.id == message.messageId)) { return; } // 将通知添加到列表 notifications.insert( 0, NotificationModel( id: message.messageId!, title: message.notification!.title!, body: message.notification!.body!, createdAt: Timestamp.now(), seen: false, type: message.data['type'] ?? '', typeId: message.data['id'] ?? '', ), ); // 将通知保存到本地存储 List<Map<String, dynamic>> notificationsMap = notifications.map((notification) => notification.toJson()).toList(); myServices.localStorage.write('notifications', notificationsMap); // fetchNotifications(); }
notifications_service.dart相关代码:
Future<void> handleBackgroundMessage(RemoteMessage message) async { await Firebase.initializeApp(); await Get.putAsync(() => MyServices().init()); final localNotificationsController = Get.put(LocalNotificationsController()); localNotificationsController.addNotification(message); } Future<void> handleForegroundMessage(RemoteMessage message) async { if (message.notification != null) { Get.find<LocalNotificationsController>().addNotification(message); await NotificationsService()._showNotification(message); } } class NotificationsService extends GetxService {
应用处于后台时接收通知,必须重新调用Get.put获取LocalNotificationsController,否则会提示控制器未找到。这会导致创建两个独立的控制器实例:后台添加的通知存入新实例的列表,而前台恢复后显示的仍是旧实例的列表,本地存储内容不同步,UI(使用GetX Builder监听notifications列表)无法更新。
- 在main文件中调用
Get.put初始化控制器 - 调用
notifications.refresh() - 调用
update()(尽管notifications是RxList无需手动调用) - 使用下拉刷新及
fetchNotifications方法 - 使用
lazyPut、putAsync初始化控制器 - 使用
initialBindings绑定控制器
1. 统一控制器获取逻辑,避免重复实例化
在后台消息处理函数中,先尝试用Get.find()获取已存在的控制器实例,仅当实例不存在时再调用Get.put()创建,同时指定全局唯一规则确保实例唯一:
修改handleBackgroundMessage:
Future<void> handleBackgroundMessage(RemoteMessage message) async { await Firebase.initializeApp(); // 确保MyServices单例 if (!Get.isRegistered<MyServices>()) { await Get.putAsync(() => MyServices().init()); } // 确保LocalNotificationsController单例 late LocalNotificationsController localNotificationsController; if (Get.isRegistered<LocalNotificationsController>()) { localNotificationsController = Get.find<LocalNotificationsController>(); } else { localNotificationsController = Get.put(LocalNotificationsController(), permanent: true); } localNotificationsController.addNotification(message); }
2. 用permanent: true确保实例不被销毁
在初始化LocalNotificationsController时添加permanent: true参数,阻止GetX在内存不足时自动销毁该服务实例:
修改initialServies中的初始化代码:
Get.put(LocalNotificationsController(), permanent: true);
3. 前台恢复时同步本地存储数据
在LocalNotificationsController的didChangeAppLifecycleState方法中,恢复前台时主动从本地存储拉取最新通知数据,确保UI和本地存储同步:
@override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.resumed) { fetchNotifications(); notifications.refresh(); // 强制触发UI更新 } }
4. 优化通知添加逻辑,以本地存储为唯一数据源
修改addNotification方法,先读取本地存储的当前列表进行判断和更新,再同步RxList,避免多实例导致的不一致:
void addNotification(RemoteMessage message) { // 从本地存储读取当前列表,避免依赖内存中的列表判断重复 List storedNotificationsList = myServices.localStorage.read('notifications') ?? []; List<NotificationModel> currentNotifications = storedNotificationsList.map((n) => NotificationModel.fromJson(n)).toList(); if (currentNotifications.any((notification) => notification.id == message.messageId)) { return; } // 添加新通知到列表头部 currentNotifications.insert( 0, NotificationModel( id: message.messageId!, title: message.notification!.title!, body: message.notification!.body!, createdAt: Timestamp.now(), seen: false, type: message.data['type'] ?? '', typeId: message.data['id'] ?? '', ), ); // 保存到本地存储 List<Map<String, dynamic>> notificationsMap = currentNotifications.map((n) => n.toJson()).toList(); myServices.localStorage.write('notifications', notificationsMap); // 同步RxList并触发更新 notifications.value = currentNotifications; notifications.sort((a, b) => b.createdAt.compareTo(a.createdAt)); }
内容的提问来源于stack exchange,提问作者Florez

