问题很详细
我使用 Awesome_notifications 包。 通知有时会很好地发挥作用,但有时它们仅在应用程序打开时才起作用。 Awesome_notifications 包说这是由于设备的省电模式造成的。
参见图片:[1]:https://i.sstatic.net/4ajWVQ9L.jpg
注意:我根据用户指定的时间安排通知
2:如何使用
背景_获取
与
太棒了_通知
//代码
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:device_preview/device_preview.dart';
import 'package:awesome_notifications/awesome_notifications.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:showcaseview/showcaseview.dart';
import 'package:upgrader/upgrader.dart';
import 'package:background_fetch/background_fetch.dart';
// [Android-only] This "Headless Task" is run when the Android app is terminated with `enableHeadless: true`
// Be sure to annotate your callback function to avoid issues in release mode on Flutter >= 3.3.0
@pragma('vm:entry-point')
void backgroundFetchHeadlessTask(HeadlessTask task) async {
String taskId = task.taskId;
bool isTimeout = task.timeout;
if (isTimeout) {
// This task has exceeded its allowed running-time.
// You must stop what you're doing and immediately .finish(taskId)
print("[BackgroundFetch] Headless task timed-out: $taskId");
BackgroundFetch.finish(taskId);
return;
}
print('[BackgroundFetch] Headless event received.');
// Do your work here...
//Notification
notfi();
BackgroundFetch.finish(taskId);
}
TimeOfDay _timeOfDay = const TimeOfDay(hour:5, minute: 0);
void main()async{
WidgetsFlutterBinding.ensureInitialized();
await Permission.notification.isDenied.then(
(value) {
if (value) {
Permission.notification.request();
}
}
);
AwesomeNotifications().initialize(
null,
[
NotificationChannel(
channelKey: 'basic_keyadh',
channelName: 'chnneladhs',
channelDescription: 'fghjks',
importance: NotificationImportance.Max,
//channelShowBadge:true,
defaultRingtoneType: DefaultRingtoneType.Notification,
onlyAlertOnce: true,
),
],
channelGroups: [
NotificationChannelGroup(
channelGroupName: 'Basic group',
channelGroupKey: 'basic_channel_group'
),
],
debug: true,
);
// await NotificationService.initializeNotifications();
runApp(DevicePreview(enabled: true,builder: (context) => const MyApp23 (),
),
);
BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask);
}
class MyApp23 extends StatefulWidget {
const MyApp23({super.key});
@override
State<MyApp23> createState() => _MyApp23State();
}
class _MyApp23State extends State<MyApp23> with WidgetsBindingObserver {
// Be sure to annotate your callback function to avoid issues in release mode on Flutter >= 3.3.0
@override
void initState() {
super.initState();
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
// Configure BackgroundFetch.
int status = await BackgroundFetch.configure(BackgroundFetchConfig(
minimumFetchInterval: 15,
stopOnTerminate: false,
enableHeadless: true,
requiresBatteryNotLow: false,
requiresCharging: false,
requiresStorageNotLow: false,
requiresDeviceIdle: false,
requiredNetworkType: NetworkType.NONE
), (String taskId) async { // <-- Event handler
// This is the fetch-event callback.
print("[BackgroundFetch] Event received $taskId");
setState(() {
});
// IMPORTANT: You must signal completion of your task or the OS can punish your app
// for taking too long in the background.
BackgroundFetch.finish(taskId);
}, (String taskId) async { // <-- Task timeout handler.
// This task has exceeded its allowed running-time. You must stop what you're doing and immediately .finish(taskId)
print("[BackgroundFetch] TASK TIMEOUT taskId: $taskId");
BackgroundFetch.finish(taskId);
});
print('[BackgroundFetch] configure success: $status');
setState(() {
});
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
}
@override
void dispose(){
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
Widget build(BuildContext context) {
//Set the fit size (Find your UI design, look at the dimensions of the device screen and fill it in,unit in dp)
return ScreenUtilInit(
//designSize: const Size(360, 690),
minTextAdapt: true,
splitScreenMode: true,
// Use builder only if you need to use library outside ScreenUtilInit context
builder: (_ , child) {
return ShowCaseWidget(
builder : (context) => MaterialApp(
debugShowCheckedModeBanner: false,
//title: 'First Method',
// You can use the library anywhere in the app even in theme
theme: ThemeData(
primarySwatch: Colors.blue,
textTheme: Typography.englishLike2018.apply(fontSizeFactor: 1.sp),
),
home: UpgradeAlert(
dialogStyle: UpgradeDialogStyle.cupertino,
upgrader: Upgrader(
// minAppVersion:"1.0.0",
debugLogging:true,
debugDisplayAlways:true,
languageCode:'ar',
messages: UpgraderMessages(
code: 'ar',
),
// durationUntilAlertAgain: const Duration(days: 1),
),
child: child),
),
);
},
child: const Demo(),
);
}
}
///Stings
class Demo extends StatefulWidget{
const Demo({super.key});
@override
State<Demo> createState() => _DemoState();
}
class _DemoState extends State<Demo> with SingleTickerProviderStateMixin {
@override
void initState() {
super.initState();
getSavedData();
}
getSavedData() async {
final pref = await SharedPreferences.getInstance();
//appData.MyColor = Color(pref.getInt('MyColor')??Colors.white.value);
//appData.col1= pref.getBool('col1') ??appData.col1;
//appData.col2= pref.getBool('col2') ??appData.col2;
// get the saved value from stored prefs
// and make sure to do it inside `setState`
// cannot use DateTime.parse because _timeOfDaybgm.toString() doesn't return a value
// DateTime.parse can understand
setState(() {
int ssavedHour = pref.getInt('_timeOfDay') ?? _timeOfDay.hour;
int ssavedMinute = pref.getInt('_timeOfDay2') ??_timeOfDay.minute;
_timeOfDay = TimeOfDay(hour: ssavedHour, minute: ssavedMinute);
//////////////////////////////////////////////////////////////////////////////////
});
}
@override
void dispose(){
super.dispose();
}
@override
Widget build(BuildContext context) {
// notfi();
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
centerTitle:true,
backgroundColor:const Color.fromARGB(255, 241, 204, 100),
scrolledUnderElevation: 0.0,
title:const Text(
"Demo",
style: TextStyle(
color:Colors.white,
fontWeight: FontWeight.bold,
)
,),
) ,
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
//mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: TextButton(onPressed: () async {
final TimeOfDay?timeOfDay = await showTimePicker(
context: context,
initialEntryMode: TimePickerEntryMode.dialOnly,
initialTime:_timeOfDay,
builder: (BuildContext context, Widget? child){
return Theme(
data: Theme.of(context).copyWith(
textTheme: const TextTheme(
// ignore: deprecated_member_use
button: TextStyle(
//fontSize: BoodyTextSize(),
),
// ignore: deprecated_member_use
subtitle1: TextStyle(
),
// ignore: deprecated_member_use
bodyText2: TextStyle(
//fontSize: Titlesize(),
color:Color.fromARGB(255, 239, 158, 52),
),
),
colorScheme: const ColorScheme.light(
onBackground:Color.fromARGB(255, 253, 199, 126),
tertiaryContainer: Color.fromARGB(255, 253, 199, 126),
primary:Color.fromARGB(255, 253, 199, 126),
)),
child: child!,
) ;
}
);
if (timeOfDay!= null) {
setState(() {
_timeOfDay=timeOfDay;
});
// ignore: use_build_context_synchronously
FocusScope.of(context).unfocus();
final prefs = await SharedPreferences.getInstance();
// changed setString to setInt, just makes it easier
await prefs.setInt('_timeOfDay', _timeOfDay.hour);
await prefs.setInt('_timeOfDay2', _timeOfDay.minute);
}
}, child: const Text("Schedule",style: TextStyle(color: Colors.red),)),
)
],
) ,
);
}
}
//Notification
notfi() {
AwesomeNotifications().createNotification(
schedule:NotificationCalendar(
// allowWhileIdle:true,
// preciseAlarm: true,
hour: _timeOfDay.hour,
minute: _timeOfDay.minute,
second: 0,
repeats: true,
),
/* actionButtons: <NotificationActionButton>[
NotificationActionButton(
key: 'accept',
label: 'قراه',
color: const Color.fromARGB(183, 79, 34, 240),
),
NotificationActionButton(
key: 'reject',
label: 'اغلاق ',
color: const Color.fromARGB(183, 79, 34, 240),
),
],*/
content: NotificationContent(
// icon: 'resource://mipmap/shado',
id:2,
channelKey: 'basic_keyadh',
title: 'He works',
// body: '',
),
);
}
我想使用background_fetch包,但问题是我不知道获取通知的方法和代码放在哪里。是后台调用函数里面吗?!
感谢所有帮助过我的人。
您自己使这个实现变得复杂,使用:Awesome_notification,Awesome_notification_fcm
并在main.dart中初始化:
await Notify.initializeNotification();
await Notify.initializeRemoteNotifications();
创建通知:
class Notify {
static Future initializeNotification() async {
AwesomeNotifications().requestPermissionToSendNotifications();
AwesomeNotifications().initialize(
null,
[
/// create channels
]
}
static Future<bool> instantNotify(String title) async {
final AwesomeNotifications awesomeNotifications = AwesomeNotifications();
return await awesomeNotifications.createNotification(
content: NotificationContent(
id: Random().nextInt(100),
title: title,
body: "Notification that delivers instantly on trigger.",
channelKey: 'channel_key',
),
);
}
static Future<bool> scheduleNotification() async {
final AwesomeNotifications awesomeNotifications = AwesomeNotifications();
return await awesomeNotifications.createNotification(
schedule: NotificationInterval(interval: 10),
content: NotificationContent(
id: Random().nextInt(100),
title: "Scheduled Notification",
body: "So this notification will get triggered when it's 9 pm on my device and the date is December 9, 2021.",
channelKey: 'channel_key_different',
wakeUpScreen: true,
autoDismissible: false,
category: NotificationCategory.Reminder,
),
);
}
static Future<void> retrieveScheduledNotifications() async {
final AwesomeNotifications awesomeNotifications = AwesomeNotifications();
List<NotificationModel> scheduledNotifications = await awesomeNotifications.listScheduledNotifications();
print(scheduledNotifications);
}
/// *********************************************
/// INITIALIZATION METHODS
/// *********************************************
static Future<void> initializeRemoteNotifications() async {
await Firebase.initializeApp();
await AwesomeNotificationsFcm().initialize(
onFcmSilentDataHandle: mySilentDataHandle,
onFcmTokenHandle: myFcmTokenHandle,
onNativeTokenHandle: myNativeTokenHandle,
// This license key is necessary only to remove the watermark for
// push notifications in release mode. To know more about it, please
// visit http://awesome-notifications.carda.me#prices
// licenseKeys: null,
debug: true,
);
}
/// *********************************************
/// REMOTE NOTIFICATION EVENTS
/// *********************************************
/// Use this method to execute on background when a silent data arrives
/// (even while terminated)
@pragma("vm:entry-point")
static Future<void> mySilentDataHandle(FcmSilentData silentData) async {
print('"SilentData": ${silentData.toString()}');
if (silentData.createdLifeCycle != NotificationLifeCycle.Foreground) {
print("bg");
} else {
print("FOREGROUND");
}
print("starting long task");
await Future.delayed(const Duration(seconds: 4));
final url = Uri.parse("http://google.com");
final re = await http.get(url);
print(re.body);
print("long task done");
}
/// Use this method to detect when a new fcm token is received
@pragma("vm:entry-point")
static Future<void> myFcmTokenHandle(String token) async {
debugPrint('FCM Token:"$token"');
}
/// Use this method to detect when a new native token is received
@pragma("vm:entry-point")
static Future<void> myNativeTokenHandle(String token) async {
debugPrint('Native Token:"$token"');
}
// Request FCM token to Firebase
Future<String> getFirebaseMessagingToken() async {
showInAppMessage();
String firebaseAppToken = '';
if (await AwesomeNotificationsFcm().isFirebaseAvailable) {
try {
firebaseAppToken = await AwesomeNotificationsFcm().requestFirebaseAppToken();
print(firebaseAppToken);
} catch (exception) {
debugPrint('$exception');
}
} else {
debugPrint('Firebase is not available on this project');
}
return firebaseAppToken;
}
}