_firebaseMessaging.*configure*( onMessage: (Map<String, dynamic> message) async {
print("\n\n on1Message: ${message.toString()}");
Map<String, dynamic> object = json.decode(
message['data']['notification'].toString());
print(
'\n\n Object==${message['data']}\n\n object===$object');
object['work'] = 'updateCount';
Stream<Map<String, dynamic>> stream =
Stream.value(object);
streamController.addStream(stream);
print("\n\n object ---> ${object}");
Error in Console Log----------------------------------------------------------------
722:24: Error: The method 'configure' isn't defined for the class 'FirebaseMessaging'.
'FirebaseMessaging' is from 'package:firebase_messaging/firebase_messaging.dart' ('/E:/flutterSDK/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_messaging-9.1.0/lib/firebase_messaging.dart').
Try correcting the name to the name of an existing method, or defining a method named 'configure'.
_firebaseMessaging.configure
( ^^^^^^^^^
configure() method is not working after the update of the Package of Firebase Cloud Messaging. I tried different solution from the stack overflow but nothing works.
What Should I do in my case.
The new FirebaseMessaging is a little bit different.
Here are two interesting links:
https://firebase.google.com/docs/flutter/setup?platform=android
https://firebase.flutter.dev/docs/messaging/usage/
After adding firebase to the App, this is what I do (NotificationDetails is a class I wrote to show the details of the Notification. You can write you own class.):
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
routes: {
'/': (context) => AppStarter(),
'/message': (context) => NotificationDetails(),
},
),
);
}
class AppStarter extends StatefulWidget{
#override
_AppStarterState createState() => _AppStarterState();
}
class _AppStarterState extends State<AppStarter>
{
FirebaseMessaging messaging = FirebaseMessaging.instance;
Future<void> showMeMyToken()
async {
var myToken = await messaging.getToken();
print("My Token is: " + myToken.toString());
}
#override
void initState() {
super.initState();
showMeMyToken();
FirebaseMessaging.instance.getInitialMessage().then((value) {
if(value != null)
{
Navigator.push(context,
MaterialPageRoute(
builder: (context){return NotificationDetails();},
settings: RouteSettings(arguments: value.data,),
),
);
}
});
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
if (message.notification != null) {
print('Message on Foreground: ${message.notification}');
}
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message)
{
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {return NotificationDetails();},
settings: RouteSettings(arguments: message.data,)
),
);
});
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Just a Test',
home: AppHome(),
);
}
}
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print("Handling a background message :-): ${message.data}");
//Here you can do what you want with the message :-)
}
Related
I couldn't find any documents about the new version. Versions 7 and 6 have a large number of documents, while 9 is almost nonexistent. Not only me but most people couldn't find it.
I just wanted to send simple notifications to the background. I would be very happy if anyone shared a document about the new version.
Or should I use the old version?
I suppose that you know how to add firebase to your App. If not: https://firebase.google.com/docs/flutter/setup?platform=android
After adding firebase to the App, this is what I do :
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
routes: {
'/': (context) => AppStarter(),
'/message': (context) => NotificationDetails(),
},
),
);
}
class AppStarter extends StatefulWidget{
#override
_AppStarterState createState() => _AppStarterState();
}
class _AppStarterState extends State<AppStarter>
{
FirebaseMessaging messaging = FirebaseMessaging.instance;
Future<void> showMeMyToken()
async {
var myToken = await messaging.getToken();
print("My Token is: " + myToken.toString());
}
#override
void initState() {
super.initState();
showMeMyToken();
FirebaseMessaging.instance.getInitialMessage().then((value) {
if(value != null)
{
Navigator.push(context,
MaterialPageRoute(
builder: (context){return NotificationDetails();},
settings: RouteSettings(arguments: value.data,),
),
);
}
});
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
if (message.notification != null) {
print('Message on Foreground: ${message.notification}');
}
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message)
{
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {return NotificationDetails();},
settings: RouteSettings(arguments: message.data,)
),
);
});
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Just a Test',
home: AppHome(),
);
}
}
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print("Handling a background message :-): ${message.data}");
//Here you can do what you want with the message :-)
}
I created a sample app showcasing how to implement the notification system with FCM on version 9.
You can refer to this project and if you need more informations, I'll edit this answer !
I created a dynamic link in Firebase Console and added the code in Flutter to take me to a specific page within my app . This works . I am unsure now thou of how to create more dynamic links within Flutter to take me to more/other pages within my app after creating a new dynamic link in Firebase Console . I followed code below from a tutorial :
runApp(MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Dynamic Links Example',
routes: <String, WidgetBuilder>{
'/': (BuildContext context) => MyApp(),
'/helloworld': (BuildContext context) => LinkPage(),
},
));
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _linkMessage;
bool _isCreatingLink = false;
String _testString =
"To test: long press link and then copy and click from a non-browser "
"app. Make sure this isn't being tested on iOS simulator and iOS xcode "
"is properly setup. Look at firebase_dynamic_links/README.md for more "
"details.";
#override
void initState() {
super.initState();
initDynamicLinks(); //
}
void setupNotification() async {
_firebaseMessaging.getToken().then((token) {
print(token);
});
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
print("Message: $message");
},
onResume: (Map<String, dynamic> message) async {
print("Message: $message");
},
onLaunch: (Map<String, dynamic> message) async {
print("Message: $message");
},
);
}
void initDynamicLinks() async {
FirebaseDynamicLinks.instance.onLink(
onSuccess: (PendingDynamicLinkData dynamicLink) async {
final Uri deepLink = dynamicLink?.link;
print(deepLink);
if (deepLink != null) {
Navigator.pushNamed(context, '/helloworld');
}
}, onError: (OnLinkErrorException e) async {
print('onLinkError');
print(e.message);
});
final PendingDynamicLinkData data =
await FirebaseDynamicLinks.instance.getInitialLink();
final Uri deepLink = data?.link;
if (deepLink != null) {
Navigator.pushNamed(context, '/helloworld');
}
}
Future<void> _createDynamicLink(bool short) async {
setState(() {
_isCreatingLink = true;
});
final DynamicLinkParameters parameters = DynamicLinkParameters(
uriPrefix: 'http://cx4k7.app.goo.gl',
link: Uri.parse('My http url'),
androidParameters: AndroidParameters(
packageName: 'my package id/name',
minimumVersion: 0,
),
dynamicLinkParametersOptions: DynamicLinkParametersOptions(
shortDynamicLinkPathLength: ShortDynamicLinkPathLength.short,
),
);
Uri url;
if (short) {
final ShortDynamicLink shortLink = await parameters.buildShortLink();
url = shortLink.shortUrl;
} else {
url = await parameters.buildUrl();
}
setState(() {
_linkMessage = url.toString();
_isCreatingLink = false;
});
}
The if statements for what happens if deeplink not null was key to finding answer . So was just a matter of writing few if statements for what happens when deeplink has same value as in FCM Console
I am practicing a email authentication in flutter and almost everything is over. Now, i want to use sharedPreference to stay the user logged in. I have tried something, but i don't get result. I am using a bool type to get whether user loggedIn or not. But i am very new to this, can you help me in this? and is there anything i am missing out?
This is the sharedPreference static Class i am using
class sharedPreference {
static String sharedPreferenceUserLoggedInKey = 'userLoggedIn';
static String sharedPreferenceUserSignedUpKey = 'userSignedUp';
//saving data to sharedPreference
static Future<bool> saveUserLoggedInSharedPreference(
bool isUserLoggedIn) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return await prefs.setBool(sharedPreferenceUserLoggedInKey, isUserLoggedIn);
}
static Future<bool> saveUserSignedUpSharedPreference(
bool isUserSignUp) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return await prefs.setBool(sharedPreferenceUserSignedUpKey, isUserSignUp);
}
//getting data to sharedPreference
static Future<bool> getUserLoggedInSharedPreference() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return await prefs.getBool(sharedPreferenceUserLoggedInKey);
}
static Future<bool> getUserSignedUpSharedPreference() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return await prefs.getBool(sharedPreferenceUserSignedUpKey);
}
}
This is the signIn button triggering the setBool:
SignInButton:
FlatButton(
onPressed: ()
{
HelperFunction.saveUserLoggedInSharedPreference(true);
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => DashBoard(email: email),
),
})
The main function
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light
.copyWith(systemNavigationBarColor: Colors.black));
runApp(
DevicePreview(
enabled: kReleaseMode,
builder: (context) => FlashChat(),
),
);
}
class FlashChat extends StatefulWidget {
#override
_FlashChatState createState() => _FlashChatState();
}
class _FlashChatState extends State<FlashChat> {
bool isUserLoggedIn;
bool isUserSignedUp;
void getLoggedInStatus() async {
await HelperFunction.getUserLoggedInSharedPreference().then((value) {
isUserLoggedIn = value;
});
}
void getSignedUpStatus() async {
await HelperFunction.getUserSignedUpSharedPreference().then((value) {
isUserSignedUp = value;
});
}
#override
void initState() {
getLoggedInStatus();
getSignedUpStatus();
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: isUserLoggedIn == true
? DashBoard.id: WelcomeScreen.id,
routes: {
WelcomeScreen.id: (context) => WelcomeScreen(),
LoginScreen.id: (context) => LoginScreen(),
RegistrationScreen.id: (context) => RegistrationScreen(),
DashBoard.id: (context) => DashBoard(),
},
debugShowCheckedModeBanner: false,
);
});
});
when the user gets login set
prefs.setBool("isLogin", True);
and when the user get a logout in logout function put
pref.clear()
and in splash screen or at starting put this logic
SharedPreferences prefs = await SharedPreferences.getInstance();
var isLogin = prefs.getBool("isLogin");
if (isLogin)
{
//Navigate user to the required screen
}
else{
//navigate user to login screen
}
Have anybody encounter this issue before?
E/flutter (12975): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe.
E/flutter (12975): At this point the state of the widget's element tree is no longer stable.
E/flutter (12975): To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.
Upon googling I found out that it has something to do with context? That is why I passed the context via argument of a function
Below is the code
class CustRegView extends StatefulWidget {
#override
_CustRegViewState createState() => _CustRegViewState();
}
class CustRegView extends StatelessWidget{
final TextEditingController _controller = TextEditingController();
#override
Widget build(BuildContext context) {
final deviceSize = MediaQuery.of(context).size;
return BaseView<CustRegViewModel>(
builder: (context, model, child) => Scaffold(
...<some code>
FlatButton (
onPressed: () async {
var registerSuccess = await model.register( _controller.text);
if (registerSuccess) {
Navigator.pushNamed(context, 'newScreen'); <--- E R R O R H E R E } else {
UIHelper().showErrorButtomSheet(context, model.errorMessage);
}
)
}
CustRegViewModel looks like this
class CustRegViewModel extends BaseViewModel {
final AuthService _authService = locator<AuthService>();
final DialogService _dialogService = locator<DialogService>();
dynamic verifiedUserID ;
Future<bool> register(String phoneNo) async {
verifiedUserID = await verifyPhone(updatedPhoneNo);
return verifiedUserID != null ? true : false; // From here it
// returns true
}
Future<void> verifyPhone(phoneNo) async {
var completer = Completer<dynamic>();
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: updatedPhoneNo,
timeout: Duration(seconds: 50),
verificationCompleted: (AuthCredential authCred) async {...... <some code>
verificationFailed: (AuthException authException) {...... <some code>
codeSent: (String verID, [int forceCodeResend]) async {...... <some code>
codeAutoRetrievalTimeout: (String verID) {...
).catchError((error) {...... <some code>
return completer.future;
}
}
main class looks like this
void main() {
setupLocator();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final GlobalKey<NavigatorState> navigatorKey =
new GlobalKey<NavigatorState>();
#override
Widget build(BuildContext context) {
return StreamProvider<User>(
initialData: User.initial(),
create: (BuildContext context) => locator<AuthService>().user,
child: MaterialApp(
builder: (context, widget) => Navigator(
onGenerateRoute: (settings) => MaterialPageRoute(
builder: (context) => DialogManager(
child: widget,
),
),
),
title: 'Fitness Diet',
theme: ThemeData(),
initialRoute: 'splash',
navigatorKey: navigatorKey,
onGenerateRoute: Router.generateRoute,
),
);
}
}
I want to show snackbar when in app notification arrive.
But when I configure firebase on first screen, snackbar shows only when user is on that screen.
I try to create a class to get BuildContext and show snackbar based on it but doesn't work and not show snackbar.
This is my HomeScreen.dart:
class _HomeScreenState extends State<HomeScreen> {
#override
void initState() {
super.initState();
Future.delayed(Duration.zero, () {
NotificationManger.init(context: context);
Fcm.initConfigure();
});
}
#override
Widget build(BuildContext context) {
return StoreConnector<AppState, Store<AppState>>(
converter: (store) => store,
onInit: (store) => initApp(store),
builder: (context, store) {
return BlocProvider<HomeBloc>(
create: (context) {
return HomeBloc(homeRepository: homeRepository)..add(ScreenOpened());
},
child: BlocListener<HomeBloc, HomeState>(
listener: (context, state) async {},
child: BlocBuilder<HomeBloc, HomeState>(
builder: (context, state) {
return Scaffold(
key: _scaffoldKey,
...
);
},
),
),
);
},
);
}
}
This is my Fcm.dart
Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) {
if (message.containsKey('data')) {
final dynamic data = message['data'];
}
if (message.containsKey('notification')) {
final dynamic notification = message['notification'];
}
}
class Fcm {
static final FirebaseRepository repository = FirebaseRepository();
static final FirebaseMessaging _fcm = FirebaseMessaging();
static initConfigure() {
if (Platform.isIOS) _iosPermission();
_fcm.requestNotificationPermissions();
_fcm.autoInitEnabled();
_fcm.configure(
onMessage: (Map<String, dynamic> message) async => NotificationManger.onMessage(message),
onLaunch: (Map<String, dynamic> message) async => NotificationManger.onLaunch(message),
onResume: (Map<String, dynamic> message) async => NotificationManger.onResume(message),
onBackgroundMessage: myBackgroundMessageHandler,
);
_fcm.getToken().then((String token) {
print('token: $token');
repository.setUserNotifToken(token);
});
}
static _iosPermission() {
_fcm.requestNotificationPermissions(IosNotificationSettings(sound: true, badge: true, alert: true));
_fcm.onIosSettingsRegistered.listen((IosNotificationSettings settings) {
print("Settings registered: $settings");
});
}
}
and this is my NotificationManager.dart:
class NotificationManger {
static BuildContext _context;
static init({#required BuildContext context}) {
_context = context;
}
static onMessage(Map<String, dynamic> message) {
print(message);
_showSnackbar(data: message);
}
static onLaunch(Map<String, dynamic> message) {
print(message);
}
static onResume(Map<String, dynamic> message) {
print(message);
}
static _showSnackbar({#required Map<String, dynamic> data}) {
// showDialog(context: _context, builder: (_) => );
SnackBar snackBar = SnackBar(
content: Text(
data['data']['title'],
style: TextStyle(
fontFamily: 'Vazir',
fontSize: 16.0,
),
),
backgroundColor: ColorPalette.primary,
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(45.0),
),
elevation: 3.0,
);
Scaffold.of(_context).showSnackBar(snackBar);
}
}
main.dart
class App extends StatelessWidget {
final Store<AppState> store;
App(this.store);
#override
Widget build(BuildContext context) {
return StoreProvider(
store: store,
child: MaterialApp(
...
),
);
}
}
I am using redux and bloc, so any approach with these tools is ok for me.
This is my sample screen:
class Reminders extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: appBar,
body: Center(
child: Text('reminders'),
),
);
}
}
SOLUTION:
Add NotificationManger.init(globalKey: _scaffoldKey); to all screens solve the problem.
class Reminders extends StatelessWidget {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
NotificationManger.init(globalKey: _scaffoldKey);
return Scaffold(
key: _scaffoldKey,
appBar: appBar,
body: Center(
child: Text('reminders'),
),
);
}
}
SOLUTION 2
UsingGet library to using only one function and no need to add it in all screen: https://pub.dev/packages/get
The problem is with registering scaffolds for your NotificationManager widget since every time a new scaffold is added to the stack for a new screen, you need to register that screen's scaffold in the NotificationManager. This is because the line:
Scaffold.of(_context).showSnackBar(snackBar);
in your NoticicationManager will only look up the widget tree until the first scaffold it finds and call it there. Since you call NotificationManger.init(context: context); in your HomeScreen widget and pass the context of the HomeScreen, it will only live inside that scaffold. So, if you navigate away from the HomeScreen to a new widget with a different scaffold it will not have the NotificationManager as a child.
To fix the issue be sure you call Fcm.initConfigure(); in the first page that loads for the app, and for any pages you navigate to call NotificationManger.init(context: context); in either the initState() method for stateful widgets to register the current scaffold of that page or if they are stateless widgets, you can add it in the build method before returning the scaffold.