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

Flutter后台服务通知常驻通知栏,如何隐藏该通知?

问题:隐藏后台服务通知同时保持Firebase推送接收

我在Flutter应用中使用flutter_local_notifications包实现了应用终止状态下接收Firebase推送通知,但后台服务弹出的通知给用户带来了不佳体验。请问能否在保持后台服务正常运行的前提下,隐藏这个通知?

现有代码

import 'dart:developer';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_background_service/flutter_background_service.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:zooper_ai/app/componets/messenger.dart';
import 'package:zooper_ai/app/core/color.dart';
import 'package:zooper_ai/app/helper/sqflite_db/database_functions.dart';
import 'package:zooper_ai/app/modules/drawer_menu/adhoc_bookings/view/adhoc_booking.dart';
import 'package:zooper_ai/app/modules/bottomsheet/botttom_sheet_screen/view/bottom_sheet.dart';
import 'package:zooper_ai/app/modules/splash/view/splash_screen.dart';
import 'package:zooper_ai/app/providers/providers.dart';
import 'package:zooper_ai/app/routes/routes.dart';
import 'package:zooper_ai/app/services/notification/notification_services.dart';

@pragma('vm:entry-point')
Future<void> firebaseMessagingBackgroundHandler(message) async {
  await Firebase.initializeApp();
  await FirebaseMessaging.instance.getToken();
  NotificationServices()
      .sendNotification("TestApp", "${message.data['notificationTitle']}");
}

void initialiseNotification() async {
  InitializationSettings initializationSettings = InitializationSettings(
    android: NotificationServices().androidInitializationSettings,
  );

  await NotificationServices().flutterLocalNotificationsPlugin.initialize(
      initializationSettings,
      onSelectNotification: notificationResponse
      // onDidReceiveNotificationResponse: notificationResponse,
      // onDidReceiveBackgroundNotificationResponse: notificationResponse,
      );
}

Future<dynamic> notificationResponse(args) async {
  if (NotificationServices().notifyredirect != null) {
    log("if some notifications come");
    log(NotificationServices().notifyredirect.toString());
    if (NotificationServices().notifyredirect ==
        "notificationCustomerConfigUpdate") {
      Routes.push(
          screen: CurvedBottomNavBar(
        page: 1,
      ));
    } else if (NotificationServices().notifyredirect ==
        "notificationOnGeofenceTrigger") {
      NotificationServices().findNotifiedRide();
    }

    //log("| asss ride id   ass $NotificationServices().notifyredirect");
  }
}

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await ScreenUtil.ensureScreenSize();
  FlutterBackgroundService();
  //if (await checkInternetConnectivity()) {
  log("here from here");
  await Firebase.initializeApp();
  await FirebaseMessaging.instance.getToken();
  FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);

  FirebaseMessaging.instance.getInitialMessage().then((message) {
    if (message != null) {
      // RemoteNotification? notification = message!.notification;
      AndroidNotificationDetails androidNotificationDetails =
          AndroidNotificationDetails(NotificationServices().channel.id,
              NotificationServices().channel.name,
              importance: Importance.high,
              priority: Priority.high,
              playSound: true);

      NotificationDetails notificationDetails =
          NotificationDetails(android: androidNotificationDetails);

      //AndroidNotification? android = message.notification?.android;
      var channelId = 0;
      print(
          "  notification details : ${NotificationServices().flutterLocalNotificationsPlugin}");
      // if (message.data['notificationType'] == 'notificationOnGeofenceTrigger') {
      //   channelId = 2;

      NotificationServices().notifyredirect = message.data['notificationType'];
      NotificationServices().rideIdNotification = message.data['rideId'];
      NotificationServices().flutterLocalNotificationsPlugin.show(channelId,
          "Zooper", "${message.data['notificationTitle']}", notificationDetails,
          payload: 'mesaf');

      FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
          alert: true, badge: true, sound: true);
    } else {
      log("data is null");
    }
  });
  // final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
  //     FlutterLocalNotificationsPlugin();
  //     flutterLocalNotificationsPlugin.cancel(notificationId);

  FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
    RemoteNotification? notification = message.notification;
    AndroidNotificationDetails androidNotificationDetails =
        AndroidNotificationDetails(NotificationServices().channel.id,
            NotificationServices().channel.name,
            importance: Importance.high,
            priority: Priority.high,
            playSound: true);

    NotificationDetails notificationDetails =
        NotificationDetails(android: androidNotificationDetails);

    //AndroidNotification? android = message.notification?.android;
    var channelId = 0;
    print(
        "  notification details : ${NotificationServices().flutterLocalNotificationsPlugin}");
    // if (message.data['notificationType'] == 'notificationOnGeofenceTrigger') {
    //   channelId = 2;

    NotificationServices().notifyredirect = message.data['notificationType'];
    NotificationServices().rideIdNotification = message.data['rideId'];
    NotificationServices().flutterLocalNotificationsPlugin.show(channelId,
        "Zooper", "${message.data['notificationTitle']}", notificationDetails,
        payload: 'mesaf');

    FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
        alert: true, badge: true, sound: true);
  });

  FirebaseMessaging.instance.getToken();
  FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
    if (message != null) {
      NotificationServices()
          .sendNotification("Zooper", "${message.data['notificationTitle']}");
    } else {
      log("backround from");
    }
    // print("A new onMessageOpenedApp event was published");
    // print("Messagenotfi ${message.data['notificationTitle']}");
  });

  initialiseNotification();
  SystemChrome.setPreferredOrientations(
      [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);

  await initiolizeDatabase();
  await getcityList();
  await gettimeSloteSqflite();
  await getRosterSqflite();
  await getCompanyInfoSqflit();
  runApp(Provders.provders());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // initState() {

  // super.initState();
  // }

  void showNotification() {
    NotificationServices().flutterLocalNotificationsPlugin.show(
        0,
        "Testing",
        "Hiw you doing",
        NotificationDetails(
            android: AndroidNotificationDetails(
                NotificationServices().channel.id,
                NotificationServices().channel.name,
                channelDescription: NotificationServices().channel.description,
                importance: Importance.high,
                color: Colors.blue,
                playSound: true,
                icon: 'assets/ui/images/Empty.png')));
  }

  @override
  Widget build(BuildContext context) {
    return ScreenUtilInit(
        splitScreenMode: false,
        minTextAdapt: true,
        builder: (BuildContext context, Widget? child) {
          return MaterialApp(
              initialRoute: '/',
              builder: (BuildContext context, Widget? child) {
                return MediaQuery(
                  data: MediaQuery.of(context).copyWith(
                    textScaleFactor: 0.85,
                  ),
                  child: child!,
                );
              },
              routes: {
                'bottomSheet': (context) => CurvedBottomNavBar(),
                '/adhoc': (context) => const MyAdhoc(),
              },
              navigatorKey: Routes.navigatorKey,
              debugShowCheckedModeBanner: false,
              title: 'Test',
            scaffoldMessengerKey:Messenger.rootScaffoldMessengerKey,
              //darkTheme: darkThemeData(context),
              theme: lightThemeData(context),
              home: const SplashScreen());
        });
  }
}

解决方案

1. 避免后台消息重复弹出通知

你的firebaseMessagingBackgroundHandler里手动调用了sendNotification,但Firebase在应用终止状态下收到通知消息(带notification字段)时,系统会自动弹出通知,手动调用会导致重复。只需要在后台处理数据消息(无notification字段)的逻辑,不要显示通知:

@pragma('vm:entry-point')
Future<void> firebaseMessagingBackgroundHandler(message) async {
  await Firebase.initializeApp();
  await FirebaseMessaging.instance.getToken();
  
  // 仅处理数据消息,通知消息由系统自动处理
  if (message.notification == null) {
    log("后台处理数据消息: ${message.data}");
    // 这里执行你的后台逻辑,比如更新本地数据库、触发其他操作
  }
}

2. 移除启动时的重复通知

getInitialMessage中你又调用了flutterLocalNotificationsPlugin.show,这会导致应用从终止状态启动时重复弹出系统已经显示过的通知,直接处理跳转逻辑即可:

FirebaseMessaging.instance.getInitialMessage().then((message) {
  if (message != null) {
    NotificationServices().notifyredirect = message.data['notificationType'];
    NotificationServices().rideIdNotification = message.data['rideId'];
    
    // 直接处理跳转,不要再次显示通知
    if (NotificationServices().notifyredirect == "notificationCustomerConfigUpdate") {
      Routes.push(screen: CurvedBottomNavBar(page: 1));
    } else if (NotificationServices().notifyredirect == "notificationOnGeofenceTrigger") {
      NotificationServices().findNotifiedRide();
    }
  } else {
    log("data is null");
  }
});

3. 配置静默后台服务通知

如果你用了flutter_background_service,它默认会显示后台服务通知。可以配置一个完全静默的通知渠道,让通知不在状态栏显示:

Future<void> initBackgroundService() async {
  final service = FlutterBackgroundService();
  await service.configure(
    androidConfiguration: AndroidConfiguration(
      onStart: onStart, // 你的后台任务逻辑
      autoStart: true,
      isForegroundMode: true,
      // 配置静默通知
      foregroundServiceNotification: AndroidNotificationDetails(
        'silent_service',
        '后台服务',
        channelDescription: '应用后台运行中',
        importance: Importance.low,
        priority: Priority.low,
        playSound: false,
        enableVibration: false,
        showBadge: false,
        visibility: NotificationVisibility.secret, // 完全隐藏通知
      ),
    ),
    iosConfiguration: IosConfiguration(
      autoStart: true,
      onForeground: onStart,
      onBackground: (service) async {
        service.setAutoStartOnBoot(true);
        return true;
      },
    ),
  );
}

然后在main中替换原来的FlutterBackgroundService();,调用这个初始化函数。

4. Android版本特殊处理

Android 12+要求前台服务必须显示通知,无法完全隐藏,但可以通过以下方式优化:

  • 创建单独的静默通知渠道,引导用户在系统设置中关闭该渠道的通知
  • 使用NotificationVisibility.secret让通知不在状态栏显示,仅在下拉通知中心可见(如果必须保留前台服务)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 12:32:19