在Flutter中用WorkManager实现无FCM/本地通知的后台订单通知
实现方案:基于WorkManager的后台订单检测与页面唤醒
核心思路
利用workmanager包实现周期性后台任务,定时调用接口检测新订单;当检测到新订单时,通过原生MethodChannel触发应用唤醒,并跳转至订单详情页。以下是完整代码示例与配置步骤:
1. 依赖配置
在pubspec.yaml中添加所需依赖:
dependencies: flutter: sdk: flutter workmanager: ^0.5.1 http: ^1.1.0
2. AndroidManifest配置(仅Android)
在android/app/src/main/AndroidManifest.xml中添加权限与WorkManager相关组件:
<!-- 基础权限 --> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <application ...> <!-- WorkManager 系统组件 --> <receiver android:name="androidx.work.impl.utils.ForceStopRunnable$BroadcastReceiver" android:enabled="false" android:exported="false" /> <receiver android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy" android:enabled="false" android:exported="false"> <intent-filter> <action android:name="android.intent.action.ACTION_POWER_CONNECTED" /> <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" /> </intent-filter> </receiver> <receiver android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryNotLowProxy" android:enabled="false" android:exported="false"> <intent-filter> <action android:name="android.intent.action.BATTERY_OKAY" /> <action android:name="android.intent.action.BATTERY_LOW" /> </intent-filter> </receiver> <service android:name="androidx.work.impl.background.systemalarm.SystemAlarmService" android:enabled="true" android:exported="false" /> <service android:name="androidx.work.impl.background.systemjob.SystemJobService" android:enabled="true" android:exported="false" android:permission="android.permission.BIND_JOB_SERVICE" /> </application>
3. Flutter端核心代码
import 'package:flutter/material.dart'; import 'package:workmanager/workmanager.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; import 'package:flutter/services.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); // 初始化WorkManager Workmanager().initialize( callbackDispatcher, isInDebugMode: true, // 发布环境改为false ); // 注册周期性任务(Android最小周期15分钟) Workmanager().registerPeriodicTask( "newOrderChecker", "checkNewOrders", frequency: const Duration(minutes: 15), constraints: const Constraints( networkType: NetworkType.connected, // 仅联网时执行 ), ); runApp(const MyApp()); } // 后台任务回调 void callbackDispatcher() { Workmanager().executeTask((task, inputData) async { if (task == "checkNewOrders") { await _fetchNewOrders(); } return Future.value(true); }); } // 检测新订单接口请求 Future<void> _fetchNewOrders() async { try { final response = await http.get(Uri.parse("https://your-api-domain.com/check-new-orders")); if (response.statusCode == 200) { final data = jsonDecode(response.body); if (data['hasNew'] == true && data['orderId'] != null) { // 调用原生代码唤醒应用并跳转 const MethodChannel("com.yourapp/order").invokeMethod( "openOrderDetail", {"orderId": data['orderId']}, ); } } } catch (e) { print("后台检测订单失败: $e"); } } class MyApp extends StatefulWidget { const MyApp({super.key}); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { final MethodChannel _channel = const MethodChannel("com.yourapp/order"); @override void initState() { super.initState(); // 监听原生跳转指令 _channel.setMethodCallHandler((call) async { if (call.method == "navigateToOrder") { Navigator.pushNamed( context, '/order-detail', arguments: call.arguments as String, ); } }); } @override Widget build(BuildContext context) { return MaterialApp( title: "订单管理", initialRoute: '/', routes: { '/': (context) => const HomePage(), '/order-detail': (context) => OrderDetailPage( orderId: ModalRoute.of(context)!.settings.arguments as String, ), }, ); } } class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("首页")), body: const Center(child: Text("等待新订单")), ); } } class OrderDetailPage extends StatelessWidget { final String orderId; const OrderDetailPage({super.key, required this.orderId}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("订单详情 #$orderId")), body: Center(child: Text("订单ID: $orderId")), ); } }
4. Android原生代码(MainActivity.kt)
import android.content.Intent import android.os.Bundle import androidx.annotation.NonNull import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel class MainActivity : FlutterActivity() { private val CHANNEL = "com.yourapp/order" override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> if (call.method == "openOrderDetail") { val orderId = call.argument<String>("orderId") wakeAppAndNavigate(orderId) result.success(null) } else { result.notImplemented() } } } private fun wakeAppAndNavigate(orderId: String?) { // 构建跳转Intent,唤醒应用 val intent = Intent(this, MainActivity::class.java).apply { putExtra("orderId", orderId) flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP } startActivity(intent) // 将应用移至前台 moveTaskToFront(taskId, 0) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 处理启动时的订单ID参数 intent?.getStringExtra("orderId")?.let { orderId -> MethodChannel(flutterEngine?.dartExecutor?.binaryMessenger, CHANNEL) .invokeMethod("navigateToOrder", orderId) } } }
关键注意事项
- Android周期限制:WorkManager周期性任务最小间隔为15分钟,无法设置更短时间。
- 应用强制停止:若用户强制停止应用,WorkManager任务会被取消,需重新打开应用才能重新注册。
- iOS兼容性:WorkManager仅支持Android,iOS需使用Background Fetch实现类似逻辑,核心思路一致但实现不同。
- 权限与优化:确保应用申请后台联网权限,后台任务中需做好异常捕获,避免任务重复失败被系统限制。
内容的提问来源于stack exchange,提问作者delio ribas
相关产品推荐
相关产品推荐

