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

在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)
        }
    }
}

关键注意事项

  1. Android周期限制:WorkManager周期性任务最小间隔为15分钟,无法设置更短时间。
  2. 应用强制停止:若用户强制停止应用,WorkManager任务会被取消,需重新打开应用才能重新注册。
  3. iOS兼容性:WorkManager仅支持Android,iOS需使用Background Fetch实现类似逻辑,核心思路一致但实现不同。
  4. 权限与优化:确保应用申请后台联网权限,后台任务中需做好异常捕获,避免任务重复失败被系统限制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 11:13:21