I have the following methods to see if the user is already logged in, which in this case I did log in and the getCurrentUser() function works because in the console it does return "USER IS NOT NULL" but the home widget is still null giving me the "EXCEPTION CAUGHT BY WIDGETS LIBRARY" saying that the home can't be null and stuff.
userAPI.dart
Future<FirebaseUser> getCurrentUser() async {
FirebaseUser user = await FirebaseAuth.instance.currentUser();
if (user != null) {
return user;
} else {
return null;
}
}
main.dart
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
Widget home;
APIs().usersAPI.getCurrentUser().then((u) {
if (u == null) {
print('USER IS NULL');
home = WelcomePage();
} else {
print('USER IS NOT NULL');
home = FeedPage();
}
});
return MaterialApp(
title: "Jedi",
debugShowCheckedModeBanner: false,
home: home,
routes: {
'/login' : (context) => new LoginPage(),
'/feed' : (context) => new FeedPage(),
},
);
}
}
You need to make the App a StatefulWidget and call setState when setting the home page
setState(() {
home = WelcomePage();
});
setState(() {
home = FeedPage();
});
Plus you may need to set the home page to something other than null before the API returns.
What probably would be a better pattern is to use a FutureBuilder. This way you will be returning the correct Widget depending on the state you are in.
return MaterialApp(
title: "Jedi",
debugShowCheckedModeBanner: false,
home: FutureBuilder<FirebaseUser>(
future: APIs().usersAPI.getCurrentUser(),
builder: (BuildContext context, AsyncSnapshot<FirebaseUser> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return CircularProgressIndicator();
default:
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
else
if(snapshot.data == null)
return WelcomePage();
else
return FeedPage();
}
}
),
routes: {
'/login' : (context) => new LoginPage(),
'/feed' : (context) => new FeedPage(),
},
);
}
Advancing the answer given by #aqwert, you need to check for the user is not null/is null after the connection status. See below working example - this assumes autologin if user is not null.
class LandingPage extends StatelessWidget {//call this class from the main.dart
#override
Widget build(BuildContext context) {
return StreamBuilder<FirebaseUser>(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
FirebaseUser user = snapshot.data;//get the user status once the connection is established
if (user == null) {
//print("User is NULL::: " + user.toString());
return LoginScreen();//
}
print("User is NOT NULL::: " + user.toString());
return DefaultScreen();//home screen
} else {
return Scaffold(
body: Center(
child: CircularProgressIndicator(),//called in case all fails while waiting for connection status
),
);
}
},
);
Here is my simple solution you can try this, First we need a stateful widget and override the function initState() inside initState() we can work something look like this-
class _MyAppState extends State<MyApp> {
String initPage;
final FirebaseAuth auth=FirebaseAuth.instance;
User currentUser;
#override
void initState() {
super.initState();
try {
currentUser = auth.currentUser;
if(currentUser!=null){
initPage=Chat.id;
/*
here id is static variable which declare as a page name.
*/
}
else{
initPage=Home.id;
}
}
catch(e){
print(e);
initPage=Home.id;
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: initPage,
routes: {
Home.id: (context) => Home(),
Login.id: (context) => Login(),
Registration.id: (context) => Registration(),
Chat.id: (context) => Chat(),
},
);
}
}
Related
I'd like to define variable 'users' but I get into an error
I want to define this on this
I was learning to make display profile on flutter like regular basic apps but it can't auto generate it
this is my full code of Profile.dart
part of 'views.dart';
class Profile extends StatefulWidget {
const Profile({Key? key}) : super(key: key);
#override
_ProfileState createState() => _ProfileState();
}
class _ProfileState extends State<Profile> {
final FirebaseAuth auth = FirebaseAuth.instance;
Future getUser() async {
await FirebaseFirestore.instance.collection('Users').doc(auth.currentUser!.uid).get().then((DocumentSnapshot doc) async {
final Users users = Users (
doc['uid'],
doc['photo'],
doc['name'],
doc['phone'],
doc['email'],
doc['password'],
doc['created'],
doc['updated'],
doc['entered'],
doc['left']
);
return ProfileView(users: users);
});
}
#override
void initState() {
super.initState();
getUser();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getUser(),
builder: (context, snapshot) {
if (snapshot.hasError || !snapshot.hasData) {
return const Center(child: Text("No internet connection"));
}
else if (snapshot.connectionState == ConnectionState.waiting) {
return Activity.loading();
}
return ProfileView(users: users);
}
);
}
}
ProfileView.dart
I was planning to return the display to ProfileView.dart that contains a lot of widgets on there
views.dart & widgets.dart just packages only
Return Users in getUser directly (not ProfileView)
Future getUser() async {
return await FirebaseFirestore.instance.collection('Users').doc(auth.currentUser!.uid).get().then((DocumentSnapshot document) async {
doc = document.data();
final Users users = Users (
doc['uid'],
doc['photo'],
doc['name'],
doc['phone'],
doc['email'],
doc['password'],
doc['created'],
doc['updated'],
doc['entered'],
doc['left']
);
return users;
});
}
Use snapshot.data
builder: (context, snapshot) {
if (snapshot.hasError || !snapshot.hasData) {
return const Center(child: Text("No internet connection"));
}
else if (snapshot.connectionState == ConnectionState.waiting) {
return Activity.loading();
}
return ProfileView(users: snapshot.data! as Users);
}
Firebase Flutter authStateChanges snapshot.data always return true,
Even i deleted the user from Firebase Authentication.
I readed some article that they said Firebase still store the token and will refresh it after 1 Hours,
But i wait 6 Hours the authStateChanges snapshot still returning true
Is that any wrong with my code or my Stream Builder?
Or how can i make a private route in Flutter to see that client is logged in or not,
Like if they not logged in they will redirected to Login page, and if they logged in they will redirected to Homepage etc
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MainApp());
}
class MainApp extends StatelessWidget {
const MainApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.hasData) {
print('Snapshot => $snapshot');
return HomeScreen();
} else {
print('Not Logged In!!!');
return GetStartedScreen();
}
} else {
return Text('Loading...');
}
},
),
);
}
}
To be honest, I don't know exactly the answer to your specific problem, but I can advice you to refactor the builder part like:
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: Text("Is Loading...");
}
else if (snapshot.hasError){
return Center(child: Text("Error: ${snapshot.error}");
}
else if (snapshot.hasData) {
print('Snapshot => $snapshot');
return HomeScreen();
} else {
print('Not Logged In!!!');
return GetStartedScreen();
}
}
},
Listen:False
I'm trying to stop rebuild LandingUser Widget but it doesn't work wiht listen : false . And if it keep it rebuilding I faces another issue I explained it // in the code below .
LandingUser class
class LandingUser extends StatelessWidget {
const LandingUser({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
print("Landing User Rebuild ----------");
final auth = Provider.of<AuthService?>(context , listen: false); // false not working
print(auth);
return StreamBuilder<MyUser?>(
stream: auth!.onChangeStateUser,
builder: (context, snapshot) {
print(snapshot.hasData);
print(snapshot);
if (snapshot.connectionState == ConnectionState.active) {
MyUser? user = snapshot.data;
print("user $user");
if (user == null) {
return Scaffold(body: Text('test login rebuild'),); // LogingScreen();
} else {
return Scaffold(body: Text('test Not null User rebuild'),); // UserProfile();
}
}else{
//if(user==null){return LoginScreen();} //without if .. CircularProgressIndicator don't stop because I used in Login Screen page TextFormFeild and when foucse the widget rebuild itself and start loading again cuz the widget rebuilt .. this is another issue _!_
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
},
);
}
}
Root widget main :
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
print("Main rebulided");
return Provider<AuthService?>.value(
value: AuthService(),
child: MaterialApp(
//.......
),
);
}
}
auth!.onChangeStateUser Stream getter:
#override
Stream<MyUser?>? get onChangeStateUser => _auth.authStateChanges().map((event) => _userFromFirebase(event));
_userFromFirebase Private Func :
MyUser? _userFromFirebase(User? user) {
if (user == null) {
return null;
}
return MyUser(uid: user.uid);
}
Thanks in advance
Provider.of<AuthService?>(context , listen: false);
The above code means the widget it's being used in will not rebuilt when the value of AuthService changes.
It does not mean your widget will not be rebuilt when something else like the auth!.onChangeStateUser stream is being used in a StreamBuilder.
So while the value of AuthService isn't changing, the stream from the instance of AuthService is changing and that is what is causing the rebuilds.
I want to check if the user is already logged in and show him page depending on that.
Here is my main.dart:
...
import 'firebase/authentication_service.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
Provider<AuthenticationService>(
create: (_) => AuthenticationService(FirebaseAuth.instance),
),
StreamProvider(
create: (context) =>
context.read<AuthenticationService>().authStateChanges,
initialData: null,
),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
backgroundColor: Colors.transparent,
primaryColor: Color(0xff4d629f),
buttonBarTheme:
ButtonBarThemeData(alignment: MainAxisAlignment.center)),
home: AuthenticationWrapper(),
),
);
}
}
class AuthenticationWrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final firebaseUser = context.watch<User>();
if (firebaseUser != null) {
//If the user is successfully Logged-In.
return HomePage();
} else {
//If the user is not Logged-In.
return LoginPage();
}
}
}
And here is my Authentication_service.dart:
class AuthenticationService {
final FirebaseAuth _firebaseAuth;
UserModel userModel = UserModel.empty();
final userRef = FirebaseFirestore.instance.collection('users');
AuthenticationService(this._firebaseAuth);
Stream<User?> get authStateChanges => _firebaseAuth.authStateChanges();
Future<String> signIn(
{required String email, required String password}) async {
try {
await _firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
return "Signed in";
} on FirebaseAuthException catch (e) {
if (e.code == 'user-not-found')
return "There is no user for that e-mail";
else if (e.code == 'wrong-password')
return "Entered wrong Password";
else
return "Something went wrong: $e";
}
}
...
And there are errors:
The following ProviderNotFoundException was thrown building AuthenticationWrapper(dirty):
Error: Could not find the correct Provider above this AuthenticationWrapper Widget
This happens because you used a BuildContext that does not include the provider
of your choice. There are a few common scenarios:
You added a new provider in your main.dart and performed a hot-reload.
To fix, perform a hot-restart.
The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then
other routes will not be able to access that provider.
You used a BuildContext that is an ancestor of the provider you are trying to read.
Make sure that AuthenticationWrapper is under your MultiProvider/Provider.
This usually happens when you are creating a provider and trying to read it immediately.
For example, instead of:
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// Will throw a ProviderNotFoundError, because `context` is associated
// to the widget that is the parent of `Provider<Example>`
child: Text(context.watch<Example>()),
),
}
consider using builder like so:
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// we use `builder` to obtain a new `BuildContext` that has access to the provider
builder: (context) {
// No longer throws
return Text(context.watch<Example>()),
}
),
}
Unfortunately it didn't help or i just can't implement it in a right way.
#SumerSingh solution worked, i just changed it a bit for my use.
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool _initialized = false;
bool _error = false;
void initializeFlutterFire() async {
try {
await Firebase.initializeApp();
setState(() {
_initialized = true;
});
} catch (e) {
setState(() {
_error = true;
});
}
}
#override
void initState() {
initializeFlutterFire();
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'App',
debugShowCheckedModeBanner: false,
home: Scaffold(
body: _error
? splashScreen()
: !_initialized
? splashScreen()
: SplashScreen()));
}
}
class SplashScreen extends StatefulWidget {
SplashScreen({Key? key}) : super(key: key);
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
final FirebaseAuth _auth = FirebaseAuth.instance;
var currentUser;
AuthenticationService _authService =
new AuthenticationService(FirebaseAuth.instance);
late final UserModel userModel;
bool isAuthinticated = false;
_isUserSignedin() async {
currentUser = _auth.currentUser;
userModel = await _authService.getUserFromDB(uid: _auth.currentUser!.uid);
setState(() {
currentUser != null ? isAuthinticated = true : isAuthinticated = false;
});
}
#override
void initState() {
super.initState();
_isUserSignedin();
startTime();
}
startTime() async {
var _duration = new Duration(seconds: 4);
return new Timer(_duration, navigationPage);
}
Widget userAuthState() {
if (!isAuthinticated)
return LoginPage();
else if (userModel.type == 'Attendant')
return AttendantMainPage();
else
return SeniorMainPage();
}
void navigationPage() {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (BuildContext context) => userAuthState()),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(body: splashScreen());
}
}
Widget splashScreen() {
return Container(
height: double.maxFinite,
width: double.maxFinite,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
CircleAvatar(
radius: 80.0, child: Image.asset('assets/logo.png')),
Text("APP NAME",
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
CircularProgressIndicator()
]),
);
}
It works well, thank you for help!
I want to add an if statement function where if current user != null it should nav to Home() and not then it should nav to Login().
this is the current main file
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MaterialApp(
title: 'TaakStore',
home: StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (BuildContext context, AsyncSnapshot<User> snapshot) {
if (snapshot.hasData) {
print(snapshot);
return Home();
} else {
return Login();
}
},
),
));
}
instead of the streambuilder i want to add a firebase auth function
this
if(FirebaseAuth.instance.currentUser != null){
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => Home();));
}
else{
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => Login();));
}
Please help me!!
You can use ternary operation like so:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
bool loggedIn = FirebaseAuth.instance.currentUser !=null;
runApp(MaterialApp(
title: 'TaakStore',
home: loggedIn ? Home(): Login(),
),
);
}
I recommend you to use 'routes' in MaterialApp, I can give you an example here:
initialRoute: await AppRoute.getInitialRoute()
and open AppRoute and define there
static Future<String> getInitialRoute() {
if (FirebaseAuth.instance.currentUser != null) {
return '/home-page';
} else {
return '/login-page';
} }