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

Flutter Android应用启动图标通知角标实现问题求助

Android应用启动图标角标未显示问题排查

我希望在Android应用的启动图标旁显示用户未读消息数量的通知角标,但尝试相关代码后未能成功实现。以下是我编写的main.dart代码,请求帮忙排查错误:

import 'package:flutter_app_badger/flutter_app_badger.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:get/get.dart';
import 'package:liquitech/services/notificationService.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await GetStorage.init();
  await MpesaFlutterPlugin.setConsumerKey(MyConfig.mpesaConsumerKey);
  await MpesaFlutterPlugin.setConsumerSecret(MyConfig.mpesaConsumerSecret);
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  ).then((value) => Get.put(AuthRepository()));

  SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
      .then((_) {
    FirebaseMessaging.onBackgroundMessage(backgroundHandler);
    runApp(const MyApp());
  });
}

class _MyAppState extends State<MyApp> {
  final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
  
   _firebaseMessaging.requestPermission();
    _firebaseMessaging.setForegroundNotificationPresentationOptions(
        alert: true, badge: true, sound: true);
    _firebaseMessaging.getToken().then((token) {
      print("token:$token");
    });
  
  

  @override
  initState() {
    NotificationApi.init();
    listenNotifications();

   _firebaseMessaging.getInitialMessage().then((message) async {
      final routeName = message?.data["route"];
      if (routeName != null) {
        Get.toNamed(routeName);
      }
    });

    //foreground
    FirebaseMessaging.onMessage.listen((message) {
      int badgeCount = int.tryParse(message.data['badge'] ?? '0') ?? 0;
      if (badgeCount > 0) {
        SystemChrome.setApplicationSwitcherDescription(
          ApplicationSwitcherDescription(
              label: myAppName, primaryColor: Colors.blue.value),
        );
        SystemChrome.setSystemUIOverlayStyle(
          const SystemUiOverlayStyle(statusBarColor: Colors.blue),
        );
        _setNotificationBadge(badgeCount);
      }
      NotificationApi.showNotification(
          title: message.notification!.title,
          body: message.notification!.body,
          payload: "sarah.abs");
    });

    //background and not terminated and user taps on the notification
    FirebaseMessaging.onMessageOpenedApp.listen((message) {
      final routeName = message.data["route"];
      if (routeName != null) {
        Get.toNamed(routeName);
      }
    });
  }

  void listenNotifications() {
    NotificationApi.onNotifications.stream.listen((onClickedNotification));
  }

  void onClickedNotification(String? payload) {
    Navigator.of(context).push(MaterialPageRoute(
        builder: (BuildContext context) => const OpenedNotificationPage()));
  }

  }

  
  @override
  Widget build(BuildContext context) {
   return ChangeNotifierProvider(
      create: (context) => GoogleSignInProvider(),
      child: GetMaterialApp(
        title: myAppName,
        debugShowCheckedModeBanner: false,
        theme: isDarkMode ? ThemeData.dark() : ThemeData.light(),
        defaultTransition: Transition.leftToRightWithFade,
        transitionDuration: const Duration(milliseconds: 800),
        darkTheme: MyThemes.darkTheme,
        home: SplashScreen(myAppName: myAppName),
        getPages: [
          GetPage(
              name: "/openedNotification",
              page: () => const OpenedNotificationPage())
        ],
      ),
    );
  }

   _setNotificationBadge(int badgeCount) async {
    if (badgeCount > 0) {
      await FlutterAppBadger.updateBadgeCount(badgeCount);
    } else {
      await FlutterAppBadger.removeBadge();
    }
  }
}

代码错误及修复方案

1. 语法结构错误

  • 问题:_firebaseMessaging.requestPermission()等初始化代码直接写在类的成员变量区域,属于语法错误;_setNotificationBadge方法前有多余的闭合花括号,导致类结构混乱。
  • 修复:将权限请求代码移至initState内部,调整类的代码结构,确保所有方法都在类的正确范围内:
@override
void initState() {
  super.initState();
  // 移到此处执行初始化
  _firebaseMessaging.requestPermission();
  _firebaseMessaging.setForegroundNotificationPresentationOptions(
      alert: true, badge: true, sound: true);
  _firebaseMessaging.getToken().then((token) {
    print("token:$token");
  });
  
  NotificationApi.init();
  listenNotifications();
  // ...其他原有代码
}

2. Android角标兼容性问题

  • 问题:flutter_app_badger在Android上依赖厂商ROM支持,国产机型(小米、华为、OPPO等)需要额外配置:
    • 小米:需在AndroidManifest.xml添加<meta-data android:name="com.xiaomi.mipush.WIFI" android:value="1"/>,并配置小米推送服务
    • 华为:需集成华为推送服务,在后台开启角标权限
    • OPPO/VIVO:需申请厂商推送权限,部分机型要求应用处于后台才显示角标
  • 修复:调用角标更新前先检查设备支持性:
void _setNotificationBadge(int badgeCount) async {
  bool isSupported = await FlutterAppBadger.isAppBadgeSupported();
  if (!isSupported) return;
  
  if (badgeCount > 0) {
    await FlutterAppBadger.updateBadgeCount(badgeCount);
  } else {
    await FlutterAppBadger.removeBadge();
  }
}

3. 消息监听逻辑漏洞

  • 问题:仅在前台收到消息时更新角标,未处理应用后台/终止状态的角标更新:
    • 后台消息无法直接在backgroundHandler中更新角标,需通过本地存储记录未读数量,在应用启动时读取更新
    • 应用启动时未主动初始化角标状态
  • 修复:在initState中主动读取本地存储的未读数量,初始化角标;点击通知时减少未读数量并更新角标:
@override
void initState() {
  super.initState();
  // ...其他代码
  // 启动时初始化角标
  _initBadgeCount();
}

void _initBadgeCount() async {
  // 从本地存储读取未读数量,示例用GetStorage
  int unreadCount = GetStorage().read('unread_count') ?? 0;
  await _setNotificationBadge(unreadCount);
}

// 点击通知时更新未读数量
void onClickedNotification(String? payload) {
  int currentCount = GetStorage().read('unread_count') ?? 0;
  if (currentCount > 0) {
    currentCount--;
    GetStorage().write('unread_count', currentCount);
    _setNotificationBadge(currentCount);
  }
  Navigator.of(context).push(MaterialPageRoute(
      builder: (BuildContext context) => const OpenedNotificationPage()));
}

内容的提问来源于stack exchange,提问作者Kevy Kibbz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 03:53:01