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

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.

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.

What Does WidgetsFlutterBinding.ensureInitialized() do?

I am trying to use the Firebase package with the below line of code.
I really want to know what this line of code actually does?
The official documentation didn't help me much. Can someone explain me, please?
You have to use it, in this way:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
https://flutter.dev/docs/resources/architectural-overview#architectural-layers
The above image is the architecture layers of Flutter, the WidgetFlutterBinding is used to interact with the Flutter engine. Firebase.initializeApp() needs to call native code to initialize Firebase, and since the plugin needs to use platform channels to call the native code, which is done asynchronously therefore you have to call ensureInitialized() to make sure that you have an instance of the WidgetsBinding.
From the docs:
Returns an instance of the WidgetsBinding, creating and initializing it if necessary. If one is created, it will be a WidgetsFlutterBinding. If one was previously initialized, then it will at least implement WidgetsBinding.
You only need to call this method if you need the binding to be initialized before calling runApp.
From the source code:
#override
Future<FirebaseAppPlatform> initializeApp(
{String name, FirebaseOptions options}) async {
if (name == defaultFirebaseAppName) {
throw noDefaultAppInitialization();
}
// Ensure that core has been initialized on the first usage of
// initializeApp
if (!isCoreInitialized) {
await _initializeCore();
}
// If no name is provided, attempt to get the default Firebase app instance.
// If no instance is available, the user has not set up Firebase correctly for
// their platform.
if (name == null) {
MethodChannelFirebaseApp defaultApp =
appInstances[defaultFirebaseAppName];
if (defaultApp == null) {
throw coreNotInitialized();
}
return appInstances[defaultFirebaseAppName];
}
assert(options != null,
"FirebaseOptions cannot be null when creating a secondary Firebase app.");
// Check whether the app has already been initialized
if (appInstances.containsKey(name)) {
throw duplicateApp(name);
}
_initializeFirebaseAppFromMap(await channel.invokeMapMethod(
'Firebase#initializeApp',
<String, dynamic>{'appName': name, 'options': options.asMap},
));
return appInstances[name];
}
The invokeMapMethod will invoke a method on the above channel with the specified arguments, which will then call the initializeApp() method in the native code,
https://github.com/FirebaseExtended/flutterfire/blob/master/packages/firebase_core/firebase_core/android/src/main/java/io/flutter/plugins/firebase/core/FlutterFirebaseCorePlugin.java#L227
There are also different ways to initialize Firebase, which you can check here:
No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp() in Flutter and Firebase
In the other ways we do not call WidgetsFlutterBinding.ensureInitialized() since the runApp() function calls it internally:
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
https://github.com/flutter/flutter/blob/bbfbf1770c/packages/flutter/lib/src/widgets/binding.dart#L1012
A simple answer is that if Flutter needs to call native code before calling runApp
WidgetsFlutterBinding.ensureInitialized();
makes sure that you have an instance of the WidgetsBinding, which is required to use platform channels to call the native code.
You only need to call this method if you need the binding to be
initialized before calling runApp.
A simple answer, you need to use this line, if your main function uses async keyword because you use await statement inside it.
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences prefs = await SharedPreferences.getInstance(); // just an example
}

Getting firebase data in a stream

I built an application, which gets data from the firebase (realtime db). I did it whith this code, but I want, that I always get the new data. In the internet I found something like in a stream, but I didn't find a manual for that.
Does somebody know how this works?
This is my code:
void readData() {
FirebaseDatabase.instance.reference().child('CHECK').once().then(
(DataSnapshot dataSnapShot) {
print(dataSnapShot.value);
},
);
}
I want to get the data for example every 0.5 seconds
That's not really how Firebase works. But if you want to get the data from the database once right away, and then whenever it is updated, you can use onValue for that.
That'd look something like:
FirebaseDatabase.instance.reference().child('CHECK').onValue.listen((event) {
print(event.snapshot.value);
});
Give it a try: just set up the listener with this code, run the app, and then make a change to the database in the Firebase console. You'll see the data be printed once as soon as you run the app, and then again whenever you make a change.
From what I've read in your comments, you want the function to be executed repeatedly every 0.5 seconds.
A stream is not appropriate for that. However, you can use Timer
#override
void initState() {
super.initState();
timer = Timer.periodic(Duration(seconds: 15), (Timer t) => readData());
}
#override
void dispose() {
timer?.cancel();
super.dispose();
}
Your build() function will be called more than once once Timer.periodic is created.

Resources