How to check if User is Signed In in Flutter Firebase - flutter

Hy here everyone. I am new to flutter and i want to check if User is SignedIn. If so the user navigate to HomeScreen else SplashScreen.
Here is my main.dart
void main() async{
runApp(MyApp());
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
}
// ignore: must_be_immutable
class MyApp extends StatelessWidget {
String initRoute;
User user = FirebaseAuth.instance.currentUser;
getUser(){
if (user != null) {
initRoute = MainScreen.routeName;
} else {
initRoute = SplashScreen.routeName;
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Instant Tasker',
theme: theme(),
initialRoute: initRoute,
routes: routes,
);
}
}

The proper way that I see is through the Firebase plugin, here is how it looks like.
in main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
Firebase.initializeApp().then((value) => print(value));
runApp(MyApp());
}
final FirebaseAuth auth = FirebaseAuth.instance;
String userData() {
final User user = auth.currentUser;
final uid = user.uid;
return uid;
}
if a user is logged in it will return uid(String) otherwise null

The easiest and the best way to identify if a user is already signed in or not is via using Shared Preferences.
Just add this dependency in your pubspec.yaml file.
Every time a user logs in a particular device you can save some value as a key value pair in the phone's storage using Shared Preferences.
Next time whenever a user opens the app you can just check if any value is available in the shared preferences. If so then open the Home Screen else open the Splash Screen.
This is by far the cleanest solution.
You can read more about shared Preferences here.
If you still need a sample code. Drop a comment and I will edit the answer.
Thanks

you should right function getuser with return statement of widget that contains return Statement of mainScreen if user signed in or else splashscreen and use this function in home:
Widget getUser(){
if (user != null) {
mainScreen();
} else {
splashScreen();
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Instant Tasker',
theme: theme(),
home:getUser(),
);
}
}

Thank you every one i solved my problem. Everyone on stackoverflow was providing complex solutions but still didn't solve the problem. The mistake everyone was making is they were using Firebase before the 'FirebaseApp initialize'. So i simply call the runApp when the FirebaseApp is initialized.cHere is my code
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp().then((value) {
runApp(MyApp());
});
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
User user = FirebaseAuth.instance.currentUser;
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Instant Tasker',
theme: theme(),
initialRoute: user != null ? MainScreen.routeName : SplashScreen.routeName,
routes: routes,
);
}
}

Related

The argument type 'Future<SharedPreferences>' can't be assigned to the parameter type 'SharedPreferences'

I want to access the shared preferences at the application startup and want to use this same object across the entire app by passing it to the classes. I am getting the following error:
The argument type 'Future' can't be assigned to the
parameter type 'SharedPreferences'.
main.dart
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:application/layouts/ScreenOne.dart';
import 'package:application/layouts/ScreenTwo.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
sharedPreferences() async {
return await SharedPreferences.getInstance();
}
final preferences = SharedPreferences.getInstance();
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'MyApp',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: (preferences.getInt("login") == 1 ? ScreenOne(preferences) : ScreenTwo(preferences)),
);
}
}
I am unable to resolve this error. Is there anything I am doing wrong or missing? Thanks!!!
First of all, you defined function sharedPreferences() but did not use it later in the code - simply remove it.
Furthermore, based on the documentation SharedPreferences.getInstance() returns Future<SharedPreferences> and not SharedPreferences, hence you get the following error. You can resolve the issue by getting the SharedPreferences instance in the main method and then using constructor injection to provide the preferences object to the MyApp:
Future<void> main() async { // <-- Notice the updated return type and async
final preferences = await SharedPreferences.getInstance(); // <-- Get SharedPreferences instance
runApp(
MyApp(preferences: preferences), // <-- Inject (pass) SharedPreferences object to MyApp
);
}
class MyApp extends StatelessWidget {
final SharedPreferences preferences;
const MyApp({
required this.preferences,
})
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'MyApp',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: (preferences.getInt("login") == 1 ? ScreenOne(preferences) : ScreenTwo(preferences)),
);
}
}

Setting theme based on SharedPreference data

My goal for this flutter app is to change the theme (Dark, Light, System) based on the stored Shared Preferences data.
I used Provider so that every time the user changes the theme, the entire app will update based on the selected theme. The issue is when the user first starts up the app, it finishes building before we can get the value of the theme from Shared Preference. Therefore we are getting a null value for the theme when the app initially loads. My code only works if the user is updating the theme value after the app finishes loading on startup.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(ChangeNotifierProvider<PreferencesProvider>(
create: (context) {
return PreferencesProvider();
},
child: MyApp()));
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
print(SettingsSharedPreferences.getThemeString());
return ChangeNotifierProvider<HabitProvider>(
create: (context) {
return HabitProvider();
},
child: MaterialApp(
darkTheme: ThemeData(brightness: Brightness.dark),
themeMode: context.watch<PreferencesProvider>().theme == "System"
? ThemeMode.system
: context.watch<PreferencesProvider>().theme == "Dark"
? ThemeMode.dark
: context.watch<PreferencesProvider>().theme == "Light"
? ThemeMode.light
: ThemeMode.system,
home: MyHomePage(title: 'My App'),
),
);
}
}
This is the Provider class. Note that the variable theme is getting its data from the stored Shared Preferences data.
class PreferencesProvider extends ChangeNotifier {
String? theme = SettingsSharedPreferences.getThemeString();
....
}
Below is the Shared Preference code where we get the value of theme from:
class SettingsSharedPreferences {
static const _keytheme = "theme";
static String _defaultTheme = "System";
static setTheme(String theme) async {
await sharedPreferences.setString(_keytheme, theme);
}
static Future<String?> getTheme() async {
final sharedPreferences = await SharedPreferences.getInstance();
if (sharedPreferences.getString(_keytheme) == null) {
await setTheme(_defaultTheme);
}
return sharedPreferences.getString(_keytheme);
}
static String? getThemeString() {
print("getThemeString");
String? theme;
getTheme().then((val) {
theme = val;
});
return theme;
}
}
I suggest loading the preferences before the app is built and shown to the user:
Future<void> main() async {
...
final preferences = await SharedPreferences.getInstance();
runApp(MainApp(
preferences: preferences,
));
}
In your case, you could pass the theme value to MyApp widget.

Monitor Auth Status from Firebase using StreamProvider 4.0.1 in Flutter

I am trying to auto login a user if they haven't expressly signed out from the app. I am using firebase for authentication and provider package from pub to monitor the auth status changes.
I have a service dart file (originally from TheNetNinja tutorials) which exposes a stream and wrapped the main.dart file with a stream provider. As I understand that in provider 4.0.1, one can no longer use .value and supply the value like below:
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return StreamProvider<User>.value(
value: AuthService().user,
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Brewzz',
home: Wrapper(),
),
);
}
}
but to change to something of this sort, using create instead of value like below:
class FoodMart extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return StreamProvider(
create: (_) => AuthService().user,
child: MaterialApp(
title: 'Flutter Demo',
onGenerateRoute: Router.generateRoute,
home: Landing(),
),
);
}
}
I have closed and re-opened the app but still not re-directing to home page. Am I doing this wrong?
Here's the implementation of AuthService.dart class:
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:food_mart/models/users.dart';
import 'package:food_mart/services/database.dart';
class AuthService {
FirebaseAuth _mAuth = FirebaseAuth.instance;
Stream<User> get user {
return _mAuth.onAuthStateChanged.map(_mapUserFromFirebase);
}
User _mapUserFromFirebase(FirebaseUser firebaseUser) {
return firebaseUser != null ? User(userId: firebaseUser.uid) : null;
}
Future createUser(String email, String pwd, String role) async{
try {
AuthResult result = await _mAuth.createUserWithEmailAndPassword(email: email, password: pwd);
FirebaseUser firebaseUser = result.user;
await DatabaseService(userId: firebaseUser.uid).setUserData(email: email, role: role);
return _mapUserFromFirebase(firebaseUser);
} catch(e){
print(e.toString());
return e;
}
}
Future loginUser(String email, String pwd) async{
try {
AuthResult authResult = await _mAuth.signInWithEmailAndPassword(email: email, password: pwd);
FirebaseUser firebaseUser = authResult.user;
return _mapUserFromFirebase(firebaseUser);
} catch(e){
print(e.toString());
return null;
}
}
Future signOut() async {
try {
return await _mAuth.signOut();
} catch(e){
print(e.toString());
return null;
}
}
}
The User model has been left out for brevity. Any suggestions please? Thanks
I had a similar issue but the code above works.
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
if(user == null){
return Landing();
}else{
return Index();
}
}
}
Just like you it didn't change the page for me. Turns out the code was working all along, only I was logging in the user from another page called LoginWithEmail which was pushed into the stack on a button event from the Landing class. I just did a Navigator.pop() on LoginWithEmail after logging in to realize that the Wrapper class changed into Index.
//code call inside login button onpressed from LoginWithEmail class
AuthService _authService = AuthService();
_authService.loginUser(email, password).then((value) {
User user = value;
if(user!=null){
Navigator.pop();
}
}

How to call sharedprefrence before first screen

I am implementing remember me option on the login screen, want to call shared preference before the widget is created. we have the one and only entry point that is the main function, but how we can call a function here to read primitive data (email/password).
void main() {
setupLocator();
runApp(MaterialApp(
debugShowCheckedModeBanner: false,
theme: new ThemeData(fontFamily: 'OpenSans-Light'),
initialRoute: "/",
onGenerateRoute: Router.generateRoute,
));
}
reading bool value
Future<bool> read(String key) async {
final prefs = await SharedPreferences.getInstance();
return prefs.getbool(key);
}
I also try to run a asyn function before route
String firstNav;
void main() {
setupLocator();
readSharedPref();
if(firstNav!=null)
runApp(MaterialApp(
debugShowCheckedModeBanner: false,
theme: new ThemeData(fontFamily: 'OpenSans-Light'),
initialRoute: firstNav,
onGenerateRoute: Router.generateRoute,
));
}
void readSharedPref() async {
Utiles df=Utiles();
String isRem=await df.read("remember");
if (isRem.contains("true")) {
firstNav='homeview';
} else {
firstNav='/';
}
}
You need to set your main function as async, and add an await and a line of code:
void main() async{
//Add this lines is necessary now that your main is async
WidgetsFlutterBinding.ensureInitialized();
//Now you have to "await" the readSharedPref() function
await readSharedPref();
// And here comes all your code
}
Instead of waiting waiting for sharedPreference to load before building any widgets, just show a loader widget with progress indicator until the shared preference is loaded, and when it's loaded, show the required view based on the value loaded from sharedPreference, here is how you can modify your code, (replace HomeView and RootView widgets with your respective widgets for your homeView and / routes)
void main() {
setupLocator();
runApp(MaterialApp(
debugShowCheckedModeBanner: false,
theme: new ThemeData(fontFamily: 'OpenSans-Light'),
initialRoute: Loader(),
onGenerateRoute: Router.generateRoute,
));
}
class Loader extends StatefulWidget {
#override
_LoaderState createState() => _LoaderState();
}
class _LoaderState extends State<Loader> {
Widget firstNav;
#override
void initState() {
super.initState();
readSharedPref();
}
void readSharedPref() async {
Utiles df=Utiles();
String isRem=await df.read("remember");
if (isRem.contains("true")) {
setState(() {
// firstNav='homeview';
firstNav=HomeView(); // replace HomeView with the widget you use for homeview route
});
} else {
setState(() {
// firstNav='/';
firstNav=RootView(); // replace RootView with the widget you use for / route
});
}
}
#override
Widget build(BuildContext context) {
return firstNav != null ? firstNav : Center(child: CircularProgressIndicator(),);
}
}

Flutter - How to pass user data to all views

I'm new to the flutter world and mobile app development and struggling with how I should pass user data throughout my app.
I've tried several things, but none seem great and I'm sure there are best practice patterns I should be following.
Because it makes examples easier, I'm using firebase for authentication.
I currently have a separate route for logging in. Once I'm logged in I want the User model in most views for checking permissions on what to show, displaying user info in the drawer, etc...
Firebase has an await firebaseAuth.currentUser(); Is it best practice to call this everywhere you might need the user? and if so, where is the best spot to place this call?
The flutter codelab shows a great example of authenticating users before allowing writes. However, if the page needs to check auth to determine what to build, the async call can't go in the build method.
initState
One method I've tried is to override initState and kick off the call to get the user. When the future completes I call setState and update the user.
FirebaseUser user;
#override
void initState() {
super.initState();
_getUserDetail();
}
Future<Null> _getUserDetail() async {
User currentUser = await firebaseAuth.currentUser();
setState(() => user = currentUser);
}
This works decent but seems like a lot of ceremony for each widget that needs it. There is also a flash when the screen loads without the user and then gets updated with the user upon the future's completion.
Pass the user through the constructor
This works too but is a lot of boilerplate to pass the user through all routes, views, and states that might need to access them. Also, we can't just do popAndPushNamed when transitioning routes because we can't pass a variable to it. We have to change routes similar to this:
Navigator.push(context, new MaterialPageRoute(
builder: (BuildContext context) => new MyPage(user),
));
Inherited Widgets
https://medium.com/#mehmetf_71205/inheriting-widgets-b7ac56dbbeb1
This article showed a nice pattern for using InheritedWidget. When I place the inherited widget at the MaterialApp level, the children aren't updating when the auth state changed (I'm sure I'm doing it wrong)
FirebaseUser user;
Future<Null> didChangeDependency() async {
super.didChangeDependencies();
User currentUser = await firebaseAuth.currentUser();
setState(() => user = currentUser);
}
#override
Widget build(BuildContext context) {
return new UserContext(
user,
child: new MaterialApp(
title: 'TC Stream',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new LoginView(title: 'TC Stream Login', analytics: analytics),
routes: routes,
),
);
}
FutureBuilder
FutureBuilder also seems like a decent option but seems to be a lot of work for each route. In the partial example below, _authenticateUser() is getting the user and setting state upon completion.
#override
Widget build(BuildContext context) {
return new FutureBuilder<FirebaseUser>(
future: _authenticateUser(),
builder: (BuildContext context, AsyncSnapshot<FirebaseUser> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return _buildProgressIndicator();
}
if (snapshot.connectionState == ConnectionState.done) {
return _buildPage();
}
},
);
}
I'd appreciate any advice on best practice patterns or links to resources to use for examples.
I'd recommend investigating inherited widgets further; the code below shows how to use them with asynchronously updating data:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(new MaterialApp(
title: 'Inherited Widgets Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new Scaffold(
appBar: new AppBar(
title: new Text('Inherited Widget Example'),
),
body: new NamePage())));
}
// Inherited widget for managing a name
class NameInheritedWidget extends InheritedWidget {
const NameInheritedWidget({
Key key,
this.name,
Widget child}) : super(key: key, child: child);
final String name;
#override
bool updateShouldNotify(NameInheritedWidget old) {
print('In updateShouldNotify');
return name != old.name;
}
static NameInheritedWidget of(BuildContext context) {
// You could also just directly return the name here
// as there's only one field
return context.inheritFromWidgetOfExactType(NameInheritedWidget);
}
}
// Stateful widget for managing name data
class NamePage extends StatefulWidget {
#override
_NamePageState createState() => new _NamePageState();
}
// State for managing fetching name data over HTTP
class _NamePageState extends State<NamePage> {
String name = 'Placeholder';
// Fetch a name asynchonously over HTTP
_get() async {
var res = await http.get('https://jsonplaceholder.typicode.com/users');
var name = json.decode(res.body)[0]['name'];
setState(() => this.name = name);
}
#override
void initState() {
super.initState();
_get();
}
#override
Widget build(BuildContext context) {
return new NameInheritedWidget(
name: name,
child: const IntermediateWidget()
);
}
}
// Intermediate widget to show how inherited widgets
// can propagate changes down the widget tree
class IntermediateWidget extends StatelessWidget {
// Using a const constructor makes the widget cacheable
const IntermediateWidget();
#override
Widget build(BuildContext context) {
return new Center(
child: new Padding(
padding: new EdgeInsets.all(10.0),
child: const NameWidget()));
}
}
class NameWidget extends StatelessWidget {
const NameWidget();
#override
Widget build(BuildContext context) {
final inheritedWidget = NameInheritedWidget.of(context);
return new Text(
inheritedWidget.name,
style: Theme.of(context).textTheme.display1,
);
}
}
I prefer to use Services with Locator, using Flutter get_it.
Create a UserService with a cached data if you like:
class UserService {
final Firestore _db = Firestore.instance;
final String _collectionName = 'users';
CollectionReference _ref;
User _cachedUser; //<----- Cached Here
UserService() {
this._ref = _db.collection(_collectionName);
}
User getCachedUser() {
return _cachedUser;
}
Future<User> getUser(String id) async {
DocumentSnapshot doc = await _ref.document(id).get();
if (!doc.exists) {
log("UserService.getUser(): Empty companyID ($id)");
return null;
}
_cachedUser = User.fromDocument(doc.data, doc.documentID);
return _cachedUser;
}
}
Then create create a Locator
GetIt locator = GetIt.instance;
void setupLocator() {
locator.registerLazySingleton(() => new UserService());
}
And instantiate in main()
void main() {
setupLocator();
new Routes();
}
That's it! You can call your Service + cachedData everywhere using:
.....
UserService _userService = locator<UserService>();
#override
void initState() {
super.initState();
_user = _userService.getCachedUser();
}
I crashed into another problem because of this problem you can check it out here
So the solution I came up with is a bit untidy,I created a separate Instance dart page and imported it to every page.
GoogleSignInAccount Guser = googleSignIn.currentUser;
FirebaseUser Fuser;
I stored the user there on login and checked on every StateWidget if it was null
Future<Null> _ensureLoggedIn() async {
if (Guser == null) Guser = await googleSignIn.signInSilently();
if (Fuser == null) {
await googleSignIn.signIn();
analytics.logLogin();
}
if (await auth.currentUser() == null) {
GoogleSignInAuthentication credentials =
await googleSignIn.currentUser.authentication;
await auth.signInWithGoogle(
idToken: credentials.idToken,
accessToken: credentials.accessToken,
);
}
This is my old code I did cleaned it up on my current app but I don't have that code now in handy. Just check out for null user and log it in again
I did it for most of the Firebase instances too because I have more than 3 pages on my app and Inherited Widgets was just too much work
You can use the GetX package to check whether or not the user is logged in, get user data and have it accessible throughout your app
For my lazy mathod,
i just create new file like userdata.dart and then put any variable on it for example like dynamic Profile = null
inside userdata.dart
//only put this or anything u want.
dynamic Profile = null;
at startingpage.dart
//import that file
import '../userdata.dart';
class startingpage extends ...{
...
//set data to store..
Profile = 'user profile';
...
}
to use the data just declare and use in
anotherpage.dart
//import that file
import '../userdata.dart';
class anotherpage extends...{
...
}
class .. State ...{
...
//set the data to variable
dynamic userdata = Profile;
print('this is my lazy pass data' + userdata.toString());
...
}