import 'dart:convert'; import 'dart:developer'; import 'package:awesome_notifications/awesome_notifications.dart'; import 'package:flutter/cupertino.dart'; import '../page/child/ChatPage.dart'; import '../page/child/UpdateCheckPage.dart'; import 'NotifyImageCache.dart'; import 'RouteAnimation.dart'; import 'StringTool.dart'; enum NotifyChannel { update, ad, message } class Notify { static BuildContext? _context; /// 初始化(必须在 main() 中调用一次) static Future init() async { await AwesomeNotifications().initialize(null, _channels, debug: true); bool isAllowed = await AwesomeNotifications().isNotificationAllowed(); if (!isAllowed) { await AwesomeNotifications().requestPermissionToSendNotifications(); } // 直接传静态方法 AwesomeNotifications().setListeners( onActionReceivedMethod: _onActionReceivedMethod, ); } /// 必须是静态或顶级方法 static Future _onActionReceivedMethod(ReceivedAction action) async { onClick(action.payload); } static void setBuildContext(BuildContext context) { _context = context; } static List get _channels => [ NotificationChannel( channelKey: NotifyChannel.update.name, channelName: '更新通知', channelDescription: '新版本通知', importance: NotificationImportance.Default, ), NotificationChannel( channelKey: NotifyChannel.ad.name, channelName: '推广', channelDescription: '新品推广', importance: NotificationImportance.High, ), NotificationChannel( channelKey: NotifyChannel.message.name, channelName: '新消息', channelDescription: '新消息通知', importance: NotificationImportance.Max, ), ]; /// 发送通知 static Future showNotification({ required NotifyChannel channel, required String title, required String body, String? smallIcon, String? bigPicture, Map? payload, }) async { String? finalIcon = smallIcon; String? finalBigPic = bigPicture; // ✅ 如果是网络图,优先使用缓存 if (smallIcon != null && smallIcon.startsWith('http')) { finalIcon = await NotifyImageCache.getCachedImage(smallIcon) ?? smallIcon; } if (bigPicture != null && bigPicture.startsWith('http')) { finalBigPic = await NotifyImageCache.getCachedImage(bigPicture) ?? bigPicture; } // 2. 转成本地文件路径格式 if (finalIcon != null && finalIcon.startsWith('/')) { finalIcon = 'file://$finalIcon'; } if (finalBigPic != null && finalBigPic.startsWith('/')) { finalBigPic = 'file://$finalBigPic'; } String text = StringTool.stripMarkdown(body); text = text.length > 25 ? text.substring(0, 24) + "..." : text; await AwesomeNotifications().createNotification( content: NotificationContent( id: DateTime.now().millisecondsSinceEpoch.remainder(100000), channelKey: channel.name, title: title, body: text, largeIcon: finalIcon, bigPicture: finalBigPic, notificationLayout: finalBigPic != null ? NotificationLayout.BigPicture : NotificationLayout.Default, payload: payload, ), ); } /// 清除全部消息 static Future clearAll() async { await AwesomeNotifications().cancelAll(); } static void onClick(Map? payload) { log(payload.toString()); WidgetsBinding.instance.addPostFrameCallback((_) { switch (payload?["type"]) { case "NEW_MESSAGE": if (_context != null) { Navigator.push( _context!, RouteAnimation( ChatPage( roomId: payload!["roomId"]!, robotAvatar: payload["avatar"]!, robotName: payload["name"]!, ), Offset(1, 0), ), ); break; } case "NEW_VERSION": { if (_context != null) { Navigator.push( _context!, RouteAnimation(UpdateCheckPage(), Offset(1, 0)), ); } break; } } }); } }