Flutter前台任务管理插件flutter_foreground_task_native的使用

Flutter前台任务管理插件flutter_foreground_task_native的使用

本插件用于在Android平台上实现前台服务。

pub package

特性

  • 可以通过前台服务执行重复任务。
  • 提供有用的功能(最小化应用、唤醒屏幕等)可以在执行任务时使用。
  • 提供一个可以防止应用程序在前台服务运行时关闭的小部件。
  • 提供一个小部件,当应用被最小化或关闭时启动前台服务。
  • 提供一个选项,可以在设备重启时自动恢复前台服务。

开始使用

要使用此插件,请在pubspec.yaml文件中添加flutter_foreground_task作为依赖。例如:

dependencies:
  flutter_foreground_task_native: ^1.0.1

在将flutter_foreground_task插件添加到Flutter项目后,我们需要指定权限和服务,以便该插件能够正常工作。

Android

由于此插件基于前台服务,因此需要向AndroidManifest.xml文件中添加以下权限。打开AndroidManifest.xml文件,并将其放在<manifest><application>标签之间。

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

还需要添加此权限以便在启动时自动恢复前台服务。

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

并指定服务,如下所示:

<service android:name="com.pravera.flutter_foreground_task.service.ForegroundService" />
iOS

我们也可以在iOS平台上启动flutter_foreground_task。然而,它有以下限制:

  • 仅适用于iOS 10.0或更高版本。
  • 如果应用程序被强制关闭,任务将不会工作。
  • 无法在设备重新启动时自动启动任务。
  • 由于平台的后台处理限制,onEvent事件可能无法在后台正常工作。但在前台运行时表现良好。

Objective-C:

  1. 若要在使用Objective-C的项目中使用用Swift语言开发的插件,你需要添加桥接头文件。如果你的项目中没有ios/Runner/Runner-Bridging-Header.h文件,请参阅此页面

  2. 打开ios/Runner/AppDelegate.swift文件并添加注释代码。

#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"

// here
#import <flutter_foreground_task/FlutterForegroundTaskPlugin.h>

// here
void registerPlugins(NSObject<FlutterPluginRegistry>* registry) {
  [GeneratedPluginRegistrant registerWithRegistry:registry];
}

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [GeneratedPluginRegistrant registerWithRegistry:self];

  // here, Without this code the task will not work.
  [FlutterForegroundTaskPlugin setPluginRegistrantCallback:registerPlugins];
  if (@available(iOS 10.0, *)) {
    [UNUserNotificationCenter currentNotificationCenter].delegate = (id<UNUserNotificationCenterDelegate>) self;
  }

  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

@end

Swift:

  1. ios/Runner/Runner-Bridging-Header.h文件中声明导入语句。
#import <flutter_foreground_task/FlutterForegroundTaskPlugin.h>
  1. 打开ios/Runner/AppDelegate.swift文件并添加注释代码。
import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)

    // here, Without this code the task will not work.
    SwiftFlutterForegroundTaskPlugin.setPluginRegistrantCallback(registerPlugins)
    if #available(iOS 10.0, *) {
      UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
    }

    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

// here
func registerPlugins(registry: FlutterPluginRegistry) {
  GeneratedPluginRegistrant.register(with: registry)
}

如何使用

此插件有两种方式启动前台任务。一种是手动启动前台任务,另一种是在应用最小化或关闭时通过WillStartForegroundTask小部件启动。

手动启动
  1. 初始化FlutterForegroundTask。你可以使用FlutterForegroundTask.init()函数来设置通知和任务选项。
Future<void> _initForegroundTask() async {
  await FlutterForegroundTask.init(
    androidNotificationOptions: AndroidNotificationOptions(
      channelId: 'notification_channel_id',
      channelName: 'Foreground Notification',
      channelDescription: 'This notification appears when the foreground service is running.',
      channelImportance: NotificationChannelImportance.LOW,
      priority: NotificationPriority.LOW,
      iconData: const NotificationIconData(
        resType: ResourceType.mipmap,
        resPrefix: ResourcePrefix.ic,
        name: 'launcher',
      ),
      buttons: [
        const NotificationButton(id: 'sendButton', text: 'Send'),
        const NotificationButton(id: 'testButton', text: 'Test'),
      ],
    ),
    iosNotificationOptions: const IOSNotificationOptions(
      showNotification: true,
      playSound: false,
    ),
    foregroundTaskOptions: const ForegroundTaskOptions(
      interval: 5000,
      autoRunOnBoot: true,
      allowWifiLock: true,
    ),
    printDevLog: true,
  );
}

@override
void initState() {
  super.initState();
  _initForegroundTask();
}
  1. 添加WithForegroundTask小部件以防止应用在前台服务运行时关闭。
@override
Widget build(BuildContext context) {
  return MaterialApp(
    // A widget that prevents the app from closing when the foreground service is running.
    // This widget must be declared above the [Scaffold] widget.
    home: WithForegroundTask(
      child: Scaffold(
        appBar: AppBar(
          title: const Text('Flutter Foreground Task'),
          centerTitle: true,
        ),
        body: buildContentView(),
      ),
    ),
  );
}
  1. 编写回调和处理器并启动前台服务。FlutterForegroundTask.startService()提供了以下选项:
// The callback function should always be a top-level function.
void startCallback() {
  // The setTaskHandler function must be called to handle the task in the background.
  FlutterForegroundTask.setTaskHandler(FirstTaskHandler());
}

class FirstTaskHandler extends TaskHandler {
  @override
  Future<void> onStart(DateTime timestamp, SendPort? sendPort) async {
    // You can use the getData function to get the data you saved.
    final customData = await FlutterForegroundTask.getData<String>(key: 'customData');
    print('customData: $customData');
  }

  @override
  Future<void> onEvent(DateTime timestamp, SendPort? sendPort) async {
    // Send data to the main isolate.
    sendPort?.send(timestamp);
  }

  @override
  Future<void> onDestroy(DateTime timestamp) async {
    // You can use the clearAllData function to clear all the stored data.
    await FlutterForegroundTask.clearAllData();
  }

  @override
  void onButtonPressed(String id) {
    // Called when the notification button on the Android platform is pressed.
    print('onButtonPressed >> $id');
  }
}

class ExampleApp extends StatefulWidget {
  const ExampleApp({Key? key}) : super(key: key);

  @override
  _ExampleAppState createState() => _ExampleAppState();
}

class _ExampleAppState extends State<ExampleApp> {
  ReceivePort? _receivePort;

  Future<bool> _startForegroundTask() async {
    // You can save data using the saveData function.
    await FlutterForegroundTask.saveData(key: 'customData', value: 'hello');

    ReceivePort? receivePort;
    if (await FlutterForegroundTask.isRunningService) {
      receivePort = await FlutterForegroundTask.restartService();
    } else {
      receivePort = await FlutterForegroundTask.startService(
        notificationTitle: 'Foreground Service is running',
        notificationText: 'Tap to return to the app',
        callback: startCallback,
      );
    }

    if (receivePort != null) {
      _receivePort = receivePort;
      _receivePort?.listen((message) {
        if (message is DateTime) {
          print('receive timestamp: $message');
        }
      });

      return true;
    }

    return false;
  }

  @override
  void dispose() {
    _receivePort?.close();
    super.dispose();
  }
}

如你所见,你可以使用以下功能管理数据:

void function() async {
  await FlutterForegroundTask.getData(key: String);
  await FlutterForegroundTask.saveData(key: String, value: Object);
  await FlutterForegroundTask.removeData(key: String);
  await FlutterForegroundTask.clearAllData();
}

如果插件提供了流,可以这样使用:

class FirstTaskHandler extends TaskHandler {
  StreamSubscription<Position>? streamSubscription;

  @override
  Future<void> onStart(DateTime timestamp, SendPort? sendPort) async {
    final positionStream = Geolocator.getPositionStream();
    streamSubscription = positionStream.listen((event) {
      // Update notification content.
      FlutterForegroundTask.updateService(
          notificationTitle: 'Current Position',
          notificationText: '${event.latitude}, ${event.longitude}');

      // Send data to the main isolate.
      sendPort?.send(event);
    });
  }

  @override
  Future<void> onEvent(DateTime timestamp, SendPort? sendPort) async {

  }

  @override
  Future<void> onDestroy(DateTime timestamp) async {
    await streamSubscription?.cancel();
  }
}
  1. 使用FlutterForegroundTask.updateService()更新前台服务。选项与启动函数相同。
// The callback function should always be a top-level function.
void startCallback() {
  // The setTaskHandler function must be called to handle the task in the background.
  FlutterForegroundTask.setTaskHandler(FirstTaskHandler());
}

class FirstTaskHandler extends TaskHandler {
  int updateCount = 0;

  @override
  Future<void> onStart(DateTime timestamp, SendPort? sendPort) async {

  }

  @override
  Future<void> onEvent(DateTime timestamp, SendPort? sendPort) async {
    FlutterForegroundTask.updateService(
        notificationTitle: 'FirstTaskHandler',
        notificationText: timestamp.toString(),
        callback: updateCount >= 10 ? updateCallback : null);

    // Send data to the main isolate.
    sendPort?.send(timestamp);
    sendPort?.send(updateCount);

    updateCount++;
  }

  @override
  Future<void> onDestroy(DateTime timestamp) async {

  }
}

void updateCallback() {
  FlutterForegroundTask.setTaskHandler(SecondTaskHandler());
}

class SecondTaskHandler extends TaskHandler {
  @override
  Future<void> onStart(DateTime timestamp, SendPort? sendPort) async {

  }

  @override
  Future<void> onEvent(DateTime timestamp, SendPort? sendPort) async {
    FlutterForegroundTask.updateService(
        notificationTitle: 'SecondTaskHandler',
        notificationText: timestamp.toString());

    // Send data to the main isolate.
    sendPort?.send(timestamp);
  }

  @override
  Future<void> onDestroy(DateTime timestamp) async {

  }
}
  1. 如果不再使用前台服务,调用FlutterForegroundTask.stopService()
Future<bool> _stopForegroundTask() async {
  return await FlutterForegroundTask.stopService();
}
使用WillStartForegroundTask小部件启动
@override
Widget build(BuildContext context) {
  return MaterialApp(
    // A widget to start the foreground service when the app is minimized or closed.
    // This widget must be declared above the [Scaffold] widget.
    home: WillStartForegroundTask(
      onWillStart: () async {
        // Return whether to start the foreground service.
        return true;
      },
      androidNotificationOptions: AndroidNotificationOptions(
        channelId: 'notification_channel_id',
        channelName: 'Foreground Notification',
        channelDescription: 'This notification appears when the foreground service is running.',
        channelImportance: NotificationChannelImportance.LOW,
        priority: NotificationPriority.LOW,
        iconData: NotificationIconData(
          resType: ResourceType.mipmap,
          resPrefix: ResourcePrefix.ic,
          name: 'launcher',
        ),
      ),
      iosNotificationOptions: const IOSNotificationOptions(
        showNotification: true,
        playSound: false,
      ),
      foregroundTaskOptions: const ForegroundTaskOptions(
        interval: 5000,
        autoRunOnBoot: false,
        allowWifiLock: false,
      ),
      printDevLog: true,
      notificationTitle: 'Foreground Service is running',
      notificationText: 'Tap to return to the app',
      callback: startCallback,
      child: Scaffold(
        appBar: AppBar(
          title: const Text('Flutter Foreground Task'),
          centerTitle: true,
        ),
        body: buildContentView(),
      ),
    ),
  );
}

模型

AndroidNotificationOptions

Android平台的通知选项。

属性 描述
channelId 通知渠道的唯一ID。
channelName 通知渠道的名称。此值在通知设置中显示给用户。
channelDescription 通知渠道的描述。此值在通知设置中显示给用户。
channelImportance 通知渠道的重要性。默认为NotificationChannelImportance.DEFAULT
priority Android 7.1及以下版本的通知优先级。默认为NotificationPriority.DEFAULT
enableVibration 创建通知时是否启用振动。默认为false
playSound 创建通知时是否播放声音。默认为false
showWhen 是否在内容视图中显示通知创建的时间戳。默认为false
isSticky 系统是否会重新启动服务,如果服务被杀死。默认为true
visibility 控制在锁定屏幕上显示的通知详细程度。默认为NotificationVisibility.VISIBILITY_PUBLIC
iconData 在通知中显示的图标数据。如果值为空,则使用应用程序启动器图标。
buttons 在通知中显示的按钮列表。最多允许三个。
NotificationIconData

设置通知图标的数据。

属性 描述
resType 通知图标的资源类型。如果资源在drawable文件夹中,则设置为ResourceType.drawable;如果资源在mipmap文件夹中,则设置为ResourceType.mipmap
resPrefix 通知图标的资源前缀。如果通知图标名称为ic_simple_notification,则设置为ResourcePrefix.ic,并将name设置为simple_notification
name 不带前缀的通知图标名称。
ResourceType

通知图标的资源类型。

描述
drawable 存储在drawable文件夹中的资源。drawable文件夹用于存储各种图像。
mipmap 存储在mipmap文件夹中的资源。mipmap文件夹通常用于存储启动器图标图像。
ResourcePrefix

通知图标的资源前缀。

描述
ic 具有ic_前缀的资源。
img 具有img_前缀的资源。
NotificationButton

在通知中显示的按钮。

属性 描述
id 按钮标识符。
text 显示在按钮上的文本。
IOSNotificationOptions

iOS平台的通知选项。

属性 描述
showNotification 是否显示通知。默认为true
playSound 创建通知时是否播放声音。默认为false
ForegroundTaskOptions

包含前台任务选项的数据类。

属性 描述
interval 任务调用间隔时间(毫秒)。默认为5000
autoRunOnBoot 是否在启动时自动运行前台任务。默认为false
allowWifiLock 允许应用程序保持Wi-Fi无线电唤醒。默认为false
NotificationChannelImportance

通知渠道的重要性。

描述
NONE 没有重要性的通知:不会在阴影中显示。
MIN 最低通知重要性:仅在阴影中显示,位于折叠部分。
LOW 低通知重要性:在阴影中显示,并可能在状态栏中显示(参见shouldHideSilentStatusBarIcons()),但不会发出听觉干扰。
DEFAULT 默认通知重要性:在所有地方显示,发出声音,但不会视觉干扰。
HIGH 较高的通知重要性:在所有地方显示,发出声音并弹出。可能使用全屏意图。
MAX 最大通知重要性:与HIGH相同,但通常不使用。
NotificationPriority

Android 7.1及以下版本的通知优先级。

描述
MIN 没有声音且不会出现在状态栏中。
LOW 没有声音。
DEFAULT 发出声音。
HIGH 发出声音并以抬头通知的形式出现。
MAX 与HIGH相同,但在需要立即通知时使用。
NotificationVisibility

在锁定屏幕上显示的通知详细程度。

描述
VISIBILITY_PUBLIC 在所有锁定屏幕上完全显示此通知。
VISIBILITY_SECRET 在安全锁定屏幕上不显示此通知的任何部分。
VISIBILITY_PRIVATE 在所有锁定屏幕上显示此通知,但在安全锁定屏幕上隐藏敏感或私人信息。

工具方法

minimizeApp (Both)

最小化应用程序到后台。

import 'package:flutter_foreground_task/flutter_foreground_task.dart';

void function() => FlutterForegroundTask.minimizeApp();
wakeUpScreen (Android)

唤醒关机设备的屏幕。

import 'package:flutter_foreground_task/flutter_foreground_task.dart';

void function() => FlutterForegroundTask.wakeUpScreen();
isIgnoringBatteryOptimizations (Android)

返回应用程序是否已排除电池优化。

import 'package:flutter_foreground_task/flutter_foreground_task.dart';

Future<bool> function() => FlutterForegroundTask.isIgnoringBatteryOptimizations;
openIgnoreBatteryOptimizationSettings (Android)

打开设置页面,可以设置忽略电池优化。

import 'package:flutter_foreground_task/flutter_foreground_task.dart';

Future<bool> function() => FlutterForegroundTask.openIgnoreBatteryOptimizationSettings();
requestIgnoreBatteryOptimization (Android)

请求忽略电池优化。此功能需要android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS权限。

import 'package:flutter_foreground_task/flutter_foreground_task.dart';

Future<bool> function() => FlutterForegroundTask.requestIgnoreBatteryOptimization();

更多关于Flutter前台任务管理插件flutter_foreground_task_native的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter前台任务管理插件flutter_foreground_task_native的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中集成并使用flutter_foreground_task_native插件的一个代码案例。这个插件允许你在Flutter应用中执行前台任务,比如长时间运行的服务,同时显示一个通知给用户。

步骤 1: 添加依赖

首先,你需要在pubspec.yaml文件中添加flutter_foreground_task_native依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_foreground_task_native: ^最新版本号 # 请替换为最新的版本号

然后运行flutter pub get来获取依赖。

步骤 2: 配置Android和iOS

Android配置

android/app/src/main/AndroidManifest.xml中,你需要添加前台服务所需的权限:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.yourapp">

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <!-- 其他权限 -->

    <application
        ...>
        <!-- 其他配置 -->
    </application>
</manifest>

iOS配置

对于iOS,你通常不需要额外的配置,但确保你的Info.plist文件包含了必要的权限声明(如果需要)。

步骤 3: 使用插件

以下是一个如何在Flutter中使用flutter_foreground_task_native插件的示例代码:

import 'package:flutter/material.dart';
import 'package:flutter_foreground_task_native/flutter_foreground_task_native.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  FlutterForegroundTaskNative _foregroundTaskPlugin = FlutterForegroundTaskNative();

  @override
  void initState() {
    super.initState();
    initForegroundTask();
  }

  void initForegroundTask() async {
    // 配置前台任务
    ForegroundTaskConfig config = ForegroundTaskConfig(
      taskId: "1", // 任务的唯一标识符
      isAutoRemove: false, // 任务完成后是否自动移除
      foregroundNotification: ForegroundNotification(
        title: "Foreground Task",
        content: "This is a foreground task running in the background.",
        priority: NotificationPriority.HIGH,
        importance: NotificationImportance.HIGH,
        channelId: "foreground_channel", // 确保在Android上创建了对应的通知渠道
      ),
    );

    // 注册前台任务
    await _foregroundTaskPlugin.registerTask(config);

    // 开始前台任务
    await _foregroundTaskPlugin.startTask("1");

    // 模拟长时间运行的任务
    _executeLongRunningTask();
  }

  void _executeLongRunningTask() async {
    // 模拟一个长时间运行的任务,比如每隔1秒打印一次日志
    for (int i = 0; i < 60; i++) { // 运行60秒
      print("Foreground task running: $i seconds");
      await Future.delayed(Duration(seconds: 1));
    }

    // 任务完成后停止前台任务
    await _foregroundTaskPlugin.stopTask("1");
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Foreground Task Demo'),
        ),
        body: Center(
          child: Text('Foreground task will start in the background.'),
        ),
      ),
    );
  }
}

注意事项

  1. 通知渠道(仅Android):确保在Android上创建了与channelId相匹配的通知渠道。这通常在MainActivity.ktMainActivity.java中配置。

  2. 权限处理:在实际应用中,你需要处理权限请求,特别是FOREGROUND_SERVICE权限,确保用户在运行时授予了权限。

  3. 后台限制:不同设备和操作系统版本对后台任务的限制可能不同,确保测试你的应用在各种环境下的行为。

这个示例展示了如何使用flutter_foreground_task_native插件来注册和启动一个前台任务,并模拟了一个长时间运行的任务。根据实际需求,你可以调整任务的逻辑和配置。

回到顶部