You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 19:50:54