Hot reload make my app logout Flutter Firebase - firebase

I am working on an e-commerce app. I made all its authentication and storing using Firebase. To enter the app I have to make and account and sign in. While signing in, when I make any change in my code and try to save it using CTRL+s or hot reload the app sign out and I get back to the login screen again!!
I don't get the problem at all !! I don't even know which code should I add with my question !!

Verify if you create de initialize var inside build in main, the correct is create outside build like this:
class myApp extends StatelessWidget {
final Future<FirebaseApp> _init = Firebase.initializeApp();
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _init,
...
)
}

Related

Flutter firebase receiving notification when closed or in background - how to pass message to class

I'm building a Flutter app with Firebase push notifications.
When a message is received I want the app to show a popup modal with the text.
When the app is in the foreground the popup modal displays - this works
When the app is the background and the message is received by the mobile it appears in the system tray, the user clicks on it, the app opens and the initial message is found and displayed to the user in the popup modal - eg. FirebaseMessaging.onMessageOpenedApp function - this works.
When the app is in the background, the notification is received by the phone (and the firebase listener is working because it outputs the message data using debugPrint to test), it appears in the system tray, but the user chooses NOT to click the message - when the app is brought back to the foreground the message is ignored - This is a problem.
The "FirebaseMessaging.onBackgroundMessage" function needs to be placed in the TOP LEVEL (outside of any class). Therefore when the app is once again placed in the foreground, how do I push message data from a message that may have been received whilst the app is in the background, in to my App Class to display the message content? I'm using "AppLifecycleState" to detect when the app is returned to the foreground, but I can't grab the message data because it is received in the top level, not in the class.
Please see my code below (see last few lines for the bit I'm stuck on)...
//TOP LEVEL-----
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
if (message.messageId!="") {
debugPrint("Have received a background message! Will have to grab the message from here somehow if the user didn't interact with the system tray message link");
}
}
Future<void> main() async {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
//APP CLASS-----
class MyAppextends StatefulWidget {
State<MyApp> createState() => _MyAppState();
}
//APP STATE CLASS
class _MyAppState extends State<MyApp> with WidgetsBindingObserver{
#override
void initState() {
super.initState();
_initiateNotificationForegroundListener();
_initiateInteractedMessage();
}
// This variable will tell you whether the application is in foreground or not.
bool _isInForeground = true;
//Initiate Foreground Notification Listener (works)
void _initiateNotificationForegroundListener() {
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
_handleNotificationInstruction(message);
});
}
//Initiate Background/Closed Notification Listener if user clicks the message in the system try (works)
Future<void> _initiateInteractedMessage() async {
RemoteMessage? message = await FirebaseMessaging.instance.getInitialMessage();
if (message != null) {
_handleNotificationInstruction(message);
}
// When app is in background (Stream listener)
FirebaseMessaging.onMessageOpenedApp
.listen(_handleNotificationInstruction);
}
void _handleNotificationInstruction(RemoteMessage message) {
//Create popup to display message info (works)
}
//Detect when an app moves in to the foreground
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
_isInForeground = state == AppLifecycleState.resumed;
if(_isInForeground){
/** HELP!!!
/* How can I check what message might have been received while app was in the background?? ie. the top-level _firebaseMessagingBackgroundHandler function??
**/
}
}
I was facing this problem too, this is annoying but thanks to this thread, I found the solution. The FCM's document states:
When received, an isolate is spawned (Android only, iOS/macOS does not
require a separate isolate) allowing you to handle messages even when
your application is not running.
An important note is that each isolate has its own memory so that your shared preferences in foreground will be different from it in background. You can "synchronize" the data by calling SharedPreference.reload() when your app resumes.
Save a message to a persistent storage and when the application starts check if the storage has pending messages.

Flutter - Is it possible to show the number of users online?

I want to show how many people are using my mobile application instantly in my application. For example: "342 people are currently using the application." or "342 people are online right now." I could not find a solution for this.
I store users data with Firebase. So what I want to do is possible by extracting data from the firebase?
You're simplest and most cost effective way, is to create a document, put in a collection for example called .collection(general), when a user logsIn, add 1 to that value, when they logout, subtract 1, and put this in a stream builder.
After success login, run the following function
await FirebaseFirestore.instance
.collection('general')
.doc('onlineCount)
.update({'membersOnline': FieldValue.increment(1)})//this will increase the number by 1.
);
On logout, substract 1.
this is very easy to handle this logic just save the status when users open your app for eg: on homepage and when they kill your app just update that collection to that particular is offline and at the and do query
where(user:online)
and check the number of users you got and simply show that number.
I hope you got this logic.
A little late to the party. But I would personally recommend making use of the App Lifecycle. Meaning:
detached: The application is still hosted on a flutter engine but is detached from any host views.
inactive: The application is in an inactive state and is not receiving user input. For example during a phone call.
paused: The application is not currently visible to the user and running in the background. This is when you press the Home button.
resumed: The application is visible and responding to user input. In this state, the application is in the foreground.
So you will have to create a StatefulWidget and WidgetsBindingObserver:
import 'package:flutter/material.dart';
class LifeCycleManager extends StatefulWidget {
LifeCycleManager({Key key, #required this.child}) : super(key: key);
final Widget child;
#override
_LifeCycleManagerState createState() => _LifeCycleManagerState();
}
class _LifeCycleManagerState extends State<LifeCycleManager> with WidgetsBindingObserver {
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
print('AppLifecycleState: $state');
}
#override
Widget build(BuildContext context) {
return widget.child;
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
}
And then just check the states as follows:
AppLifecycleState _appLifecycleState;
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
setState(() {
_appLifecycleState = state;
});
if(state == AppLifecycleState.paused) {
print('AppLifecycleState state: Paused audio playback');
//update user file eg. online_status: offline
}
if(state == AppLifecycleState.resumed) {
print('AppLifecycleState state: Resumed audio playback');
//update user file eg. online_status: online
}
print('AppLifecycleState state: $state');
}

accessing data from a static function globally

in the init() of my splash screen page, i am calling the function of the next page in order to load the data from backend, and meanwhile the splash screen will run.
the issue here is it only calls the static function, and that function stores the data locally.
on my other page, i want data globally, so that i can access that data anywhere on that particular page.
highlights of my code is:
splash screen page init code:
void initState() {
super.initState();
FeedScreen.getdata();
}
and my next page, that is FeedScreen page, where i want data globally is:
class FeedScreen extends StatefulWidget {
#override
_FeedScreenState createState() => _FeedScreenState();
static void getdata() async{
CollectionReference collectionReference = FirebaseFirestore.instance
.collection('Feed');
var snapshot = await collectionReference.get();
snapshot.docs.forEach((result){
collectionReference.doc(result.id).collection('myfeed').snapshots().listen((event) {
var latarr,longarr,titlearr,descarr,urlarr;
for(int i=0;i<event.docs.length;i++){
urlarr.add(event.docs[i].data()['imageurl']);
latarr.add(event.docs[i].data()['lat']);
longarr.add(event.docs[i].data()['long']);
titlearr.add(event.docs[i].data()['title']);
descarr.add(event.docs[i].data()['description']);
}
});
});
}
i want to access the value of latarr,longarr,titlearr,descarr,urlarr outside the getdata() function.
Declare your variables latarr,longarr,titlearr,descarr,urlarr outside any class. For instance in your main.dart file before the void main() function. These variables will be considered as global variables and will be accessible anywhere in your app.
The best and clean approach to do this is that you use State Management. With that, you will be able to manage your variables, etc in your code smoothly and you can access those variables anywhere in your program whenever needed. Some popular ones are Provider
, Bloc and GetX.
By using state management you can easily able to Manipulate and access data anywhere in your project.

Flutter check Firebase is initialised or not and what happens if initialised multiple times?

Scenario: I have three screen of an app that I launch based on condition. One screen is buttons with other two screen options.
It works fine on a screen where I am initialising the firebase and doing fetching and all the stuff fine. void startFirebase() async { await Firebase.initializeApp(); } . However on a second screen I am doing initialisation same way but I am encountered with this error:
No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp().
Question: How to check if it is initialised (to check if initialised on first screen and wont reinitialise on second one) and - what will happen if I initialise Firebase on both the screens or if initialised twice?
update based on first provided answer:
I am not sure but calling initilise twice does not show any error. Heres how I'm trying twice:
#override
void initState() {
startFirebase();
try{
startFirebase();
}catch(e){
print(e.toString());
}
super.initState();
}
//another way:
#override
void initState() {
startFirebase();
startFirebase();
/* try{
startFirebase();
}catch(e){
print(e.toString());
}*/
super.initState();
}
No error on run tab and app works fine.
Firebase init will fail with a different message if you attempt to do it a second time.
FirebaseApp name [DEFAULT] already exists
You can check if it's already initialized as described in this other question.
Unless you have specific needs, you should instead consider instead initializing Firebase just once globally for your main app object when it first launches, and don't worry about it again after that.

How do I track Flutter screens in Firebase analytics?

I have a Flutter app and I'm testing Google Analytics for Firebase on Flutter.
I wanted to see the routes our users (well, me for now) are visiting. I followed the setup steps in firebase_analytics and I checked their example app, too. I enabled debugging for Analytics as described in the Debug View docs
Unfortunately, the only two kinds of screen views (firebase_screen_class) I receive in my Analytics Debug view are Flutter and MainActivity.
I'd expect to see /example-1, /example-2 and /welcome somewhere, but I don't.
This is the app I'm running in Flutter
class App extends StatelessWidget {
final FirebaseAnalytics analytics = FirebaseAnalytics();
#override
Widget build(BuildContext context) {
return MaterialApp(
routes: <String, WidgetBuilder>{
'/example-1': (_) => Example1(),
'/example-2': (_) => Example2(),
'/welcome': (_) => Welcome(),
},
home: Welcome(),
navigatorObservers: [FirebaseAnalyticsObserver(analytics: analytics)],
);
}
}
This exact use-case is in the documentation for Firebase Analytics under the Track Screenviews section.
Manually tracking screens is useful if your app does not use a separate UIViewController or Activity for each screen you may wish to track, such as in a game.
This is exactly the case with Flutter, as Flutter is taking care of the screen updates: most simple Flutter apps run one single FlutterActivity/FlutterAppDelegate and it takes care of rendering different screens on its own, so letting Firebase Analytics automatically track screens will not bring the desired effect.
As far as my past experience goes, the FirebaseAnalyticsObserver was not very helpful, however, I recommend you, too, check their docs again, they do imply that things should "just work". My best guess is that it didn't work well for me because I didn't use RouteSettings on any of my routes *.
In case FirebaseAnalyticsObserver won't work or apply for your app, the next approach worked quite well for me over the past months of development.
You can set the current screen with FirebaseAnalytics at any point, if you call the setCurrentScreen method with the screen name:
import 'package:firebase_analytics/firebase_analytics.dart';
// Somewhere in your widgets...
FirebaseAnalytics().setCurrentScreen(screenName: 'Example1');
As a first attempt I did this in the widget constructor, but that will not work well and miscount the events: if you pop or push routes, all widget constructors in the stack will be called, even though only the top route really qualifies as "the current screen".
To solve this, we need to use the RouteAware class and only set the current screen in case it's the top route: either our route is added to the stack or the previous top route was popped and we arrived onto the route.
RouteAware comes with boilerplate code and we don't want to repeat that boilerplate for all of our screens. Even for small apps, you have tens of different screens, so I created the RouteAwareAnalytics mixin:
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:flutter/widgets.dart';
// A Navigator observer that notifies RouteAwares of changes to state of their Route
final routeObserver = RouteObserver<PageRoute>();
mixin RouteAwareAnalytics<T extends StatefulWidget> on State<T>
implements RouteAware {
AnalyticsRoute get route;
#override
void didChangeDependencies() {
routeObserver.subscribe(this, ModalRoute.of(context));
super.didChangeDependencies();
}
#override
void dispose() {
routeObserver.unsubscribe(this);
super.dispose();
}
#override
void didPop() {}
#override
void didPopNext() {
// Called when the top route has been popped off,
// and the current route shows up.
_setCurrentScreen(route);
}
#override
void didPush() {
// Called when the current route has been pushed.
_setCurrentScreen(route);
}
#override
void didPushNext() {}
Future<void> _setCurrentScreen(AnalyticsRoute analyticsRoute) {
print('Setting current screen to $analyticsRoute');
return FirebaseAnalytics().setCurrentScreen(
screenName: screenName(analyticsRoute),
screenClassOverride: screenClass(analyticsRoute),
);
}
}
I created an enum to track the screens (and functions to turn the enum to screen names). I used the enums to be able to easily track all routes, refactor route names. Using these enums and functions, I can unit test all possible values and enforce consistent naming: no accidental spaces or special characters, no inconsistent capitalization. There could be other, better ways to determine screen class values, but I went with this approach.
enum AnalyticsRoute { example }
String screenClass(AnalyticsRoute route) {
switch (route) {
case AnalyticsRoute.example:
return 'ExampleRoute';
}
throw ArgumentError.notNull('route');
}
String screenName(AnalyticsRoute route) {
switch (route) {
case AnalyticsRoute.example:
return '/example';
}
throw ArgumentError.notNull('route');
}
Next step in the inital setup is to register the routeObserver as a navigatorObserver of your MaterialApp:
MaterialApp(
// ...
navigatorObservers: [
routeObserver,
// FirebaseAnalyticsObserver(analytics: FirebaseAnalytics()),
],
);
Finally, we can add our first example route that's tracked. Add the with RouteAwareAnalytics to your states and override get route.
class ExampleRoute extends StatefulWidget {
#override
_ExampleRouteState createState() => _ExampleRouteState();
}
class _ExampleRouteState extends State<ExampleRoute> with RouteAwareAnalytics{
#override
Widget build(BuildContext context) => Text('Example');
#override
AnalyticsRoute get route => AnalyticsRoute.example;
}
Every time you add a new route, you can do so with little effort: first, add a new enum value, then the Dart compiler will guide you what to add next: add the screen name and class override values in their respective switch-case. Then, find your state that's building your route, add with RouteAwareAnalytics, and add the route getter.
* The reason why I didn't use RouteSettings is that I prefer Simon Lightfoot's approach with the typed arguments instead of the Object arguments the settings provide:
class ExampleRoute extends StatefulWidget {
const ExampleRoute._({#required this.integer, Key key}) : super(key: key);
// All types of members are supported, but I used int as example
final int integer;
static Route<void> route({#required int integer}) =>
MaterialPageRoute(
// I could add the settings here, though, it wouldn't enforce good types
builder: (_) => ExampleRoute._(integer: integer),
);
// ...
}
Add a Navigation Observer
Add Firebase analytics navigation observer to your MatetialApp:
class MyApp extends StatelessWidget {
FirebaseAnalytics analytics = FirebaseAnalytics();
...
MaterialApp(
home: MyAppHome(),
navigatorObservers: [
FirebaseAnalyticsObserver(analytics: analytics), // <-- here
],
);
That's it! Your analytics should appear in the DebugView:
NOTE!
If it's the first time that you are integrating analytics in your app, it will take about a day for your analytics to appear in your dashboard.
See results right away
To see debug results right away, run the above command on your terminal, then check that they appear in the DebugView:
adb shell setprop debug.firebase.analytics.app [your_app_package_name]
Enjoy!
I experienced the issue for some time and was just able to make it work
The issue for me is that I'm not properly passing settings in MaterialPageRoute
return MaterialPageRoute(
settings: RouteSettings(
name: routeName,
),
builder: (_) => viewToShow);
}
I follow the tutorial on FilledStack and was able to figure out my issue after seeing the sample code
If you are seeing "Flutter" in the firebase_screen_class parameter of the screen_view
event, it means you have it configured properly.
You should find the values you are expecting in the firebase_screen parameter, instead of the firebase_screen_class.
It's also worth checking the firebase_previous_screen parameter to see what was the screen that was open before that one.

Resources