Другое

Flutter фоновые уведомления: Полное руководство по исправлению

Исправьте проблемы с Flutter уведомлениями, которые не работают при закрытом приложении. Узнайте решения с Android WorkManager и iOS фоновой для уведомлений.

Как исправить проблему с планированными уведомлениями, которые не работают, когда приложение Flutter закрыто на Android и iOS?

Запланированные уведомления в Flutter могут не работать, когда приложение закрыто, из‑за ограничений конкретных платформ и требований к конфигурации. Чтобы исправить эту проблему, необходимо реализовать правильную обработку фоновых задач, корректно настроить нативные разрешения и использовать подходящие плагины уведомлений, поддерживающие выполнение в фоне как на Android, так и на iOS.


Contents


Понимание проблемы

Когда ваше Flutter‑приложение закрыто, операционная система приостанавливает его процессы, включая любые сервисы планирования уведомлений. Это поведение преднамеренно как в Android, так и в iOS, чтобы экономить заряд батареи и системные ресурсы. Однако это создаёт сложности для разработчиков, которым необходимо, чтобы уведомления срабатывали даже тогда, когда приложение не активно.

Основные проблемы:

  • Ограничения фонового выполнения Android: с Android 6.0 (Marshmallow) Google постепенно ограничивает выполнение приложений в фоне.
  • Ограничения фонового обновления iOS: iOS имеет строгие правила о том, когда фоновые процессы могут работать.
  • Ограничения плагинов: не все Flutter‑плагины уведомлений поддерживают выполнение в фоне.
  • Отсутствие нативных настроек: правильная настройка в нативных проектах Android и iOS часто упускается из виду.

Многие разработчики сталкиваются с этой проблемой, полагая, что код Dart будет продолжать работать, когда приложение закрыто, но это не так.


Решения для Android

Реализация WorkManager

Для Android наиболее надёжный подход – использовать WorkManager для планирования уведомлений даже при закрытом приложении:

yaml
# pubspec.yaml
dependencies:
  workmanager: ^0.5.2
  flutter_local_notifications: ^16.0.0
dart
import 'package:workmanager/workmanager.dart';

void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) async {
    // Notification logic here
    await FlutterLocalNotificationsPlugin().show(
      0,
      'Scheduled Notification',
      'This is a scheduled notification',
      const NotificationDetails(
        android: AndroidNotificationDetails(
          'main_channel',
          'Main Channel',
          channelDescription: 'Main channel notifications',
          importance: Importance.max,
          priority: Priority.high,
        ),
      ),
    );
    return Future.value(true);
  });
}

void scheduleNotification() {
  Workmanager().initialize(
    callbackDispatcher,
    isInDebugMode: true,
  );
  
  Workmanager().registerPeriodicTask(
    '1',
    'simplePeriodicTask',
    frequency: const Duration(minutes: 15),
    constraints: Constraints(
      networkType: NetworkType.not_required,
    ),
  );
}

Конфигурация AndroidManifest.xml

Добавьте это в ваш AndroidManifest.xml:

xml
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

<application
    ...>
    
    <receiver
        android:name="com.fluttercandies.flutter_local_notifications.ScheduledNotificationReceiver"
        android:enabled="true"
        android:exported="false" />
    
    <receiver
        android:name="com.fluttercandies.flutter_local_notifications.ScheduledBootReceiver"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
            <action android:name="android.intent.action.QUICKBOOT_POWERON" />
            <action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
        </intent-filter>
    </receiver>
</application>

Решения для iOS

Настройка режимов фона

Для iOS необходимо включить режимы фона в вашем Info.plist:

xml
<key>UIBackgroundModes</key>
<array>
    <string>background-fetch</string>
    <string>background-processing</string>
    <string>remote-notification</string>
</array>

Конфигурация AppDelegate

Измените ваш AppDelegate.swift:

swift
import UIKit
import Flutter
import UserNotifications

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
    
    if #available(iOS 10.0, *) {
      UNUserNotificationCenter.current().delegate = self
    }
    
    // Request notification permissions
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
      if granted {
        print("Notification permission granted")
      }
    }
    
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

Реализация фонового получения

swift
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    // Schedule notification logic here
    // Call completionHandler with the appropriate result
    completionHandler(.newData)
}

Рекомендации по плагинам

flutter_local_notifications

Самое полное решение:

yaml
dependencies:
  flutter_local_notifications: ^16.0.0

Правильно инициализируйте его:

dart
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();

Future<void> _initNotifications() async {
  const AndroidInitializationSettings initializationSettingsAndroid =
      AndroidInitializationSettings('@mipmap/ic_launcher');

  const DarwinInitializationSettings initializationSettingsIOS =
      DarwinInitializationSettings(
        requestAlertPermission: true,
        requestBadgePermission: true,
        requestSoundPermission: true,
        onDidReceiveLocalNotification: _onDidReceiveLocalNotification,
      );

  const InitializationSettings initializationSettings =
      InitializationSettings(
        android: initializationSettingsAndroid,
        iOS: initializationSettingsIOS,
      );

  await flutterLocalNotificationsPlugin.initialize(initializationSettings);
}

void _onDidReceiveLocalNotification(
  int id,
  String? title,
  String? body,
  String? payload,
) {
  // Handle foreground notifications
}

workmanager + flutter_local_notifications

Для самой надёжной планировки комбинируйте оба плагина:

dart
void scheduleNotification() {
  Workmanager().registerOneOffTask(
    'task-identifier',
    'simpleTask',
    initialDelay: Duration(minutes: 5),
    existingWorkPolicy: ExistingWorkPolicy.replace,
  );
}

Советы по отладке

Тестирование фонового выполнения

  1. Принудительно закройте приложение: используйте переключатель приложений, чтобы полностью закрыть ваше приложение.
  2. Установите время устройства: продвиньте время устройства, чтобы вызвать запланированные уведомления.
  3. Проверьте логи устройства: ищите записи о фоновых выполнениях.

Общие проблемы и решения

Проблема Решение
Уведомления не срабатывают Проверьте настройки оптимизации батареи
Разрешения отклонены Убедитесь, что запросы разрешений выполнены корректно
Инициализация плагинов Убедитесь, что плагины инициализированы до использования
Фоновые процессы убиты Проверьте с отключённой оптимизацией батареи

Информация для отладки

Добавьте логирование, чтобы проверить фоновое выполнение:

dart
Workmanager().registerPeriodicTask(
  '1',
  'simplePeriodicTask',
  frequency: Duration(minutes: 15),
  inputData: {'debug': 'true'},
  constraints: Constraints(
    networkType: NetworkType.not_required,
  ),
);

Лучшие практики

Обработка разрешений

Всегда запрашивайте разрешения корректно:

dart
Future<void> _requestPermissions() async {
  if (Platform.isAndroid) {
    final AndroidFlutterLocalNotificationsPlugin =
        flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<
            AndroidFlutterLocalNotificationsPlugin>();
    await AndroidFlutterLocalNotificationsPlugin?.requestNotificationsPermission();
  }
  
  if (Platform.isIOS) {
    await flutterLocalNotificationsPlugin
        .resolvePlatformSpecificImplementation<
            IOSFlutterLocalNotificationsPlugin>()
        ?.requestPermissions(
          alert: true,
          badge: true,
          sound: true,
        );
  }
}

Оптимизация батареи

Обрабатывайте оптимизацию батареи на Android:

dart
if (await PowerSaveMode.isIgnoringBatteryOptimizations()) {
  // Proceed with background work
} else {
  // Request permission to ignore battery optimization
  await PowerSaveMode.requestIgnoreBatteryOptimizations();
}

Стратегия тестирования

  1. Тестируйте на реальных устройствах, а не в эмуляторе.
  2. Тестируйте как в переднем, так и в заднем плане.
  3. Тестируйте с разными настройками оптимизации батареи.
  4. Тестируйте с разными версиями iOS и Android.

Альтернативные решения

Для сложных задач планирования:

  • Firebase Cloud Messaging: для серверного планирования.
  • Firebase Functions: для более сложной логики уведомлений.
  • Background Isolate: для длительных фоновых задач.

Заключение

Запланированные уведомления в Flutter требуют реализации платформенно‑специфичных решений, чтобы работать при закрытом приложении. Ключевые решения включают:

  1. Использование WorkManager на Android для надёжного фонового выполнения.
  2. Настройку режимов фона на iOS для разрешения периодических фоновых задач.
  3. Правильную обработку разрешений для обеих платформ.
  4. Выбор подходящей комбинации плагинов, поддерживающих фоновое выполнение.

Всегда тщательно тестируйте реализацию на реальных устройствах с разными настройками оптимизации батареи и версиями ОС. Помните, что мобильные ОС имеют строгие политики фонового выполнения, поэтому понимание этих ограничений критически важно для успешного планирования уведомлений.

Для наиболее надёжных результатов комбинируйте flutter_local_notifications с workmanager и убедитесь в правильной нативной конфигурации в проектах Android и iOS.


Источники

  1. Flutter Local Notifications Plugin Documentation
  2. WorkManager Plugin Documentation
  3. Android Background Execution Documentation
  4. iOS Background Execution Documentation
Авторы
Проверено модерацией
Модерация