Flutter streamprovider issue with userdata from Firebase - 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?

Related

Google Sign-in with a different account is not working (FlutterFire Package)

When I Sign-in with one of my Google accounts the sign-in works perfectly. but after signing out, when I try to sign in with a different account, instead of popping up the google accounts list the app directly sign-ins to the previous account I signed in to.
This is the Code Snippet for Main.dart
main.dart ->>
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:social_media_app/widget/auth_gate.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: const Size(375, 667),
minTextAdapt: true,
builder: () {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const AuthGate(),
);
},
);
}
}
This is the code snippet of the AuthGate.
auth_gate.dart -->
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutterfire_ui/auth.dart';
class AuthGate extends StatelessWidget {
const AuthGate({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return SignInScreen(
// showAuthActionSwitch: false,
subtitleBuilder: (context, action) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Text(
action == AuthAction.signIn
? 'Welcome to FlutterFire UI! Please sign in to continue.'
: 'Welcome to FlutterFire UI! Please create an account to continue',
),
);
},
footerBuilder: (context, _) {
return const Padding(
padding: EdgeInsets.only(top: 16),
child: Text(
'By signing in, you agree to our terms and conditions.',
style: TextStyle(color: Colors.grey),
),
);
},
sideBuilder: (context, constraints) {
return Padding(
padding: const EdgeInsets.all(20),
child: AspectRatio(
aspectRatio: 1,
child: Image.network(
'https://firebase.flutter.dev/img/flutterfire_300x.png'),
),
);
},
headerBuilder: (context, constraints, _) {
return Padding(
padding: const EdgeInsets.all(20),
child: AspectRatio(
aspectRatio: 1,
child: Image.network(
'https://firebase.flutter.dev/img/flutterfire_300x.png',
),
),
);
},
providerConfigs: const [
EmailProviderConfiguration(),
PhoneProviderConfiguration(),
GoogleProviderConfiguration(
clientId: '1:778935804680:android:d2da33128b67cdc68c42bd',
),
FacebookProviderConfiguration(
clientId: '...',
),
],
);
}
return const ProfileScreen(
providerConfigs: [
EmailProviderConfiguration(),
GoogleProviderConfiguration(
clientId: '1:778935804680:android:d2da33128b67cdc68c42bd',
),
FacebookProviderConfiguration(
clientId: '...',
),
],
avatarSize: 200,
);
},
);
}
}
I take it you did first sign-out of google in any open browsers right? I believe the idea is to speed up login by using often already established Google logins. So if you have more, log off in other windows.
You should add an extra line of code where you explicitly diconnect from googleSignIn.
final googleSignIn = GoogleSignIn();
.
.
.
Future logout() async {
await googleSignIn.disconnect();
FirebaseAuth.instance.signOut();
}
Also, be sure to add the google_sign_in package :)

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(),
},
),
));
}

The getter 'uid' was called on null - but only half the time

So I have this screen in my Flutter app that is supposed to show all the notes for a particular user. I have the Firestore structured so that there is a collection of notes and each user has one document named their uid. Then all their notes are store in a collection (usernotes) under their document.
The problem I am having here is that when you try to access the notes page in the app, you get the error
The getter 'uid' was called on null.
Receiver: null
Tried calling: uid
But when I simply click run from the Flutter app, everything works just fine. You can see all the notes on the screen. Here is my note screen.
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'welcome_screen.dart';
class NoteScreen extends StatefulWidget {
static const String id = 'note_screen';
#override
_NoteScreenState createState() => _NoteScreenState();
}
class _NoteScreenState extends State<NoteScreen> {
final _auth = FirebaseAuth.instance;
User loggedInUser;
#override
void initState() {
super.initState();
getCurrentUser();
}
void getCurrentUser() async {
try {
final user = await _auth.currentUser;
if (user != null) {
loggedInUser = user;
}
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Field Notes'),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.chat),
tooltip: 'Messages',
onPressed: () {},
),
IconButton(
icon: const Icon(Icons.exit_to_app),
tooltip: 'Log Out',
onPressed: () {
_auth.signOut();
Navigator.pushNamed(context, WelcomeScreen.id);
},
),
],
),
body: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('notes').doc(loggedInUser.uid).collection('usernotes')
.snapshots(),
builder: (ctx, streamSnapShot) {
if(!streamSnapShot.hasData) return const Text('Loading...');
if (streamSnapShot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
final noteData = streamSnapShot.data.docs;
return ListView.builder(
itemCount: noteData.length,
itemBuilder: (ctx, index) => Container(
padding: EdgeInsets.all(8),
child: Text(noteData[index]['text']),
),
);
},
),
floatingActionButton:
FloatingActionButton(child: Icon(Icons.add), onPressed: () {
FirebaseFirestore.instance.collection('notes').doc(loggedInUser.uid).collection('usernotes').add({
'text' : 'This was added by clicking the button!'
});
}),
);
}
}
currentUser is of type User therefore you do not need to use await since it doesn't return a Future. You can do the following:
FirebaseFirestore.instance.collection('notes').doc(_auth.currentUser.uid).collection('usernotes').snapshots(),
And:
FloatingActionButton(child: Icon(Icons.add), onPressed: () {
FirebaseFirestore.instance.collection('notes').doc(_auth.currentUser.uid).collection('usernotes').add({'text' : 'This was added by clicking the button!'});
});

Flutter web application: can't write to Firebase

I am trying to convert the boilerplate Flutter example into a web application and store the time of each click in Firestore. The program can login and the counter got incremented at each click. But it can't write to Firestore (checked via Firebase Console). Grateful if anyone can point out where I have done wrong, or pointer to the right approach. This is the code:
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
#override
void _incrementCounter() {
setState(() async {
Firestore.instance.collection("click").add(
{
"time" : DateTime.now(),
});
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('DEMO')
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class AuthService {
//Handle Authentication
handleAuth() {
FirebaseAuth.instance
.signInWithEmailAndPassword(
email: 'xxxxxxxx#gmail.com', password: 'xxxxxxxx');
return StreamBuilder(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (context, snapshot) {
if (snapshot.hasData) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MyHomePage()),
);
;
} else {
return Text('Login failed');
}
},
);
}
}

handle link and navigate with firebase dynamic links in flutter

I am using firebase_dynamic_links 0.5.0 in my flutter project app. I am using bloc for state management and I have the issue with the navigation. I am handling the link normally but when I try to navigate to another page that's not work . I don't know why ?
PS : I have test the firebase_dynamic_links example and that's work normally
main() {
WidgetsFlutterBinding.ensureInitialized();
// Production code
ErrorWidget.builder = (FlutterErrorDetails details) => Container();
runApp(MyApp());
}
// Myapp class here I get the result of print but navigation don't work
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final _authBloc = AuthBloc();
final _progressBloc = new ProgressBloc();
#override
void initState() {
super.initState();
initDynamicLinks();
}
void initDynamicLinks() async {
final PendingDynamicLinkData data =
await FirebaseDynamicLinks.instance.getInitialLink();
final Uri deepLink = data?.link;
//print("hhhhhhh" + deepLink.path);
if (deepLink != null) {
print("Link :" + deepLink.path);
// this line don't work
Navigator.pushNamed(context, '/helloworld');
}
FirebaseDynamicLinks.instance.onLink(
onSuccess: (PendingDynamicLinkData dynamicLink) async {
final Uri deepLink = dynamicLink?.link;
if (deepLink != null) {
print("Link :" + deepLink.path);
Navigator.pushNamed(context, '/helloworld');
}
}, onError: (OnLinkErrorException e) async {
print('onLinkError');
print(e.message);
});
}
#override
Widget build(BuildContext context) {
ThemeData themeData = ThemeData(
fontFamily: Constants.DEFAULT_FONT_NAME,
textTheme: Theme.of(context)
.textTheme
.apply(fontFamily: Constants.DEFAULT_FONT_NAME),
brightness: Brightness.light,
canvasColor: Colors.black12,
scaffoldBackgroundColor: Colors.white,
primaryIconTheme:
Theme.of(context).primaryIconTheme.copyWith(color: Colors.white),
accentIconTheme:
Theme.of(context).accentIconTheme.copyWith(color: Colors.white),
primaryTextTheme:
Theme.of(context).primaryTextTheme.apply(bodyColor: Colors.white),
primaryColorLight: Colors.white,
primaryColorBrightness: Brightness.light,
primarySwatch: Constants.COLOR_PRIMARY,
primaryColor: Constants.COLOR_PRIMARY,
);
var home = Stack(children: <Widget>[
RootPage(),
ProgressDialog(),
]);
return ProgressProvider(
dialog: _progressBloc,
child: AuthBlocProvider(
bloc: _authBloc,
child: StoreBlocProvider(
store: Store(),
child: RootProvider(
bloc: new RootBloc(),
child: MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
const Locale('en'), // English
const Locale('fr'), // Hebrew
],
title: 'Foodynasty',
theme: themeData,
debugShowCheckedModeBanner: false,
routes: <String, WidgetBuilder>{
'/': (BuildContext context) => home,
'/helloworld': (BuildContext context) => RestaurantShare(
restaurantId: 'test',
),
},
),
),
),
),
);
}
}
thank you, and sorry for my bad English.
It could be that the deepLink is null and the code inside the if statement is not executed. Could you provide an example of the deep link you are sending to your app? It could be that firebase_dynamic_links cannot parse the deep link correctly if it has an error.

Resources