feat: 集成推送通知服务
主要功能: 1. 添加推送依赖 - jpush_flutter: ^2.5.0 (极光推送) - flutter_local_notifications: ^16.0.0 (本地通知) - permission_handler: ^11.0.0 (权限管理) 2. 创建PushService服务类 - 初始化极光推送SDK - 处理推送消息接收 - 处理推送点击事件 - 显示本地通知 - 设置/删除别名(用户ID绑定) 3. 在main.dart中初始化推送服务 - 应用启动时自动初始化 - 登录后可设置用户别名 - 退出登录时删除别名 4. 推送场景支持 - 充值审批通知 - 提现审批通知 - 资产变动通知 - 自定义消息推送 5. 文档 - PUSH_NOTIFICATION_GUIDE.md: 完整的集成指南 - 包含Android/iOS配置说明 - 后端接口示例 - 测试方法 技术栈: - 极光推送 (JPush) - 国内推送到达率高 - 本地通知 - 支持前台和后台推送 - 别名机制 - 按用户ID精准推送 待完成: - [ ] 配置极光推送APPKEY - [ ] Android权限配置 - [ ] iOS证书配置 - [ ] 后端推送接口开发
This commit is contained in:
328
flutter_monisuo/PUSH_NOTIFICATION_GUIDE.md
Normal file
328
flutter_monisuo/PUSH_NOTIFICATION_GUIDE.md
Normal file
@@ -0,0 +1,328 @@
|
||||
# Flutter前端推送通知集成方案
|
||||
|
||||
## 技术选型
|
||||
|
||||
### 推送服务选择
|
||||
考虑到国内环境,推荐使用 **极光推送 (JPush)** 或 **个推 (Getui)**
|
||||
|
||||
**优势**:
|
||||
- 国内推送到达率高
|
||||
- 支持iOS和Android
|
||||
- 提供完善的Flutter SDK
|
||||
- 免费版本足够使用
|
||||
|
||||
## 集成步骤
|
||||
|
||||
### 1. 添加依赖
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
# 极光推送
|
||||
jpush_flutter: ^2.5.0
|
||||
|
||||
# 本地通知
|
||||
flutter_local_notifications: ^16.0.0
|
||||
|
||||
# 权限管理
|
||||
permission_handler: ^11.0.0
|
||||
```
|
||||
|
||||
### 2. Android配置
|
||||
|
||||
**android/app/build.gradle**:
|
||||
```gradle
|
||||
android {
|
||||
defaultConfig {
|
||||
manifestPlaceholders = [
|
||||
JPUSH_PKGNAME: applicationId,
|
||||
JPUSH_APPKEY: "你的APPKEY",
|
||||
JPUSH_CHANNEL: "developer-default"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**android/app/src/main/AndroidManifest.xml**:
|
||||
```xml
|
||||
<uses-permission android:name="android.permission.RECEIVE_USER_PRESENT"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
```
|
||||
|
||||
### 3. iOS配置
|
||||
|
||||
**ios/Runner/Info.plist**:
|
||||
```xml
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
```
|
||||
|
||||
### 4. 初始化推送
|
||||
|
||||
**lib/services/push_service.dart**:
|
||||
```dart
|
||||
import 'package:jpush_flutter/jpush_flutter.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
|
||||
class PushService {
|
||||
static final PushService _instance = PushService._internal();
|
||||
factory PushService() => _instance;
|
||||
PushService._internal();
|
||||
|
||||
final JPush jpush = JPush();
|
||||
final FlutterLocalNotificationsPlugin localNotifications =
|
||||
FlutterLocalNotificationsPlugin();
|
||||
|
||||
Future<void> init() async {
|
||||
// 初始化极光推送
|
||||
jpush.addEventHandler(
|
||||
onReceiveNotification: (Map<String, dynamic> message) async {
|
||||
print('收到推送: $message');
|
||||
_handleNotification(message);
|
||||
},
|
||||
onOpenNotification: (Map<String, dynamic> message) async {
|
||||
print('点击推送: $message');
|
||||
_handleNotificationTap(message);
|
||||
},
|
||||
);
|
||||
|
||||
jpush.setup(
|
||||
appKey: '你的APPKEY',
|
||||
channel: 'developer-default',
|
||||
production: false,
|
||||
debug: true,
|
||||
);
|
||||
|
||||
// 初始化本地通知
|
||||
await _initLocalNotifications();
|
||||
}
|
||||
|
||||
Future<void> _initLocalNotifications() async {
|
||||
const AndroidInitializationSettings initializationSettingsAndroid =
|
||||
AndroidInitializationSettings('@mipmap/ic_launcher');
|
||||
|
||||
final DarwinInitializationSettings initializationSettingsDarwin =
|
||||
DarwinInitializationSettings();
|
||||
|
||||
final InitializationSettings initializationSettings =
|
||||
InitializationSettings(
|
||||
android: initializationSettingsAndroid,
|
||||
iOS: initializationSettingsDarwin,
|
||||
);
|
||||
|
||||
await localNotifications.initialize(
|
||||
initializationSettings,
|
||||
onDidReceiveNotificationResponse: (NotificationResponse response) {
|
||||
_handleLocalNotificationTap(response.payload);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _handleNotification(Map<String, dynamic> message) {
|
||||
// 处理推送消息
|
||||
final title = message['title'] ?? '新消息';
|
||||
final body = message['alert'] ?? '';
|
||||
|
||||
_showLocalNotification(title, body, message);
|
||||
}
|
||||
|
||||
void _handleNotificationTap(Map<String, dynamic> message) {
|
||||
// 处理推送点击
|
||||
final type = message['extras']['type'];
|
||||
|
||||
switch (type) {
|
||||
case 'order':
|
||||
// 跳转到订单详情
|
||||
break;
|
||||
case 'asset':
|
||||
// 跳转到资产页面
|
||||
break;
|
||||
default:
|
||||
// 跳转到首页
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _showLocalNotification(
|
||||
String title,
|
||||
String body,
|
||||
Map<String, dynamic> data,
|
||||
) async {
|
||||
const AndroidNotificationDetails androidPlatformChannelSpecifics =
|
||||
AndroidNotificationDetails(
|
||||
'monisuo_channel',
|
||||
'模拟所通知',
|
||||
channelDescription: '模拟所应用通知',
|
||||
importance: Importance.max,
|
||||
priority: Priority.high,
|
||||
);
|
||||
|
||||
const NotificationDetails platformChannelSpecifics =
|
||||
NotificationDetails(android: androidPlatformChannelSpecifics);
|
||||
|
||||
await localNotifications.show(
|
||||
DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
||||
title,
|
||||
body,
|
||||
platformChannelSpecifics,
|
||||
payload: jsonEncode(data),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleLocalNotificationTap(String? payload) {
|
||||
if (payload != null) {
|
||||
final data = jsonDecode(payload);
|
||||
_handleNotificationTap(data);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置别名(通常是用户ID)
|
||||
Future<void> setAlias(String userId) async {
|
||||
jpush.setAlias(userId);
|
||||
}
|
||||
|
||||
// 删除别名
|
||||
Future<void> deleteAlias() async {
|
||||
jpush.deleteAlias();
|
||||
}
|
||||
|
||||
// 设置标签
|
||||
Future<void> setTags(List<String> tags) async {
|
||||
jpush.setTags(tags);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 在main.dart中初始化
|
||||
|
||||
```dart
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// 初始化推送服务
|
||||
await PushService().init();
|
||||
|
||||
runApp(const MyApp());
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 登录后设置别名
|
||||
|
||||
```dart
|
||||
// 用户登录成功后
|
||||
await PushService().setAlias(userId);
|
||||
|
||||
// 用户退出登录后
|
||||
await PushService().deleteAlias();
|
||||
```
|
||||
|
||||
## 后端推送接口
|
||||
|
||||
**后端需要提供推送接口**:
|
||||
|
||||
```java
|
||||
@PostMapping("/api/push/send")
|
||||
public Result<Void> sendPush(
|
||||
@RequestParam String userId,
|
||||
@RequestParam String title,
|
||||
@RequestParam String body,
|
||||
@RequestParam(required = false) Map<String, Object> extras
|
||||
) {
|
||||
// 调用极光推送API
|
||||
JPushClient jpushClient = new JPushClient(appKey, masterSecret);
|
||||
|
||||
PushPayload payload = PushPayload.newBuilder()
|
||||
.setPlatform(Platform.all())
|
||||
.setAudience(Audience.alias(userId))
|
||||
.setNotification(Notification.alert(body))
|
||||
.setMessage(Message.content(body))
|
||||
.setOptions(Options.newBuilder().setApnsProduction(false).build())
|
||||
.build();
|
||||
|
||||
jpushClient.sendPush(payload);
|
||||
|
||||
return Result.success("推送成功");
|
||||
}
|
||||
```
|
||||
|
||||
## 推送场景
|
||||
|
||||
### 1. 充值审批通知
|
||||
```dart
|
||||
{
|
||||
"title": "充值审批通过",
|
||||
"body": "您的充值订单已审批通过,金额: 100 USDT",
|
||||
"extras": {
|
||||
"type": "order",
|
||||
"orderNo": "F20260324001"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 提现审批通知
|
||||
```dart
|
||||
{
|
||||
"title": "提现审批完成",
|
||||
"body": "您的提现申请已处理,金额: 100 USDT",
|
||||
"extras": {
|
||||
"type": "order",
|
||||
"orderNo": "F20260324002"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 资产变动通知
|
||||
```dart
|
||||
{
|
||||
"title": "资产变动",
|
||||
"body": "您的账户余额发生变动",
|
||||
"extras": {
|
||||
"type": "asset",
|
||||
"userId": "123"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 测试
|
||||
|
||||
### 1. 测试推送
|
||||
```dart
|
||||
// 在开发环境中测试
|
||||
await PushService()._showLocalNotification(
|
||||
'测试标题',
|
||||
'测试内容',
|
||||
{'type': 'test'},
|
||||
);
|
||||
```
|
||||
|
||||
### 2. 测试极光推送
|
||||
- 在极光推送控制台发送测试推送
|
||||
- 检查应用是否收到推送
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **权限申请**: Android需要申请通知权限
|
||||
2. **iOS证书**: 需要配置APNs证书
|
||||
3. **测试环境**: 开发时使用开发环境
|
||||
4. **生产环境**: 上线前切换到生产环境
|
||||
5. **用户隐私**: 首次启动时请求推送权限
|
||||
|
||||
## 成本估算
|
||||
|
||||
- 极光推送免费版:每月100万条推送
|
||||
- 对于中小规模应用完全够用
|
||||
- 如需更多,可以升级付费版本
|
||||
|
||||
---
|
||||
|
||||
**集成时间**: 预计2-3小时
|
||||
**难度**: 中等
|
||||
**维护成本**: 低
|
||||
@@ -13,6 +13,7 @@ import 'data/services/market_service.dart';
|
||||
import 'data/services/trade_service.dart';
|
||||
import 'data/services/asset_service.dart';
|
||||
import 'data/services/fund_service.dart';
|
||||
import 'services/push_service.dart';
|
||||
import 'providers/auth_provider.dart';
|
||||
import 'providers/market_provider.dart';
|
||||
import 'providers/asset_provider.dart';
|
||||
@@ -26,6 +27,10 @@ void main() async {
|
||||
|
||||
await SharedPreferences.getInstance();
|
||||
await LocalStorage.init();
|
||||
|
||||
// 初始化推送服务
|
||||
await PushService().init();
|
||||
debugPrint('✅ 推送服务已初始化');
|
||||
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
321
flutter_monisuo/lib/services/push_service.dart
Normal file
321
flutter_monisuo/lib/services/push_service.dart
Normal file
@@ -0,0 +1,321 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:jpush_flutter/jpush_flutter.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
|
||||
/// 推送通知服务
|
||||
class PushService {
|
||||
static final PushService _instance = PushService._internal();
|
||||
factory PushService() => _instance;
|
||||
PushService._internal();
|
||||
|
||||
final JPush jpush = JPush();
|
||||
final FlutterLocalNotificationsPlugin localNotifications =
|
||||
FlutterLocalNotificationsPlugin();
|
||||
|
||||
bool _initialized = false;
|
||||
|
||||
/// 初始化推送服务
|
||||
Future<void> init({String? appKey}) async {
|
||||
if (_initialized) return;
|
||||
|
||||
try {
|
||||
// 初始化极光推送
|
||||
jpush.addEventHandler(
|
||||
onReceiveNotification: (Map<String, dynamic> message) async {
|
||||
debugPrint('📱 收到推送: $message');
|
||||
_handleNotification(message);
|
||||
},
|
||||
onOpenNotification: (Map<String, dynamic> message) async {
|
||||
debugPrint('📱 点击推送: $message');
|
||||
_handleNotificationTap(message);
|
||||
},
|
||||
onReceiveMessage: (Map<String, dynamic> message) async {
|
||||
debugPrint('📱 收到自定义消息: $message');
|
||||
},
|
||||
);
|
||||
|
||||
// 如果有appKey则设置,否则跳过(用于开发环境)
|
||||
if (appKey != null && appKey.isNotEmpty) {
|
||||
jpush.setup(
|
||||
appKey: appKey,
|
||||
channel: 'developer-default',
|
||||
production: false,
|
||||
debug: true,
|
||||
);
|
||||
}
|
||||
|
||||
// 初始化本地通知
|
||||
await _initLocalNotifications();
|
||||
|
||||
_initialized = true;
|
||||
debugPrint('✅ 推送服务初始化成功');
|
||||
} catch (e) {
|
||||
debugPrint('❌ 推送服务初始化失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 初始化本地通知
|
||||
Future<void> _initLocalNotifications() async {
|
||||
const AndroidInitializationSettings initializationSettingsAndroid =
|
||||
AndroidInitializationSettings('@mipmap/ic_launcher');
|
||||
|
||||
final DarwinInitializationSettings initializationSettingsDarwin =
|
||||
DarwinInitializationSettings(
|
||||
requestAlertPermission: true,
|
||||
requestBadgePermission: true,
|
||||
requestSoundPermission: true,
|
||||
);
|
||||
|
||||
final InitializationSettings initializationSettings =
|
||||
InitializationSettings(
|
||||
android: initializationSettingsAndroid,
|
||||
iOS: initializationSettingsDarwin,
|
||||
);
|
||||
|
||||
await localNotifications.initialize(
|
||||
initializationSettings,
|
||||
onDidReceiveNotificationResponse: (NotificationResponse response) {
|
||||
_handleLocalNotificationTap(response.payload);
|
||||
},
|
||||
);
|
||||
|
||||
// 请求通知权限(Android 13+)
|
||||
await localNotifications
|
||||
.resolvePlatformSpecificImplementation<
|
||||
AndroidFlutterLocalNotificationsPlugin>()
|
||||
?.requestNotificationsPermission();
|
||||
}
|
||||
|
||||
/// 处理推送消息
|
||||
void _handleNotification(Map<String, dynamic> message) {
|
||||
try {
|
||||
final extras = message['extras'] as Map<String, dynamic>?;
|
||||
final title = extras?['title']?.toString() ??
|
||||
message['title']?.toString() ??
|
||||
'新消息';
|
||||
final body = extras?['body']?.toString() ??
|
||||
message['alert']?.toString() ??
|
||||
'';
|
||||
|
||||
_showLocalNotification(title, body, message);
|
||||
} catch (e) {
|
||||
debugPrint('❌ 处理推送消息失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 处理推送点击
|
||||
void _handleNotificationTap(Map<String, dynamic> message) {
|
||||
try {
|
||||
final extras = message['extras'] as Map<String, dynamic>?;
|
||||
final type = extras?['type']?.toString();
|
||||
|
||||
debugPrint('📱 推送类型: $type');
|
||||
|
||||
// TODO: 根据type跳转到对应页面
|
||||
switch (type) {
|
||||
case 'order':
|
||||
// 跳转到订单详情
|
||||
final orderNo = extras?['orderNo']?.toString();
|
||||
debugPrint('📱 跳转到订单: $orderNo');
|
||||
break;
|
||||
case 'asset':
|
||||
// 跳转到资产页面
|
||||
debugPrint('📱 跳转到资产页面');
|
||||
break;
|
||||
default:
|
||||
// 默认跳转到首页
|
||||
debugPrint('📱 默认跳转');
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('❌ 处理推送点击失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 显示本地通知
|
||||
Future<void> _showLocalNotification(
|
||||
String title,
|
||||
String body,
|
||||
Map<String, dynamic> data,
|
||||
) async {
|
||||
try {
|
||||
const AndroidNotificationDetails androidPlatformChannelSpecifics =
|
||||
AndroidNotificationDetails(
|
||||
'monisuo_channel',
|
||||
'模拟所通知',
|
||||
channelDescription: '模拟所应用通知',
|
||||
importance: Importance.max,
|
||||
priority: Priority.high,
|
||||
showWhen: true,
|
||||
enableVibration: true,
|
||||
enableLights: true,
|
||||
);
|
||||
|
||||
const DarwinNotificationDetails iOSPlatformChannelSpecifics =
|
||||
DarwinNotificationDetails(
|
||||
presentAlert: true,
|
||||
presentBadge: true,
|
||||
presentSound: true,
|
||||
);
|
||||
|
||||
const NotificationDetails platformChannelSpecifics =
|
||||
NotificationDetails(
|
||||
android: androidPlatformChannelSpecifics,
|
||||
iOS: iOSPlatformChannelSpecifics,
|
||||
);
|
||||
|
||||
await localNotifications.show(
|
||||
DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
||||
title,
|
||||
body,
|
||||
platformChannelSpecifics,
|
||||
payload: jsonEncode(data),
|
||||
);
|
||||
|
||||
debugPrint('✅ 本地通知显示成功: $title - $body');
|
||||
} catch (e) {
|
||||
debugPrint('❌ 显示本地通知失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 处理本地通知点击
|
||||
void _handleLocalNotificationTap(String? payload) {
|
||||
if (payload != null) {
|
||||
try {
|
||||
final data = jsonDecode(payload) as Map<String, dynamic>;
|
||||
_handleNotificationTap(data);
|
||||
} catch (e) {
|
||||
debugPrint('❌ 处理本地通知点击失败: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 设置别名(通常是用户ID)
|
||||
Future<void> setAlias(String userId) async {
|
||||
try {
|
||||
if (!_initialized) {
|
||||
debugPrint('⚠️ 推送服务未初始化,无法设置别名');
|
||||
return;
|
||||
}
|
||||
|
||||
jpush.setAlias(userId);
|
||||
debugPrint('✅ 设置推送别名: $userId');
|
||||
} catch (e) {
|
||||
debugPrint('❌ 设置别名失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 删除别名
|
||||
Future<void> deleteAlias() async {
|
||||
try {
|
||||
if (!_initialized) {
|
||||
debugPrint('⚠️ 推送服务未初始化,无法删除别名');
|
||||
return;
|
||||
}
|
||||
|
||||
jpush.deleteAlias();
|
||||
debugPrint('✅ 删除推送别名');
|
||||
} catch (e) {
|
||||
debugPrint('❌ 删除别名失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 设置标签
|
||||
Future<void> setTags(List<String> tags) async {
|
||||
try {
|
||||
if (!_initialized) {
|
||||
debugPrint('⚠️ 推送服务未初始化,无法设置标签');
|
||||
return;
|
||||
}
|
||||
|
||||
jpush.setTags(tags);
|
||||
debugPrint('✅ 设置推送标签: $tags');
|
||||
} catch (e) {
|
||||
debugPrint('❌ 设置标签失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取注册ID
|
||||
Future<String?> getRegistrationId() async {
|
||||
try {
|
||||
if (!_initialized) {
|
||||
debugPrint('⚠️ 推送服务未初始化');
|
||||
return null;
|
||||
}
|
||||
|
||||
final rid = await jpush.getRegistrationId();
|
||||
debugPrint('📱 注册ID: $rid');
|
||||
return rid;
|
||||
} catch (e) {
|
||||
debugPrint('❌ 获取注册ID失败: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// 测试本地通知(用于开发环境)
|
||||
Future<void> testNotification() async {
|
||||
await _showLocalNotification(
|
||||
'测试通知',
|
||||
'这是一条测试推送消息',
|
||||
{
|
||||
'type': 'test',
|
||||
'title': '测试通知',
|
||||
'body': '这是一条测试推送消息',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// 发送充值审批通知
|
||||
Future<void> sendDepositApprovalNotification({
|
||||
required String userId,
|
||||
required String orderNo,
|
||||
required String amount,
|
||||
required bool approved,
|
||||
}) async {
|
||||
final title = approved ? '充值审批通过' : '充值审批驳回';
|
||||
final body = approved
|
||||
? '您的充值订单已审批通过,金额: $amount USDT'
|
||||
: '您的充值订单已被驳回,金额: $amount USDT';
|
||||
|
||||
await _showLocalNotification(title, body, {
|
||||
'type': 'order',
|
||||
'orderNo': orderNo,
|
||||
'title': title,
|
||||
'body': body,
|
||||
});
|
||||
}
|
||||
|
||||
/// 发送提现审批通知
|
||||
Future<void> sendWithdrawApprovalNotification({
|
||||
required String userId,
|
||||
required String orderNo,
|
||||
required String amount,
|
||||
required bool approved,
|
||||
}) async {
|
||||
final title = approved ? '提现审批通过' : '提现审批驳回';
|
||||
final body = approved
|
||||
? '您的提现申请已处理,金额: $amount USDT'
|
||||
: '您的提现申请已被驳回,金额: $amount USDT';
|
||||
|
||||
await _showLocalNotification(title, body, {
|
||||
'type': 'order',
|
||||
'orderNo': orderNo,
|
||||
'title': title,
|
||||
'body': body,
|
||||
});
|
||||
}
|
||||
|
||||
/// 发送资产变动通知
|
||||
Future<void> sendAssetChangeNotification({
|
||||
required String userId,
|
||||
required String message,
|
||||
}) async {
|
||||
await _showLocalNotification('资产变动', message, {
|
||||
'type': 'asset',
|
||||
'userId': userId,
|
||||
'title': '资产变动',
|
||||
'body': message,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,11 @@ dependencies:
|
||||
# 字体
|
||||
google_fonts: ^6.2.1
|
||||
|
||||
# 推送通知
|
||||
jpush_flutter: ^2.5.0
|
||||
flutter_local_notifications: ^16.0.0
|
||||
permission_handler: ^11.0.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
Reference in New Issue
Block a user