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

使用flutter_local_notifications时Flutter定时通知无法触发求助

Flutter定时通知无法触发问题排查(基于flutter_local_notifications)

我正尝试在Flutter应用中使用flutter_local_notifications包实现定时通知功能,即时通知可正常工作,但定时通知完全无法触发。请问我使用zonedSchedule调度通知的方式是否存在问题?

以下是我的代码:

import 'package:timezone/data/latest.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:permission_handler/permission_handler.dart';

class NotificationScreen extends Stateful Widget {
  @override
  _NotificationScreenState createState() => _NotificationScreenState();
}

class _NotificationScreenState extends State<NotificationScreen> {
  final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
      FlutterLocalNotificationsPlugin();
  TimeOfDay? selectedTime;

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

  Future<void> _initializeNotifications() async {
    tz.initializeTimeZones(); // Timezone initialization
    const AndroidInitializationSettings initializationSettingsAndroid =
        AndroidInitializationSettings('@mipmap/ic_launcher');

    const InitializationSettings initializationSettings =
        InitializationSettings(android: initializationSettingsAndroid);

    await flutterLocalNotificationsPlugin.initialize(initializationSettings);

    // Request notification permission
    var status = await Permission.notification.status;
    if (status.isDenied) {
      status = await Permission.notification.request();
      if (status.isPermanentlyDenied) {
        openAppSettings();
      }
    }
  }

  Future<void> _scheduleNotification(TimeOfDay time) async {
    final now = DateTime.now();
    DateTime scheduledDate =
        DateTime(now.year, now.month, now.day, time.hour, time.minute);
    if (scheduledDate.isBefore(now)) {
      scheduledDate = scheduledDate.add(const Duration(days: 1));
    }

    tz.TZDateTime scheduledTime = tz.TZDateTime.from(scheduledDate, tz.local);
    print('Scheduled Time: $scheduledTime'); // Debugging output

    const AndroidNotificationDetails androidNotificationDetails =
        AndroidNotificationDetails(
            'scheduled_notification_channel', 'Scheduled Notifications',
            channelDescription: 'Channel for scheduled notifications',
            importance: Importance.max,
            priority: Priority.high,
            ticker: 'ticker');

    const NotificationDetails notificationDetails =
        NotificationDetails(android: androidNotificationDetails);

    await flutterLocalNotificationsPlugin.zonedSchedule(
      DateTime.now().millisecondsSinceEpoch ~/ 1000, // Unique ID
      'Scheduled Notification',
      'This is your reminder!',
      scheduledTime,
      notificationDetails,
      uiLocalNotificationDateInterpretation:
          UILocalNotificationDateInterpretation.absoluteTime,
      androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
    );

    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Notification scheduled for ${time.format(context)}')),
    );
  }

  Future<void> _sendInstantNotification() async {
    const AndroidNotificationDetails androidNotificationDetails =
        AndroidNotificationDetails(
            'instant_notification_channel', 'Instant Notifications',
            channelDescription: 'Channel for instant notifications',
            importance: Importance.max,
            priority: Priority.high,
            ticker: 'ticker');
    const NotificationDetails notificationDetails =
        NotificationDetails(android: androidNotificationDetails);
    await flutterLocalNotificationsPlugin.show(
        1, 'Instant Notification', 'This is an instant notification!', notificationDetails);
    ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Instant notification sent!')));
  }

  Future<void> _pickTime() async {
    final TimeOfDay? picked = await showTimePicker(
      context: context,
      initialTime: TimeOfDay.now(),
    );
    if (picked != null) {
      setState(() {
        selectedTime = picked;
      });
      _scheduleNotification(picked);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Schedule Notification')),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Text(
              selectedTime != null
                  ? 'Selected Time: ${selectedTime!.format(context)}'
                  : 'No Time Selected',
              style: const TextStyle(fontSize: 18),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: _pickTime,
              child: const Text('Pick Time'),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: _sendInstantNotification,
              child: const Text('Send Instant Notification'),
            ),
          ],
        ),
      ),
    );
  }
}

问题分析与修复方案

  • 通知ID重复覆盖:原代码用秒级时间戳作为ID,短时间内多次调度会导致ID重复,直接覆盖之前的通知。建议改用递增整数ID或UUID生成唯一ID,避免冲突。
  • 时区初始化不完整:仅调用tz.initializeTimeZones()不足以确保时区正确,需添加tz.setLocalLocation(tz.getLocation('Asia/Shanghai'))(根据你的实际时区调整),保证tz.local指向正确时区,避免时间转换错误。
  • Android电池优化限制:即使设置了AndroidScheduleMode.exactAllowWhileIdle,部分国产厂商的电池优化仍会阻止后台定时任务。需添加ignoreBatteryOptimizations权限请求,并引导用户关闭应用的电池优化。
  • 通知渠道未手动创建:Android 8.0+要求必须显式创建通知渠道,初始化时需调用createNotificationChannel()确保渠道存在,否则通知无法显示。
  • 调度时间验证:打印scheduledTime确认转换后的时间是否符合预期,若时区设置错误,会导致通知在非预期时间触发或完全不触发。

内容的提问来源于stack exchange,提问作者asif ullah

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 05:14:59