Cannot get onMessage to show when App is in foreground - firebase

I am writing an app where you can press a button and it will send a 'Help' notification to every over app (this will be a closed group of around 100) I need the notifiction to pop up whatever state the app is in, I have the onResume and onLaunch sorted, but I cannot get it to display the notification when the app is in the foreground. There are many screens, I need the notification to show up on whatever screen the user is on.
I have tried many tutorials, but just cannot get it to work. I have the code in my initState on my Main page.
void main() {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
#override
void initState() {
super.initState();
var initializationSettingsAndroid =
new AndroidInitializationSettings('#mipmap/ic_launcher');
var initializationSettingsIOS = IOSInitializationSettings();
var initializationSettings = InitializationSettings(
initializationSettingsAndroid, initializationSettingsIOS);
flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: onSelectNotification);
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
print("onMessage: $message");
showDialog(
context: context,
builder: (context) =>
AlertDialog(
content: ListTile(
title: Text(message['notification']['title']),
subtitle: Text(message['notification']['body']),
),
actions: <Widget>[
FlatButton(
child: Text('Ok'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
);
},
onLaunch: (Map<String, dynamic> message) async {
print("onLaunch: $message");
showDialog(
context: context,
builder: (context) =>
AlertDialog(
content: ListTile(
title: Text(message['notification']['title']),
subtitle: Text(message['notification']['body']),
),
actions: <Widget>[
FlatButton(
child: Text('Ok'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
);
},
onResume: (Map<String, dynamic> message) async {
print("onResume: $message");
showDialog(
context: context,
builder: (context) =>
AlertDialog(
content: ListTile(
title: Text(message['notification']['title']),
subtitle: Text(message['notification']['body']),
),
actions: <Widget>[
FlatButton(
child: Text('Ok'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
);
},
);
Future onSelectNotification(String payload) async {
if (payload != null) {
debugPrint('notification payload: ' + payload);
var androidPlatformChannelSpecifics = AndroidNotificationDetails(
'your channel id', 'your channel name', 'your channel description',
importance: Importance.Max, priority: Priority.High,ticker:'ticker');
var iOSPlatformChannelSpecifics = IOSNotificationDetails();
var platformChannelSpecifics = NotificationDetails(
androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
await flutterLocalNotificationsPlugin.show(
0, 'plain title', 'plain body', platformChannelSpecifics,
payload: 'item x');
}
}
// #override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Benidorm or Bust',
theme: ThemeData.dark().copyWith(
primaryColor: Color(0xFF0A0E21),
scaffoldBackgroundColor: Color(0xFF0A0E21),
),
initialRoute: '/',
routes: {
'/': (context) => MyHomePage(),
'/setup': (context) => Setup(),
'/dayinit': (context) => Dayinit(),
'/day1': (context) => Day1(),
'/day2': (context) => Day2(),
'/day3': (context) => Day3(),
'/day4': (context) => Day4(),
'/day5': (context) => Day5(),
'/CheckList': (context) => CheckList(),
'/eveinit': (context) => Eveinit(),
'eve1eve': (context) => Eve1Eve(),
// 'push':(context)=> Push(),
},
);
}
}
I am not getting any error, just nothing happens.

I faced the same issue, just move Firebase related code from "app" MyApp to MyHomePage widget. Inside MyHomePage it will have proper context.
I've used this as an example https://github.com/FirebaseExtended/flutterfire/blob/master/packages/firebase_messaging/example/lib/main.dart#L240

Related

Flutter streamprovider issue with userdata from Firebase

I've been stuck in this issue for sometime now and after lots of tries, now i don't know where the issue is.
The problem is i'm using StreamProvider to get userData from firebase and then use that userData through provider throughout my project, Now everything works fine but the issue arises when i try to log out. Actually even logout works perfectly, untill i close the app using the backbutton of my phone and then reopen the app from open apps list, then when i try to logout it doesn't work correctly. When i refresh the app the user is logged out but initially it doesn't navigate to the login screen.
here is the logout code.
InkWell(
onTap: () async {
await showDialog(
context: context,
useRootNavigator: false,
builder: (ctx) => AlertDialog(
title: const Text("Are you Sure?"),
content: const Text("Do You want to logout?"),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text(
"No",
style: TextStyle(
color: ColorConstant.appColor,
),
),
),
TextButton(
onPressed: () async {
Navigator.of(context).pop(true);
await AuthService().signOut();
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: ((context) =>
const LoginScreen())),
(route) => false);
},
child: const Text(
"Yes",
style: TextStyle(
color: ColorConstant.appColor,
),
),
),
],
),
);
},
And here is the main.dart i've defined StreamProvider.
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
final _auth = AuthService();
final String? _userId = _auth.user?.uid;
return MultiProvider(
providers: [
StreamProvider<List<Packages>>(
create: (_) => DatabaseService().packages, initialData: const []),
StreamProvider<UserModel?>(
create: (_) => DatabaseService().userData(_userId),
initialData: null,
),
],
builder: (context, child) {
return MaterialApp(
title: 'DreamaX',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: ColorConstant.appColor,
errorColor: Colors.red,
),
home: const Wrapper(),
);
},
);
}
}
Also one another thing that when i'm logging in, i navigate to MyApp so that the StreamProvider can get the userId and initialize the streamprovider.
here is the login code
try {
await _authService.signIn(
emailCont: _emailCont.text.trim(),
passCont: _passCont.text.trim());
Navigator.of(context)
.pushAndRemoveUntil(
MaterialPageRoute(
builder: ((context) =>
const MyApp())),
(route) => false);
} on FirebaseAuthException catch (e) {
So i'm really confused now what could be the cause here?

User is loged/signed in but the screen does not change

I am working with Firebase authentication and lately was not able to do anything because of the following error. When I enter the user's credentials, it gets authenticated on the Firebase console, but my app's screen does not change. What's more, the following error appears in debug console.
2 I/System.out(26261): (HTTPLog)-Static: isSBSettingEnabled false
I/SurfaceControl(26261): nativeRelease nativeObject
s[-5476376676702711408] I/SurfaceControl(26261): nativeRelease
nativeObject e[-5476376676702711408] W/System (26261): Ignoring
header X-Firebase-Locale because its value was null. 2
I/System.out(26261): (HTTPLog)-Static: isSBSettingEnabled false
void authUser(
String email, String password, String conPassword, bool isLogin) async {
UserCredential authResult;
setState(() {
isLoading = true;
});
FocusScope.of(context).unfocus();
try {
if (password != conPassword && isLogin == false) {
showDialog(
context: context,
builder: (_) => const AlertDialog(
title: Text('Passwords don\'t match'),
content: Text('Please check if your passwords match'),
),
);
}
if (isLogin == false) {
authResult = await firebaseAuth.createUserWithEmailAndPassword(
email: email,
password: password,
);
} else {
authResult = await firebaseAuth.signInWithEmailAndPassword(
email: email,
password: password,
);
}
setState(() {
isLoading = false;
});
} catch (e) {
rethrow;
}
}
...
TextFormField(
key: const ValueKey('email'),
keyboardType: TextInputType.emailAddress,
decoration: const InputDecoration(
label: Text('Email adress'),
),
onSaved: (newValue) {
email = newValue!;
FocusScope.of(context).requestFocus(_passwordNode);
},
),
TextFormField(
key: const ValueKey('password'),
focusNode: _passwordNode,
obscureText: true,
onSaved: (newValue) {
password = newValue!;
FocusScope.of(context).requestFocus(_conPasswordNode);
},
decoration: const InputDecoration(
label: Text('Password'),
),
),
if (isLogin == false)
TextFormField(
key: const ValueKey('conPassword'),
focusNode: _conPasswordNode,
obscureText: true,
decoration: const InputDecoration(
label: Text('Confirm password'),
),
onSaved: (newValue) {
conPassword = newValue!;
},
),
Expanded(
child: Container(),
),
widget.isLoading == true
? const Center(
child: CircularProgressIndicator(),
)
: ButtonBar(
alignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
setState(() {
isLogin = !isLogin;
});
},
child: Text(isLogin == true ? 'SignUp' : 'Login'),
),
ElevatedButton(
onPressed: _trySubmit,
child: const Text('Done'),
),
],
),
...
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: Firebase.initializeApp(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (ctx) => RecipeProvider(),
),
],
child: MaterialApp ...
home: StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
if (snapshot.hasData) {
return AllRecipesScreen();
}
return AuthScreen();
}),
routes: {
AllRecipesScreen.routeName: (ctx) => AllRecipesScreen(),
RecipeDetailScreen.routeName: (ctx) => RecipeDetailScreen(),
AddRecipeScreen.routeName: (ctx) => AddRecipeScreen(),
AuthScreen.routeName: (ctx) => AuthScreen(),
},
),
);
});
}
}
try changing the targetedSdk to 27, hope this helps.
The code seems to be quite incomplete to conclude anything out of it.
And as much as I know the logs that you have provided are not errors , it is just that way and are printed every-time. I don't think your main issue seems to be firebase authentication. It may be an issue of navigation(or a wrapper if you're using one).

Flutter Firebase Auth - change home widget if the user is logged in

I'm building an app that supports only google login using google_sign_in package. When the app is run, it first checks if FirebaseAuth is alive. If user in FirebaseAuth.instance.authStateChanges().listen((User? user) is not null, HomePage() should be shown. If the user is null it should go to AuthPage() which has a google sign-in button.
The code for main.dart is shown below.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
Widget _defaultHome = HomePage();
// Check if user already logged in
FirebaseAuth auth = FirebaseAuth.instance;
auth.authStateChanges().listen((User? user) {
if (user == null) {
_defaultHome = AuthPage();
}
print(user);
});
runApp(MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => SignInProvider()),
ChangeNotifierProvider(create: (_) => DataProvider()),
],
child: new MaterialApp(
title: 'Mentea',
home: _defaultHome,
routes: {
'auth': (context) => AuthPage(),
'home': (context) => HomePage(),
},
),
));
}
But when I run it, console gives
I/flutter (14933): null
which means no logged in user, but the emulator shows HomePage() instead of AuthPage(). Anybody know how to fix this? I tried changing 'home:' attribute to 'initialRoute:', but it doesn't work either (directs to HomePage() while printing null).
String _defaultHome = 'home';
...
auth.authStateChanges().listen((User? user) {
if (user == null) {
_defaultHome = 'auth;
}
});
...
child: new MaterialApp(
title: 'Mentea',
initialRoute: _defaultHome,
routes: {
'auth': (context) => AuthPage(),
'home': (context) => HomePage(),
},
),
We can assign a StreamBuilder that listens to FirebaseAuth state changes, to the home property:
StreamBuilder(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (user) {
return user == null ? AuthPage() : HomePage(),
}
),
Follows a full example:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => SignInProvider()),
ChangeNotifierProvider(create: (_) => DataProvider()),
],
child: MaterialApp(
title: 'Mentea',
home: StreamBuilder(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (user) {
return user == null ? AuthPage() : HomePage(),
},
),
routes: {
'auth': (context) => AuthPage(),
'home': (context) => HomePage(),
},
),
));
}

Type 'ReadCitee' is not a subtype of type 'String'

I have a little problem. I try to add the results of my database in a dynamic list but I get the error message "type 'ReadCitee' is not a subtype of type 'String' ". I tried to return an object of type String in my "READCITEE" class but this one only wants to return objects of type widgets. I have been stuck for several days. Thank you for the help in advance.
My class PageVille:
import 'package:ampc93/fonction/firebase_crud/add_citee.dart';
import 'package:ampc93/fonction/firebase_crud/read_citee.dart';
import 'package:ampc93/page_cite.dart';
import 'package:flutter/material.dart';
class PageVille extends StatefulWidget {
final String? titre;
PageVille(this.titre);
#override
_PageVilleState createState() => _PageVilleState();
}
class _PageVilleState extends State<PageVille> {
TextEditingController citeeController = TextEditingController();
var citeesList = [];
#override
void initState() {
super.initState();
citeesList.add(ReadCitee(widget.titre!));
}
Future<void> displayDialog(BuildContext context) async {
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: TextField(
controller: citeeController,
textInputAction: TextInputAction.go,
decoration:
InputDecoration(hintText: "Entrez une nouvelle citée"),
),
actions: [
TextButton(
child: Text("Ok"),
onPressed: () {
AddCitee().addCitee(widget.titre!, citeeController.text);
setState(() {
citeesList.add(citeeController.text);
citeeController.clear();
});
Navigator.pop(context);
}),
TextButton(
onPressed: () {
Navigator.pop(context);
citeeController.clear();
},
child: Text("Annuler"),
)
],
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.titre!),
backgroundColor: Colors.teal,
),
body: Container(
padding: EdgeInsets.all(20.0),
child: ListView.separated(
itemBuilder: (context, index) {
return ListTile(
title: Text(citeesList[index]),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PageCite(citeesList[index]))),
trailing: IconButton(
icon: Icon(Icons.delete_forever),
color: Colors.red[300],
iconSize: 32,
onPressed: () {
setState(() {
citeesList.remove(citeesList[index]);
});
}));
},
separatorBuilder: (context, index) => Divider(),
itemCount: citeesList.length)),
floatingActionButton: FloatingActionButton(
onPressed: () => displayDialog(context),
child: Icon(Icons.add),
backgroundColor: Colors.teal[300],
),
);
}
}
My Class ReadCitee:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class ReadCitee extends StatelessWidget {
final String documentId;
ReadCitee(this.documentId);
#override
Widget build(BuildContext context) {
CollectionReference users = FirebaseFirestore.instance.collection('citee');
return FutureBuilder<DocumentSnapshot>(
future: users.doc(documentId).get(),
builder: (context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return Text("Something ent wrong");
}
if (snapshot.hasData && !snapshot.data!.exists) {
return Text("Documents does not exist");
}
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data =
snapshot.data!.data() as Map<String, dynamic>;
print("Ca marche");
return Text("Full name: ${data['Nom']}");
}
return Text("Loading");
});
}
}
The error:
The following _TypeError was thrown building:
type 'ReadCitee' is not a subtype of type 'String'
When the exception was thrown, this was the stack
#0 _PageVilleState.build.<anonymous closure>
package:ampc93/page_ville.dart:72
#1 new ListView.separated.<anonymous closure>
package:flutter/…/widgets/scroll_view.dart:1277
#2 SliverChildBuilderDelegate.build
package:flutter/…/widgets/sliver.dart:455
#3 SliverMultiBoxAdaptorElement._build
package:flutter/…/widgets/sliver.dart:1201
#4 SliverMultiBoxAdaptorElement.performRebuild.processElement
package:flutter/…/widgets/sliver.dart:1145
...
In the build method of _PageVileeState, there is the following code:
return ListTile(
title: Text(citeesList[index]),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PageCite(citeesList[index]))),
trailing: IconButton(
icon: Icon(Icons.delete_forever),
color: Colors.red[300],
iconSize: 32,
onPressed: () {
setState(() => citeesList.remove(citeesList[index]));
},
),
);
The Text expects a String in its constructor. You're passing a citeesList[index]. A citeesList is a List<ReadCitee>, not a List<String>. Therefore, citeesList[index] is a ReadCitee, not a String, thus the error. I don't know what's the logic behind your code, but to fix it you can replace
title: Text(citeesList[index]),
with the following, if you want a Text with the documentId as content:
title: Text(citeesList[index].documentId),
or the following, if you want the ReadCitee itself as the ListTile's title:
title: citeesList[index],

Why Bottom Tab is still visible when I navigate to another screen?

This is the signin page in the app and my app contains 2 more screens under bottom tab after signing in but when I try to logout and Navigate back to signin screen ,it does navigate but the bottom tab bar still remains in the bottom which is not in the SignIn page.
FlatButton.icon(
onPressed: () => {
_auth.signOutGoogle(),
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SignIn()))
},
icon: Icon(Icons.exit_to_app),
label: Text('Sign Out'))
This is my logout button and below is my main.dart file where I defined all the routes.
void main() {
SystemChrome.setEnabledSystemUIOverlays([]);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
StreamProvider.value(value: AuthService().user),
],
child: Consumer<User>(
builder: (ctx, auth, _) => MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Wrapper(),
routes: {
'/homescreen': (ctx) => HomeScreen(),
'/signin': (ctx) => SignIn(),
'/dashboard': (ctx) => DashBoard(),
'/status': (ctx) => Status()
},
)),
);
}
}
Thank You.
FlatButton.icon(
onPressed: () => {
_auth.signOutGoogle(),
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => SignIn()),
(Route<dynamic> route) => false,);
},
icon: Icon(Icons.exit_to_app),
label: Text('Sign Out'))
if above solution doesn't work check this 2nd solution
FlatButton.icon(
onPressed: () => {
_auth.signOutGoogle(),
Navigator.of(context, rootNavigator: true).pushReplacement(
MaterialPageRoute(builder: (context) => SignIn()));
},
icon: Icon(Icons.exit_to_app),
label: Text('Sign Out'))

Resources