Flutter前台任务管理插件flutter_foreground_task的使用
Flutter前台任务管理插件flutter_foreground_task的使用
Features
- 可以通过前台服务执行重复性任务。
- 支持前台服务与UI(主隔离区)之间的双向通信。
- 提供一个在用户按下软返回按钮时最小化应用程序而不关闭它的组件。
- 提供了在执行任务时可以使用的有用工具。
- 提供了一个选项,可以在启动时自动恢复前台服务。
Support Version
- Flutter:
3.10.0+
- Dart:
3.0.0+
- Android:
5.0+ (minSdkVersion: 21)
- iOS:
12.0+
Getting Started
添加依赖
在pubspec.yaml
文件中添加flutter_foreground_task
作为依赖项:
dependencies:
flutter_foreground_task: ^8.17.0
Android平台配置
打开AndroidManifest.xml
文件并声明以下内容:
<!-- required -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- foregroundServiceType: dataSync -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<!-- foregroundServiceType: remoteMessaging -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING" />
<!-- Warning: Do not change service name. -->
<service
android:name="com.pravera.flutter_foreground_task.service.ForegroundService"
android:foregroundServiceType="dataSync|remoteMessaging"
android:exported="false" />
iOS平台配置
在Info.plist
中添加如下代码:
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.pravera.flutter_foreground_task.refresh</string>
</array>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
</array>
对于Objective-C项目,需要添加桥接头文件,并修改AppDelegate.m
。对于Swift项目,修改AppDelegate.swift
文件。
How to Use
Step by Step
1. 初始化通信端口
void main() {
// Initialize port for communication between TaskHandler and UI.
FlutterForegroundTask.initCommunicationPort();
runApp(const ExampleApp());
}
2. 编写TaskHandler
和callback
@pragma('vm:entry-point')
void startCallback() {
FlutterForegroundTask.setTaskHandler(MyTaskHandler());
}
class MyTaskHandler extends TaskHandler {
@override
Future<void> onStart(DateTime timestamp, TaskStarter starter) async {
print('onStart(starter: ${starter.name})');
}
@override
void onRepeatEvent(DateTime timestamp) {
final Map<String, dynamic> data = {
"timestampMillis": timestamp.millisecondsSinceEpoch,
};
FlutterForegroundTask.sendDataToMain(data);
}
@override
Future<void> onDestroy(DateTime timestamp) async {
print('onDestroy');
}
@override
void onReceiveData(Object data) {
print('onReceiveData: $data');
}
@override
void onNotificationButtonPressed(String id) {
print('onNotificationButtonPressed: $id');
}
@override
void onNotificationPressed() {
print('onNotificationPressed');
}
@override
void onNotificationDismissed() {
print('onNotificationDismissed');
}
}
3. 接收来自TaskHandler
的数据
void _onReceiveTaskData(Object data) {
if (data is Map<String, dynamic>) {
final dynamic timestampMillis = data["timestampMillis"];
if (timestampMillis != null) {
final DateTime timestamp =
DateTime.fromMillisecondsSinceEpoch(timestampMillis, isUtc: true);
print('timestamp: ${timestamp.toString()}');
}
}
}
@override
void initState() {
super.initState();
FlutterForegroundTask.addTaskDataCallback(_onReceiveTaskData);
}
@override
void dispose() {
FlutterForegroundTask.removeTaskDataCallback(_onReceiveTaskData);
super.dispose();
}
4. 请求权限并初始化服务
Future<void> _requestPermissions() async {
final NotificationPermission notificationPermission =
await FlutterForegroundTask.checkNotificationPermission();
if (notificationPermission != NotificationPermission.granted) {
await FlutterForegroundTask.requestNotificationPermission();
}
if (Platform.isAndroid) {
if (!await FlutterForegroundTask.isIgnoringBatteryOptimizations) {
await FlutterForegroundTask.requestIgnoreBatteryOptimization();
}
if (!await FlutterForegroundTask.canScheduleExactAlarms) {
await FlutterForegroundTask.openAlarmsAndRemindersSettings();
}
}
}
void _initService() {
FlutterForegroundTask.init(
androidNotificationOptions: AndroidNotificationOptions(
channelId: 'foreground_service',
channelName: 'Foreground Service Notification',
channelDescription:
'This notification appears when the foreground service is running.',
onlyAlertOnce: true,
),
iosNotificationOptions: const IOSNotificationOptions(
showNotification: false,
playSound: false,
),
foregroundTaskOptions: ForegroundTaskOptions(
eventAction: ForegroundTaskEventAction.repeat(5000),
autoRunOnBoot: true,
autoRunOnMyPackageReplaced: true,
allowWakeLock: true,
allowWifiLock: true,
),
);
}
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
_requestPermissions();
_initService();
});
}
5. 启动服务
Future<ServiceRequestResult> _startService() async {
if (await FlutterForegroundTask.isRunningService) {
return FlutterForegroundTask.restartService();
} else {
return FlutterForegroundTask.startService(
serviceId: 256,
notificationTitle: 'Foreground Service is running',
notificationText: 'Tap to return to the app',
notificationIcon: null,
notificationButtons: [
const NotificationButton(id: 'btn_hello', text: 'hello'),
],
notificationInitialRoute: '/',
callback: startCallback,
);
}
}
6. 更新服务
FlutterForegroundTask.updateService(
notificationTitle: 'Hello SecondTaskHandler :)',
notificationText: timestamp.toString(),
);
7. 停止服务
Future<ServiceRequestResult> _stopService() {
return FlutterForegroundTask.stopService();
}
示例Demo
以下是完整的示例代码,您可以直接复制并在您的Flutter项目中运行:
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
void main() {
// Initialize port for communication between TaskHandler and UI.
FlutterForegroundTask.initCommunicationPort();
runApp(const ExampleApp());
}
// The callback function should always be a top-level or static function.
@pragma('vm:entry-point')
void startCallback() {
FlutterForegroundTask.setTaskHandler(MyTaskHandler());
}
class MyTaskHandler extends TaskHandler {
static const String incrementCountCommand = 'incrementCount';
int _count = 0;
void _incrementCount() {
_count++;
// Update notification content.
FlutterForegroundTask.updateService(
notificationTitle: 'Hello MyTaskHandler :)',
notificationText: 'count: $_count',
);
// Send data to main isolate.
FlutterForegroundTask.sendDataToMain(_count);
}
// Called when the task is started.
@override
Future<void> onStart(DateTime timestamp, TaskStarter starter) async {
print('onStart(starter: ${starter.name})');
_incrementCount();
}
// Called based on the eventAction set in ForegroundTaskOptions.
@override
void onRepeatEvent(DateTime timestamp) {
_incrementCount();
}
// Called when the task is destroyed.
@override
Future<void> onDestroy(DateTime timestamp) async {
print('onDestroy');
}
// Called when data is sent using `FlutterForegroundTask.sendDataToTask`.
@override
void onReceiveData(Object data) {
print('onReceiveData: $data');
if (data == incrementCountCommand) {
_incrementCount();
}
}
// Called when the notification button is pressed.
@override
void onNotificationButtonPressed(String id) {
print('onNotificationButtonPressed: $id');
}
// Called when the notification itself is pressed.
@override
void onNotificationPressed() {
print('onNotificationPressed');
}
// Called when the notification itself is dismissed.
@override
void onNotificationDismissed() {
print('onNotificationDismissed');
}
}
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
routes: {
'/': (context) => const ExamplePage(),
'/second': (context) => const SecondPage(),
},
initialRoute: '/',
);
}
}
class ExamplePage extends StatefulWidget {
const ExamplePage({super.key});
@override
State<StatefulWidget> createState() => _ExamplePageState();
}
class _ExamplePageState extends State<ExamplePage> {
final ValueNotifier<Object?> _taskDataListenable = ValueNotifier(null);
Future<void> _requestPermissions() async {
final NotificationPermission notificationPermission =
await FlutterForegroundTask.checkNotificationPermission();
if (notificationPermission != NotificationPermission.granted) {
await FlutterForegroundTask.requestNotificationPermission();
}
if (Platform.isAndroid) {
if (!await FlutterForegroundTask.isIgnoringBatteryOptimizations) {
await FlutterForegroundTask.requestIgnoreBatteryOptimization();
}
if (!await FlutterForegroundTask.canScheduleExactAlarms) {
await FlutterForegroundTask.openAlarmsAndRemindersSettings();
}
}
}
void _initService() {
FlutterForegroundTask.init(
androidNotificationOptions: AndroidNotificationOptions(
channelId: 'foreground_service',
channelName: 'Foreground Service Notification',
channelDescription:
'This notification appears when the foreground service is running.',
onlyAlertOnce: true,
),
iosNotificationOptions: const IOSNotificationOptions(
showNotification: false,
playSound: false,
),
foregroundTaskOptions: ForegroundTaskOptions(
eventAction: ForegroundTaskEventAction.repeat(5000),
autoRunOnBoot: true,
autoRunOnMyPackageReplaced: true,
allowWakeLock: true,
allowWifiLock: true,
),
);
}
Future<ServiceRequestResult> _startService() async {
if (await FlutterForegroundTask.isRunningService) {
return FlutterForegroundTask.restartService();
} else {
return FlutterForegroundTask.startService(
serviceId: 256,
notificationTitle: 'Foreground Service is running',
notificationText: 'Tap to return to the app',
notificationIcon: null,
notificationButtons: [
const NotificationButton(id: 'btn_hello', text: 'hello'),
],
notificationInitialRoute: '/second',
callback: startCallback,
);
}
}
Future<ServiceRequestResult> _stopService() {
return FlutterForegroundTask.stopService();
}
void _onReceiveTaskData(Object data) {
print('onReceiveTaskData: $data');
_taskDataListenable.value = data;
}
void _incrementCount() {
FlutterForegroundTask.sendDataToTask(MyTaskHandler.incrementCountCommand);
}
@override
void initState() {
super.initState();
FlutterForegroundTask.addTaskDataCallback(_onReceiveTaskData);
WidgetsBinding.instance.addPostFrameCallback((_) {
_requestPermissions();
_initService();
});
}
@override
void dispose() {
FlutterForegroundTask.removeTaskDataCallback(_onReceiveTaskData);
_taskDataListenable.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return WithForegroundTask(
child: Scaffold(
appBar: AppBar(
title: const Text('Flutter Foreground Task'),
centerTitle: true,
),
body: SafeArea(
child: Column(
children: [
Expanded(child: _buildCommunicationDataText()),
_buildServiceControlButtons(),
],
),
),
),
);
}
Widget _buildCommunicationDataText() {
return ValueListenableBuilder(
valueListenable: _taskDataListenable,
builder: (context, data, _) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('You received data from TaskHandler:'),
Text('$data', style: Theme.of(context).textTheme.headlineMedium),
],
),
);
},
);
}
Widget _buildServiceControlButtons() {
buttonBuilder(String text, {VoidCallback? onPressed}) {
return ElevatedButton(
onPressed: onPressed,
child: Text(text),
);
}
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
buttonBuilder('start service', onPressed: _startService),
buttonBuilder('stop service', onPressed: _stopService),
buttonBuilder('increment count', onPressed: _incrementCount),
],
),
);
}
}
class SecondPage extends StatelessWidget {
const SecondPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Second Page'),
centerTitle: true,
),
body: Center(
child: ElevatedButton(
onPressed: Navigator.of(context).pop,
child: const Text('pop this page'),
),
),
);
}
}
这个完整的示例展示了如何使用flutter_foreground_task
插件来创建和管理前台服务。希望这能帮助您更好地理解和使用该插件。如果有任何问题或需要进一步的帮助,请随时提问!
更多关于Flutter前台任务管理插件flutter_foreground_task的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter前台任务管理插件flutter_foreground_task的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何使用 flutter_foreground_task
插件的简单示例代码,该插件用于在 Flutter 应用中实现前台任务管理,比如显示一个持续运行的任务通知。
首先,你需要在 pubspec.yaml
文件中添加依赖项:
dependencies:
flutter:
sdk: flutter
flutter_foreground_task: ^x.y.z # 请将 x.y.z 替换为最新版本号
然后,运行 flutter pub get
以获取依赖项。
接下来,在你的 Dart 代码中,你可以按照以下步骤使用 flutter_foreground_task
插件:
- 导入插件并配置权限:
确保在 AndroidManifest.xml
和 Info.plist
中添加必要的权限配置,比如前台服务权限和通知权限。
import 'package:flutter/material.dart';
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
FlutterForegroundTask _foregroundTask = FlutterForegroundTask();
@override
void initState() {
super.initState();
_configureForegroundTask();
}
void _configureForegroundTask() async {
// 配置前台任务
Map<String, dynamic> options = {
"id": 1,
"title": "Foreground Task",
"content": "This is a foreground task running in the background.",
"importance": NotificationImportance.HIGH,
"priority": NotificationPriority.HIGH,
"channelId": "foreground_channel", // 确保在 Android 中已经创建了该通知渠道
"autoCancel": false,
"ongoing": true,
// 其他可选配置...
};
// 注册前台任务
bool isRegistered = await _foregroundTask.registerTask("foreground_task_identifier", options);
if (isRegistered) {
print("Foreground task registered successfully.");
// 开始前台任务
bool isStarted = await _foregroundTask.startTask("foreground_task_identifier");
if (isStarted) {
print("Foreground task started successfully.");
} else {
print("Failed to start foreground task.");
}
} else {
print("Failed to register foreground task.");
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Flutter Foreground Task Example'),
),
body: Center(
child: Text('Foreground task is configured and running in the background.'),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
// 停止前台任务
bool isStopped = await _foregroundTask.stopTask("foreground_task_identifier");
if (isStopped) {
print("Foreground task stopped successfully.");
} else {
print("Failed to stop foreground task.");
}
},
tooltip: 'Stop Foreground Task',
child: Icon(Icons.stop),
),
),
);
}
}
- 在 Android 中配置通知渠道:
在 MainActivity.kt
或相应的 Android 入口文件中,添加通知渠道的配置(如果插件没有自动处理)。
import android.app.Application
import android.app.NotificationChannel
import android.app.NotificationManager
import android.os.Build
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelId = "foreground_channel"
val channelName = "Foreground Channel"
val importance = NotificationManager.IMPORTANCE_HIGH
val channel = NotificationChannel(channelId, channelName, importance).apply {
description = "Channel for foreground tasks"
}
val notificationManager: NotificationManager =
getSystemService(NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
}
并在 AndroidManifest.xml
中指定你的 Application
类:
<application
android:name=".MyApplication"
...>
...
</application>
- 在 iOS 中配置通知权限:
确保在 Info.plist
中添加必要的通知权限配置。
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>processing</string>
<string>remote-notification</string>
</array>
<key>USApplicationSupportsRemoteNotifications</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
请注意,以上代码是一个基本的示例,实际项目中可能需要更多的配置和处理逻辑,比如错误处理、状态管理、权限请求等。此外,flutter_foreground_task
插件的具体用法和 API 可能会随着版本更新而变化,请参考插件的官方文档和示例代码以获取最新和最准确的信息。