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);
}
Related
This question already has answers here:
What is a Future and how do I use it?
(6 answers)
Closed 10 months ago.
The idea
I want to display followers. the page take list of followers user id and then display their username.
Error
when I tried to I get an Error say type 'Future<dynamic>' is not a subtype of type 'Widget'
The issue in this line Text(user["username"]),
Code
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class Following extends StatefulWidget {
final following ;
const Following({Key? key, required this.following}) : super(key: key);
#override
_FollowingState createState() => _FollowingState();
}
class _FollowingState extends State<Following> {
/*attribute*/
var following =[];
bool islouded = false;
var usersData= [];
#override
void initState() {
super.initState();
setState(() {
following = widget.following;
});
getFollowing();
}
void getFollowing() {
for(var user in following){
setState(() {
print(user);
// print(getUser(user));
usersData.add( getUser(user));
});
}
setState(() {
islouded = true;
});
}
getUser(uid)async{
try {
if (uid != null) {
var userSnap = await FirebaseFirestore.instance
.collection('users')
.doc(uid)
.get();
var userData = userSnap.data()!;
// print(userSnap.data()!["username"].toString());
return userData;
}
}catch(e){
showSnackBar(context, e.toString());
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: !islouded?
const Center(
child: CircularProgressIndicator(),
):following.isNotEmpty?
Column(
children: [
for(var user in usersData)
Text(user["username"]),
],
):Text("No following yet!"),
);
}
}
Tried
I tried use FutureBuilder but I did not how to use it right because it return nothing. I believe I'm using it wrong.
the code as follow:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class Following extends StatefulWidget {
final following ;
const Following({Key? key, required this.following}) : super(key: key);
#override
_FollowingState createState() => _FollowingState();
}
class _FollowingState extends State<Following> {
/*attribute*/
var following =[];
bool islouded = false;
var usersData= [];
#override
void initState() {
super.initState();
setState(() {
following = widget.following;
});
getFollowing();
}
void getFollowing() {
for(var user in following){
setState(() {
print(user);
// print(getUser(user));
usersData.add( getUser(user));
});
}
setState(() {
islouded = true;
});
}
getUser(uid) async{
try {
if (uid != null) {
var userSnap = await FirebaseFirestore.instance
.collection('users')
.doc(uid)
.get();
return userSnap;
// print(userSnap.data()!["username"].toString());
// return userData;
}
}catch(e){
print(e.toString());
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: !islouded?
const Center(
child: CircularProgressIndicator(),
):following.isNotEmpty?
Column(
children: [
for(var user in usersData)
FutureBuilder(
future: user,
builder: (context, snapshot){
switch(snapshot.connectionState){
case ConnectionState.none:
return Text("No following yet!");
case ConnectionState.active:
return Text("active");
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(),
);
case ConnectionState.done:
print(user);//Instance of 'Future<dynamic>'
print(snapshot);//AsyncSnapshot<Object?>(ConnectionState.done, Instance of '_JsonDocumentSnapshot', null, null)
return Text("username");//i want to display username but getting different error
default:
return Text("No following yet");
}
}
)
// Text(user["username"]),
],
):Text("No following yet!"),
);
}}
Thank you for taking the time reading my question. I hope you have beautiful day like you <3
I feel this may be the culprit:
usersData.add( getUser(user));.
Try this instead: await usersData.add( getUser(user));.
As you call the async method getUser(user) async { ... } it returns a Future, and this Future gets added to the List not the user. This would explain the error complaining about an unexpected Future.
I wan't to persist authentication state of an user registered in Firebase Auth. The user has data in Firestore DB.
My final attempt :
main.dart
#override
Widget build(BuildContext context) {
return StreamProvider<AppUser?>.value(
value: AuthenticationService().user,
initialData: null,
child: const ....
);
}
home.dart
#override
Widget build(BuildContext context) {
var user = Provider.of<AppUser?>(context);
print(user);
Home.user = user;
...
}
authentication.dart
class AuthenticationService {
final FirebaseAuth _auth = FirebaseAuth.instance;
final FirebaseFirestore _db = FirebaseFirestore.instance;
final CollectionReference _usersCollection = FirebaseFirestore.instance.collection('users');
Stream<AppUser?> get user {
return _auth.authStateChanges().map((firebaseUser) {
AppUser? user;
_usersCollection.doc(firebaseUser!.uid).get().then((DocumentSnapshot userSnapshot) {
user = _toAppUser(firebaseUser, userSnapshot);
});
return user;
});
}
}
But with this code, the get user is always null, even just afte logging in
So after dozens of changes, the following code is "working" :
main.dart
#override
Widget build(BuildContext context) {
cart.cache();
return const MaterialApp(
title: 'CharleMi\'App',
debugShowCheckedModeBanner: false,
home: Home(),
);
}
home.dart
#override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.hasData) {
AuthenticationService.delegate(snapshot.data).then((user) {
if (user != null) {
Home.user = user;
}
});
} else if (snapshot.hasError) {
print(snapshot.error);
}
return ....
authentication.dart
static Future<AppUser?> delegate(User? data) async {
return AuthenticationService()._toAsyncAppUser(data, null);
}
Future<AppUser?> _toAsyncAppUser(User? user) async {
AppUser _user = AppUser(uid: user!.uid);
var exists = await _user.init(); //Return true, get vars from firestore
if (exists) {
return _user; //Returned here when logging in (because exists)
}
return null;
}
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();
}
}
},
it works since now, I get this problem sometimes but I am sure to initializate firebase.
It run normally without debug mode but with debug mode it give me this error.
I try to change database rules too but not works.
This is my Main code:
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(App());
}
class App extends StatefulWidget {
App({Key? key}) : super(key: key);
#override
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
final Future<FirebaseApp> _initialization = Firebase.initializeApp();
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _initialization,
builder: (context, snapshot) {
User user = FirebaseAuth.instance.currentUser;
if (snapshot.hasError) {
return Text(snapshot.error.toString());
} else if (snapshot.connectionState == ConnectionState.done) {
if (user != null) {
if (Platform.isAndroid) {
return const MaterialApp(
home: ChooseMarco(),
);
} else {
return const CupertinoApp(
home: ChooseMarcoIOS(),
);
}
} else {
if (Platform.isAndroid) {
return MaterialApp(
home: SignInAndroid(),
);
} else {
return CupertinoApp(
home: SignInIOS(),
);
}
}
} else {
return const CircularProgressIndicator();
}
},
);
}
}
What can I do?
Thank you very much !
You need to put await in front of Firebase.initializeApp(). This is my example for it.
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(App());
}
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(),
},
);
}
}