In the application I use rabbitmq, background_fetch and flutter_local_notifications.
When the application is launched or minimized everything works fine, but when it is closed I cannot run my application in background, i can not find any information about this, all another questions only for FCM, no more info about flutter with amqp, I am a newbee in this framework
main.dart
Future<void> backgroundFetchHeadlessTask(String taskId) async {
await Rabbitmq.getDelivery();
BackgroundFetch.finish(taskId);
}
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
// Streams are created so that app can respond to notification-related events since the plugin is initialised in the `main` function
final BehaviorSubject<ReceivedNotification> didReceiveLocalNotificationSubject = BehaviorSubject<ReceivedNotification>();
final BehaviorSubject<String> selectNotificationSubject = BehaviorSubject<String>();
NotificationAppLaunchDetails notificationAppLaunchDetails;
void main() async {
runApp(MyApp());
await initPlatformState();
}
Future<void> initPlatformState() async {
BackgroundFetch.configure(BackgroundFetchConfig(
startOnBoot: true,
minimumFetchInterval: 15,
stopOnTerminate: false,
enableHeadless: true,
requiresBatteryNotLow: false,
requiresCharging: false,
requiresStorageNotLow: false,
requiresDeviceIdle: false,
requiredNetworkType: NetworkType.ANY
), (String taskId) async {
await backgroundFetchHeadlessTask(taskId);
}
);
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
void initState() {
super.initState();
_requestIOSPermissions();
_configureDidReceiveLocalNotificationSubject();
_configureSelectNotificationSubject();
BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask);
}
void _requestIOSPermissions() {
flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(
alert: true,
badge: true,
sound: true,
);
}
void _configureDidReceiveLocalNotificationSubject() {
didReceiveLocalNotificationSubject.stream
.listen((ReceivedNotification receivedNotification) async {
await showDialog(
context: context,
builder: (BuildContext context) => CupertinoAlertDialog(
title: receivedNotification.title != null
? Text(receivedNotification.title)
: null,
content: receivedNotification.body != null
? Text(receivedNotification.body)
: null,
actions: [
CupertinoDialogAction(
isDefaultAction: true,
child: Text('Ok'),
onPressed: () async {
Navigator.of(context, rootNavigator: true).pop();
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
LandingPage(selectedPage: 3),
//SecondScreen(receivedNotification.payload),
),
);
},
)
],
),
);
});
}
void _configureSelectNotificationSubject() {
selectNotificationSubject.stream.listen((String payload) async {
await Navigator.push(
context,
MaterialPageRoute(builder: (context) => LandingPage(selectedPage: 3)),//SecondScreen(payload)),
);
});
}
#override
void dispose() {
didReceiveLocalNotificationSubject.close();
selectNotificationSubject.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'My first app',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: LandingPage(),
);
}
}
And inside rabbitmq.dart
class Rabbitmq {
static Future getDelivery() async
{
var auth = await DBProvider.db.getAuth();
print(hash);
Client client = new Client(
settings: ConnectionSettings(
host: 'myhost',
authProvider: AmqPlainAuthenticator('user', 'password')
)
);
client
.channel()
.then((Channel channel) => channel.queue("queue", durable: true))
.then((Queue queue) => queue.consume())
.then((Consumer consumer) => consumer.listen((AmqpMessage message) async {
Map messageBody = message.payloadAsJson;
if(messageBody.containsKey('message') == true && messageBody.containsKey('from') == true)
{
await Rabbitmq()._showNotification(messageBody['message'], messageBody['from']);
}
})
);
}
Future<void> _showNotification(String text, String from) async {
var initializationSettingsAndroid = new AndroidInitializationSettings('#mipmap/ic_launcher');
var initializationSettingsIOS = new IOSInitializationSettings();
var initializationSettings = new InitializationSettings(initializationSettingsAndroid, initializationSettingsIOS);
FlutterLocalNotificationsPlugin().initialize(initializationSettings);
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
var androidPlatformChannelSpecifics = AndroidNotificationDetails(
'com.example.g2r_market/chat', 'Уведомления чата', 'Настройка уведомлений для чатов',
importance: Importance.Max, priority: Priority.High, ticker: 'ticker'
);
var iOSPlatformChannelSpecifics = IOSNotificationDetails();
var platformChannelSpecifics = NotificationDetails(androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
await flutterLocalNotificationsPlugin.show(
0, from, text, platformChannelSpecifics,
payload: 'item x');
}
}
Related
I want the notification to display on particular time which end-user selects it using time picker. i'm confused with the process of how to do that. please guide me to solve this problem.
Getting variable from model:
late var selectedTime = TimeOfDay.now();
User picks the time from front-end:
Future<Null> selectTime (BuildContext context) async{
picked = (await showTimePicker(
context: context,
initialTime: model.selectedTime,
))!;
if (picked != null){
setState(() {
model.selectedTime = picked;
this.preferences?.setString("dateValue", model.selectedTime.format(context));
});
}
}
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: Container(
child: GestureDetector(
onTap: (){
selectTime(context);
},
child: Text(
'${this.preferences?.getString("dateValue") ?? model.defaultTime.format(context)}',
),
),
This class contains notifications sepearately.
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
final mymodel = myModel();
Future<void> firstNotifications() async{
var initializationSettingsAndroid =
AndroidInitializationSettings('#mipmap/ic_launcher');
var initializationSettings = InitializationSettings(
android: initializationSettingsAndroid);
flutterLocalNotificationsPlugin.initialize(initializationSettings);
const AndroidNotificationDetails firstNotificationSpecifics =
AndroidNotificationDetails(
'channel id',
'channel name',
'channel description',
sound: RawResourceAndroidNotificationSound('song1'),
playSound: true,
enableVibration: false ,
visibility: NotificationVisibility.secret,
autoCancel: true,
timeoutAfter:7000
);
const NotificationDetails firstNotificationPlatformSpecifics =
NotificationDetails(android: firstNotificationSpecifics);
await flutterLocalNotificationsPlugin.zonedSchedule
(1, null, null, tz.TZDateTime.now(tz.local).add(Duration(seconds: 5)), firstNotificationPlatformSpecifics,
uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime,
androidAllowWhileIdle: true);
}
But now this code play notification after 5 seconds which i had given manually. but i want the notification to display at the user picked time.
You need to call the setNotification function after you picked the time
Future<Null> selectTime (BuildContext context) async{
picked = (await showTimePicker(
context: context,
initialTime: model.selectedTime,
))!;
if (picked != null){
setState(() {
model.selectedTime = picked;
setNotification(picked);//Call it here
this.preferences?.setString("dateValue", model.selectedTime.format(context));
});
}
}
void setNotification( TimeOfDay selectedTime) {
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
// final mymodel = myModel();
Future<void> firstNotifications() async {
var initializationSettingsAndroid =
AndroidInitializationSettings('#mipmap/ic_launcher');
var initializationSettings =
InitializationSettings(android: initializationSettingsAndroid);
flutterLocalNotificationsPlugin.initialize(initializationSettings);
const AndroidNotificationDetails firstNotificationSpecifics =
AndroidNotificationDetails(
'channel id', 'channel name', 'channel description',
sound: RawResourceAndroidNotificationSound('song1'),
playSound: true,
enableVibration: false,
visibility: NotificationVisibility.secret,
autoCancel: true,
timeoutAfter: 7000);
const NotificationDetails firstNotificationPlatformSpecifics =
NotificationDetails(android: firstNotificationSpecifics);
tz.TZDateTime zonedTime = tz.TZDateTime.local(DateTime.now().year,DateTime.now().month,DateTime.now().day,selectedTime.hour,
selectedTime.minute).subtract(DateTime.now().timeZoneOffset);
await flutterLocalNotificationsPlugin.zonedSchedule(
1,
null,
null,
tz.TZDateTime.now(tz.local) ,
firstNotificationPlatformSpecifics,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
androidAllowWhileIdle: true);
}
}
I have built my own application in flutter and I have implemented Local notifications and also FirebaseMessaging:
final FirebaseMessaging firebaseMessaging = FirebaseMessaging();
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
#override
void initState() {
super.initState();
registerNotification();
configLocalNotification();
firebaseMessaging.requestNotificationPermissions();
}
void registerNotification() {
firebaseMessaging.requestNotificationPermissions();
firebaseMessaging.configure(onMessage: (Map<String, dynamic> message) {
print('onMessage: $message');
Platform.isAndroid
? showNotification(message['notification'])
: showNotification(message['aps']['alert']);
return ;
}, onResume: (Map<String, dynamic> message) {
print('onResume: $message');
return Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NotificationsScreen()));
}, onLaunch: (Map<String, dynamic> message) {
print('onLaunch: $message');
return;
});
firebaseMessaging.getToken().then((token) {
print('token: $token');
FirebaseFirestore.instance
.collection('Consultant')
.doc(firebaseUser.uid)
.update({'deviceToken': token});
}).catchError((err) {
//Fluttertoast.showToast(msg: err.message.toString());
});
}
Future selectNotification(String payload) async {
if (payload != null) {
debugPrint('notification payload: $payload');
}
await Navigator.push(
context,
MaterialPageRoute<void>(builder: (context) => NotificationsScreen(payload: payload,)),
);
}
void showNotification(message) async {
var androidPlatformChannelSpecifics = new AndroidNotificationDetails(
Platform.isAndroid
? 'it.wytex.vibeland_pro_app'
: 'it.wytex.vibeland_pro_app',
'Vibeland',
'Vibeland',
playSound: true,
enableVibration: true,
importance: Importance.max,
priority: Priority.high,
ongoing: true,
);
var iOSPlatformChannelSpecifics = new IOSNotificationDetails();
var platformChannelSpecifics = new NotificationDetails(
android: androidPlatformChannelSpecifics, iOS: iOSPlatformChannelSpecifics);
print(message);
print(message['body'].toString());
print(json.encode(message));
await flutterLocalNotificationsPlugin.show(0, message['title'].toString(),
message['body'].toString(), platformChannelSpecifics,
payload: json.encode(message));
await flutterLocalNotificationsPlugin.show(
0, '📩 Hai ricevuto un messaggio 📩 ', 'Controlla subito le Tue notifiche 🔔🔔', platformChannelSpecifics,
payload: 'item x',
);
}
void configLocalNotification() {
var initializationSettingsAndroid =
new AndroidInitializationSettings('vibeland_pro');
var initializationSettingsIOS = new IOSInitializationSettings(
requestAlertPermission: true,
requestBadgePermission: true,
requestSoundPermission: true,
);
var initializationSettings = new InitializationSettings(
android: initializationSettingsAndroid, iOS: initializationSettingsIOS);
flutterLocalNotificationsPlugin.initialize(initializationSettings);
}
I wrote a function into index.js to push also firestore notification when user creates order etc..
Everything works fine, I got Localnotifications when all is open and FCM notifications when app is in background, the problem is when the app sleep...
after some minutes the app goes in sleep mode and to start it again you need to restart the application which keep my user Logged:
MultiProvider(
providers: [
Provider<AuthenticationProvider>(
create: (_) => AuthenticationProvider(FirebaseAuth.instance),
),
StreamProvider(
create: (context) => context.read<AuthenticationProvider>().authState,
)
],
child: MaterialApp(
theme: ThemeData(
pageTransitionsTheme: PageTransitionsTheme(
builders: {
TargetPlatform.android: CupertinoPageTransitionsBuilder(
),
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
}
)
),
debugShowCheckedModeBanner: false,
title: 'Vibeland Admin Authentication',
home: Authenticate(),
),
);
}
}
class Authenticate extends StatelessWidget {
#override
Widget build(BuildContext context) {
final firebaseUser = context.watch<User>();
if (firebaseUser != null) {
return StartScreenLogged();
}
return StartScreen();
}
}
but if I dont run again the app I dont get the notification because the app sleep.
what we need to do in this case?
I wanted to make my application to work in 2 different language.
I guess problem is that I cannot handle Cosumer in multiprovider ,could it be?
I am using flutter_localization package alongside with flutter provider package;
Here is my code:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
AppLanguage appLanguage = AppLanguage();
await appLanguage.fetchLocale();
SharedPreferences.getInstance().then((prefs) {
var darkModeOn = prefs.getBool('darkMode') ?? true;
runApp(
ChangeNotifierProvider<ThemeManager>(
builder: (_) => ThemeManager(lightTheme),
child: MyApp(appLanguage: appLanguage),
),
);
});
}
Class My App
class MyApp extends StatelessWidget {
final AppLanguage appLanguage;
MyApp({this.appLanguage});
#override
Widget build(BuildContext context) {
final themeNotifier = Provider.of<ThemeManager>(context);
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: ApiService()),
ChangeNotifierProxyProvider<ApiService, StudentData>(
builder: (ctx, auth, _) => StudentData(auth.token, auth.school),
)
],
child: Consumer<ApiService>(
builder: (ctx, auth, _) => ChangeNotifierProvider<AppLanguage>(
builder: (_) => appLanguage,
child: Consumer<AppLanguage>(
builder: (context, model, child) => MaterialApp(
locale: appLanguage.appLocal,
supportedLocales: [Locale('ru', 'RU'), Locale('uz', 'UZ')],
localizationsDelegates: [
AppLocalization.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
debugShowCheckedModeBanner: false,
title: "Socratus |Mobile",
theme: themeNotifier.getTheme(),
home: auth.isAuth
? MainScreen()
: FutureBuilder(
future: auth.tryAutoLogin(),
builder: (ctx, authResultSnapshot) => LoginScreen()),
routes: {
MainScreen.routeName: (ctx) => MainScreen(),
ProfilePage.routeName: (ctx) => ProfilePage(),
SettingsPage.routeName: (ctx) => SettingsPage(
appLanguage: appLanguage,
),
ChangePassword.routeName: (ctx) => ChangePassword(),
HomeworkScreen.routeName: (ctx) => HomeworkScreen(),
HWDetails.routeName: (ctx) => HWDetails(),
NewsPage.routeName: (ctx) => NewsPage(),
QuestionAndAnswers.routeName: (ctx) => QuestionAndAnswers(),
MyDownloads.routeName: (ctx) => MyDownloads(),
},
),
),
),
),
);
}
}
Here how I tried to implement
class AppLocalization {
final Locale locale;
AppLocalization(this.locale);
static AppLocalization of(BuildContext context) {
return Localizations.of<AppLocalization>(context, AppLocalization);
}
static const LocalizationsDelegate<AppLocalization> delegate =
_AppLocalizationDelegate();
Map<String, String> _localizedStrings;
Future<bool> load() async {
String jsonString = await rootBundle
.loadString('assets/translations/${locale.languageCode}.json');
Map<String, dynamic> jsonMap = json.decode(jsonString);
_localizedStrings = jsonMap.map((key, value) {
return MapEntry(key, value.toString());
});
return true;
}
String translate(String key) {
return _localizedStrings[key];
}
}
class _AppLocalizationDelegate extends LocalizationsDelegate<AppLocalization> {
// This delegate instance will never change (it doesn't even have fields!)
// It can provide a constant constructor.
const _AppLocalizationDelegate();
#override
bool isSupported(Locale locale) {
// Include all of your supported language codes here
return ['ru', 'uz'].contains(locale.languageCode);
}
#override
Future<AppLocalization> load(Locale locale) async {
AppLocalization localizations = new AppLocalization(locale);
await localizations.load();
return localizations;
}
#override
bool shouldReload(_AppLocalizationDelegate old) => false;
}
And my provider :
class AppLanguage extends ChangeNotifier {
Locale _appLocale = Locale('ru');
Locale get appLocal => _appLocale ?? Locale("ru");
fetchLocale() async {
var prefs = await SharedPreferences.getInstance();
if (prefs.getString('language_code') == null) {
_appLocale = Locale('ru');
return Null;
}
_appLocale = Locale(prefs.getString('language_code'));
return Null;
}
void changeLanguage(Locale type) async {
var prefs = await SharedPreferences.getInstance();
if (_appLocale == type) {
return;
}
if (type == Locale("uz")) {
_appLocale = Locale("uz");
await prefs.setString('language_code', 'uz');
await prefs.setString('countryCode', 'UZ');
} else {
_appLocale = Locale("ru");
await prefs.setString('language_code', 'ru');
await prefs.setString('countryCode', 'RU');
}
notifyListeners();
}
}
If this is not a correct way , how can I implement this feature ?
The way you are trying to implement is explained in this article, if you'd like a different approach I'd suggest you to check out Easy Localization from pub.dev which is currently handling well all the Flutter internationalization features.
MY SOLUTION
Note:
Used get_storage for persistent key/value storage.
The above question has been asked in reference to this tutorial
main.dart
void main() async {
...
///Initialize Storage
await GetStorage.init();
...
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
...
///Check for locale
AppLanguage language = AppLanguage();
language.fetchLocale();
final providers = [
/**
* Add all providers in here like below
* ChangeNotifierProvider<ThemeModel>(create: (context) => ThemeModel(),),
*/
...
///Language provider
ChangeNotifierProvider<AppLanguage>(
create: (context) => language,
),
...
];
return MultiProvider(
providers: providers,
child: Consumer<AppLanguage>(
builder: (context, model, _) {
/**
* Access other providers apart from language like below
* Provider.of<ThemeModel>(context).theme,
*/
...
return MaterialApp(
locale: model.appLocal,
supportedLocales: [
Locale('ru', 'RU'),
Locale('uz', 'UZ'),
],
localizationsDelegates: [
AppLocalization.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
...
);
},
),
);
}
}
Here is the provider language.dart
class AppLanguage extends ChangeNotifier {
Locale _appLocale = Locale('ru');
Locale get appLocal => _appLocale;
fetchLocale() async {
var storage = GetStorage();
if (!(storage.hasData('language_code'))) {
_appLocale = Locale('ru');
return Null;
}
_appLocale = Locale(storage.read('language_code'));
return Null;
}
Future<void> updateLanguage(Locale type) async {
var storage = GetStorage();
if (_appLocale == type) {
return;
}
if (type == Locale('uz')) {
_appLocale = Locale('sw');
await storage.write('language_code', 'uz');
await storage.write('countryCode', 'UZ');
} else {
_appLocale = Locale('ru');
await storage.write('language_code', 'ru');
await storage.write('countryCode', 'RU');
}
notifyListeners();
}
}
The AppLocalization class on the above question has been implemented correctly.
USAGE
Widget build(BuildContext context) {
...
final languageModel = Provider.of<LanguageModel>(context);
...
return Scaffold(
body: ListView(
children: [
GestureDetector(
...
onTap: () {
languageModel.appLocal == Locale('ru')
? languageModel.updateLanguage(Locale('uz'))
: languageModel.updateLanguage(Locale('ru'));
},
),
],
),
);
}
Thoughts
Language provider should be consumed first and the rest to follow i.e ApiService.. e.t.c.
Any misconception on my code and bugs, don't hesitate to comment. I will reply as soon as possible.
Im having issues trying to get the current user from a child widget. When I login I can see the response if I print from AuthProvider. However, when I try to get the currentUser from a child widget the value is null.
Login in.
Calling query from UsersProvider.
setting the currentUser variable in said provider.
go back to AuthProvider and finish login in the user.
main
#override
Widget build(BuildContext context) {
print('build Main');
return MultiProvider(
providers: [
ChangeNotifierProvider.value(
value: AuthProvider(),
),
ChangeNotifierProvider.value(
value: UsersProvider(),
),
],
child: Consumer<AuthProvider>(
builder: (ctx, auth, _) => MaterialApp(
title: 'Wayuu',
theme: ThemeData(
primarySwatch: Colors.indigo,
accentColor: Colors.purpleAccent,
// textTheme: ThemeData.dark().textTheme.copyWith(
// title: TextStyle(
// color: Theme.of(context).accentColor,
// fontWeight: FontWeight.bold,
// fontSize: 20,
// ),
// ),
),
home: auth.isAuthenticated
? HomeState()
: FutureBuilder(
future: auth.isAuth(),
builder: (ctx, authResultSnapshot) =>
authResultSnapshot.connectionState ==
ConnectionState.waiting
? Splash()
: Login()),
routes: {
Home.routeName: (context) => Home(),
Profile.routeName: (context) => Profile(),
ForgotPassword.routeName: (context) => ForgotPassword(),
RegisterScreen.routeName: (context) => RegisterScreen(),
Favorites.routeName: (context) => Favorites(),
Podcast.routeName: (context) => Podcast(),
MyAccount.routeName: (context) => MyAccount(),
},
),
),
);
}
}
AuthProvider
bool _userAuthenticated = false;
bool get isAuthenticated {
return _userAuthenticated;
}
Future<bool> isAuth() async {
final FirebaseUser response = await FirebaseAuth.instance.currentUser();
if (response != null) {
_userAuthenticated = true;
notifyListeners();
}
return _userAuthenticated;
}
Future<void> loginUser(Map<String, String> loginData) async {
UsersProvider usersProvider = UsersProvider();
try {
final response = await FirebaseAuth.instance.signInWithEmailAndPassword(
email: loginData['usernameOrEmail'], password: loginData['password']);
// _currentUserId = response.user.uid;
await usersProvider.getUserById(response.user.uid);
await storeUserIdInSharePreferences(response.user.uid);
_userAuthenticated = true;
notifyListeners();
} catch (error) {
throw HttpException(error.toString());
}
}
}
UsersProvider
class UsersProvider with ChangeNotifier {
Map<String, dynamic> _currentUser = {};
Map<String, dynamic> get currentUser {
return {..._currentUser};
}
Future<void> getUserById(String userId) async {
try {
QuerySnapshot querySnapshot = await Firestore.instance
.collection('users')
.where('id', isEqualTo: userId)
.getDocuments();
querySnapshot.documents.forEach((documentData) {
_currentUser = {
'id': documentData.data['id'],
'fullName': documentData.data['fullName'],
'email': documentData.data['email'],
'username': documentData.data['username'],
'profilePicture': documentData.data['profilePicture'],
'bio': documentData.data['bio'],
'instagram': documentData.data['instagram'],
'facebook': documentData.data['facebook'],
'web': documentData.data['web']
};
});
notifyListeners();
} catch (error) {
print('error from query');
print(error);
throw HttpException(error.toString());
}
}
}
Profile Widget
Widget build(BuildContext context) {
print('build profile widget');
final currentUser = Provider.of<UsersProvider>(context).currentUser;
print(currentUser);
I get null from here when my "Profile" widget executes
final currentUser = Provider.of<UsersProvider>(context).currentUser;
Thank you in advance !
The instance of UsersProvider that you create inside of loginUser exists only in the scope of that method. Refactor the method to return the response and then use it to update the provider.
var response = await authProvider.loginUser(loginData);
await Provider.of<UsersProvider>(context).getUserById(response.user.uid);
Then when you call the current user it'll be the same instance that will be updated.
I want my flutter app to open a page when a local notification is clicked. I've defined the following codes in the event listener:
Navigator.push(
Ccontext,
MaterialPageRoute(builder: (context) => PendingOrders()),
);
debugPrint('notification payload: ' + payload);
The event listener is executed successfully and printed the debugPrint's parameter when a local notification is called but it can not open the PendingOrders route.
Here is the full code in the main.dart
class _MyAppState extends State<MyApp> {
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
#override
void initState() {
super.initState();
var android = AndroidInitializationSettings('mipmap/ic_launcher');
var ios = IOSInitializationSettings();
var platform = InitializationSettings(android, ios);
flutterLocalNotificationsPlugin.initialize(platform,onSelectNotification: onSelectNotification);
_firebaseMessaging.subscribeToTopic('order-created');
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
print("onMessage: $message");
showNotification(message);
},
onLaunch: (Map<String, dynamic> message) async {
print("onLaunch: $message");
print("Hafijur");
},
onResume: (Map<String, dynamic> message) async {
print("onResume: ${message['notification']['data']['click_action']}");
},
);
_firebaseMessaging.requestNotificationPermissions(
const IosNotificationSettings(sound: true, badge: true, alert: true));
}
showNotification(Map<String, dynamic> msg) async {
var android = new AndroidNotificationDetails(
'sdffds dsffds',
"CHANNLE NAME",
"channelDescription",
);
var iOS = new IOSNotificationDetails();
var platform = new NotificationDetails(android, iOS);
await flutterLocalNotificationsPlugin.show(
0, "New order arrived", "Hey, hurry up! you have a order to deliver", platform,payload: "order created");
}
Future onSelectNotification(String payload) async {
this.build(context);
Navigator.push(
context,
MaterialPageRoute(builder: (context) => PendingOrders()),
);
if (payload != null) {
debugPrint('notification payload: ' + payload);
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: Constants.lightTheme,
debugShowCheckedModeBanner: false,
routes: {
"/": (context) => MainScreen(),
"/login": (context) => LoginPage(),
"/dashboard": (context) => Dashboard(),
"/all_orders": (context) => AllOrders(),
"/pending_orders": (context) => PendingOrders(),
"/delivered_orders": (context) => DeliveredOrders(),
"/order": (context) => Order(),
},
);
}
}
To receive payload when app was closed after open with tapping on notification then you have to use getNotificationAppLaunchDetails method
final NotificationAppLaunchDetails? notificationAppLaunchDetails =
await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails();
String? payload = notificationAppLaunchDetails!.payload;
put this code in main after notification initialization lines
use onSelectNotification will be triggered only if the app is already open when tapping notification.
but if the app is already closed and you want to run it after tapping notification with handling the payload value then you have to use getNotificationAppLaunchDetails
code will be something like following:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
var initializationSettingsAndroid =
AndroidInitializationSettings('#mipmap/ic_launcher');
var initializationSettingsIOs = IOSInitializationSettings();
var initSetttings = InitializationSettings(
android: initializationSettingsAndroid, iOS: initializationSettingsIOs);
var cnf = await flutterLocalNotificationsPlugin.initialize(initSetttings,
onSelectNotification: onSelectNotification);
final NotificationAppLaunchDetails? notificationAppLaunchDetails =
await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails();
print('payload=');
String? payload= notificationAppLaunchDetails!.payload;
print(payload);
runApp(
MaterialApp(
home: payload== 'page1' ? Page1() : MainPage()
)
);
}
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
var initializationSettingsAndroid =
const AndroidInitializationSettings('#drawable/icon_menu_car_no');
var initializationSettingsIOs = const IOSInitializationSettings();
var initSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOs);
flutterLocalNotificationsPlugin?.initialize(initSettings,
onSelectNotification: (payload) {
switch(payload){
case "A":
//route to some where
break;
}
});
FirebaseMessaging.onMessage.listen((message) async {
showNotification(message);
});
void showNotification(RemoteMessage message) {
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification?.android;
if (notification != null && android != null && !kIsWeb) {
var messageData = FirebaseMessageObject.fromJson(message.data);
flutterLocalNotificationsPlugin?.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel!.id,
channel!.name,
channelDescription: channel!.description,
icon: 'icon_menu_car_no',
),
),
payload: messageData.functionID);
}
}
class FirebaseMessageObject{
late String functionID;
static FirebaseMessageObject fromJson(Map<String,dynamic> data){
FirebaseMessageObject object = FirebaseMessageObject();
object.functionID = data['functionID'];
return object;
}
}
※FCM Request
{
"to": "your fcm token ",
"notification": {
"body": "I am body ",
"title": "I am title ",
"sound": "default"
},
"data":{
"functionID":"A"
},
"priority": "high"
}
I had a similar issue but this fixed my problem
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) {
displayNotification(message);
return;
},
onResume: (Map<String, dynamic> message) {
_navigateToItemDetail(message);
return;
},
onLaunch: (Map<String, dynamic> message) {
_navigateToItemDetail(message);
return;
},
);
void _navigateToItemDetail(Map<String, dynamic> message) async {
Navigator.popUntil(context, (Route<dynamic> route) => route is PageRoute);
await Navigator.of(context).push(PageRouteBuilder(
opaque: false, pageBuilder: (context, _, __) => NewPage();
}