I am using the code from -> https://github.com/aaronksaunders/simple_firebase_auth/tree/master/lib
When I log in using Firebase, I would like the page to automatically redirect to the HomePage.
The code for main.dart is as follows:
void main() {
// setupLocator();
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown])
.then((_) => runApp(
ChangeNotifierProvider<AuthService>(
child: MyApp(),
builder: (BuildContext context) {
return AuthService();
},
),
));
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
onGenerateRoute: Router.generateRoute,
theme: ThemeData(primarySwatch: Colors.blue),
home: FutureBuilder<FirebaseUser>(
future: Provider.of<AuthService>(context).getUser(),
builder: (context, AsyncSnapshot<FirebaseUser> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// log error to console
if (snapshot.error != null) {
print("error");
return Text(snapshot.error.toString());
}
// redirect to the home page
return snapshot.hasData
? CustomerHomePage(snapshot.data)
: LoginPage();
} else {
// show loading indicator
return LoadingCircle();
}
},
),
);
}
}
The login page code is:
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final _formKey = GlobalKey<FormState>();
String _password;
String _email;
FocusNode nodeOne = FocusNode();
FocusNode nodeTwo = FocusNode();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: prefix0.backgroundColor,
body: Form(
key: _formKey,
child: Padding(
padding: EdgeInsets.all(20.0),
child: Stack(
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
TextFormField(
style: TextStyle(color: Colors.white),
textInputAction: TextInputAction.next,
focusNode: nodeOne,
onSaved: (value) => _email = value,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
prefixIcon:
Image.asset('assets/images/icons/Username.png'),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white),
),
labelText: "Email Address",
labelStyle: TextStyle(
color: nodeOne.hasFocus ? Colors.white : Colors.grey),
),
),
TextFormField(
style: TextStyle(color: Colors.white),
textInputAction: TextInputAction.go,
focusNode: nodeTwo,
onSaved: (value) => _password = value,
obscureText: true,
decoration: InputDecoration(
prefixIcon: Image.asset(
'assets/images/icons/PasswordLock.png'),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white),
),
labelStyle: TextStyle(
color: nodeTwo.hasFocus
? Colors.white
: Colors.grey),
labelText: "Password")),
SizedBox(height: 20.0),
Center(
child: FlatButton(
onPressed: () {},
child: Text('Forgotten password?',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white)),
),
),
ButtonTheme(
minWidth: MediaQuery.of(context).size.width,
height: 40,
child: RaisedButton(
textColor: prefix0.backgroundColor,
color: Colors.white,
child: Text("Login"),
onPressed: () {
_loginAction();
},
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(5.0))),
),
Center(
child: FlatButton(
onPressed: () {},
child: Text('Not registered?',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white)),
),
),
Divider(
indent: MediaQuery.of(context).size.width * 0.333333,
height: 5,
thickness: 3,
color: Colors.white,
endIndent: MediaQuery.of(context).size.width * 0.333333,
),
SizedBox(height: 15),
ButtonTheme(
minWidth: MediaQuery.of(context).size.width,
height: 40,
child: RaisedButton(
textColor: prefix0.backgroundColor,
color: Colors.white,
child: Text("Not a customer? Visit out site"),
onPressed: () {},
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(5.0))),
),
SizedBox(height: 20),
],
),
],
),
),
),
);
}
_loginAction() async {
final form = _formKey.currentState;
form.save();
// Validate will return true if is valid, or false if invalid.
if (form.validate()) {
try {
FirebaseUser result = await Provider.of<AuthService>(context)
.loginUser(email: _email, password: _password);
print(result);
} on AuthException catch (error) {
return _buildErrorDialog(context, error.message);
} on Exception catch (error) {
return _buildErrorDialog(context, error.toString());
}
}
}
Future _buildErrorDialog(BuildContext context, _message) {
return showDialog(
builder: (context) {
return AlertDialog(
title: Text('Error Message'),
content: Text(_message),
actions: <Widget>[
FlatButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
})
],
);
},
context: context,
);
}
}
When I log in, I've put a print statement in the init state of my home page. Successful login does execute this code as I get "I am in the home page" however my screen still shows my login screen. If I press R to rebuild, I am automatically logged in and see my home page as required.
If I logout, everything works perfectly and I am automatically redirected to the login screen as expected.
Am I doing something wrong?
From what I understand on your implementation, it seems that you're trying to listen for authentication changes using FutureBuilder. However, this won't work since FutureBuilder only run once when called. If you want to listen for auth status changes, StreamBuilder is best used for this case. You can use this to listen for value changes on a Stream.
Related
In my app, any user can add an Ad and, in home screen, with all Ads showing up in a list view, I get info of every single Ad from the table of cars in firebase; but I want to show the profile images of this user by sending the user id and getting the image from users table.
My function to get data from firebase:
Future<DocumentSnapshot> getAdData(String uid)async{
return await FirebaseFirestore.instance.collection('users').doc(uid).
get();
}
My homescreen:
import 'package:cargaaaalery/functions.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'globalVar.dart';
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
FirebaseAuth auth = FirebaseAuth.instance;
String? username;
String? userNumber;
String? carPrice;
String? carModel;
String? description;
String? urlImage;
String? carLocation;
String? carColor;
QuerySnapshot? cars;
// DocumentSnapshot? users;
String? usersImg;
CarMethods carobj = new CarMethods();
dynamic carUserId;
Future<bool?> showDialogForAddingData() async {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
"post a new AD",
style: TextStyle(
fontSize: 22, fontFamily: "Bebas", letterSpacing: 2),
),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
decoration: InputDecoration(hintText: "Enter your number"),
onChanged: (val) {
userNumber = val;
},
),
SizedBox(
height: 5,
),
TextField(
decoration:
InputDecoration(hintText: "Enter your car price"),
onChanged: (val) {
carPrice = val;
},
),
SizedBox(
height: 5,
),
TextField(
decoration:
InputDecoration(hintText: "Enter your car name"),
onChanged: (val) {
carModel = val;
},
),
SizedBox(
height: 5,
),
TextField(
decoration:
InputDecoration(hintText: "Enter your car Color"),
onChanged: (val) {
carColor = val;
},
),
SizedBox(
height: 5,
),
TextField(
decoration:
InputDecoration(hintText: "Enter your car description"),
onChanged: (val) {
description = val;
},
),
SizedBox(
height: 5,
),
TextField(
decoration:
InputDecoration(hintText: "Enter your car Image"),
onChanged: (val) {
urlImage = val;
},
),
SizedBox(
height: 5,
),
TextField(
decoration:
InputDecoration(hintText: "Enter your car Location"),
onChanged: (val) {
carLocation = val;
},
),
SizedBox(
height: 5,
),
],
),
),
actions: [
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("Cancel"),
),
ElevatedButton(
onPressed: () {
Map<String, dynamic> carData = {
'username': getUsername,
'uId': userId,
'userNumber': this.userNumber,
'carPrice': this.carPrice,
'carModel': this.carModel,
'carLocation': this.carLocation,
'carColor': this.carColor,
'description': this.description,
'urlImage': this.urlImage,
'imgPro': userImageUrl,
'time': DateTime.now(),
};
carobj.addData(carData).then((value) {
print("data add successfuly");
Navigator.push(context,
MaterialPageRoute(builder: (context) {
return HomeScreen();
}));
}).catchError((onError) {
print(onError);
});
},
child: Text("Add Now"),
),
],
);
});
}
getimage()async{
await carobj.getAdData(carUserId).then((res){
setState(() {
usersImg=(res.data() as Map)['imgPro'].toString();
});
});
}
getMyData() async {
await FirebaseFirestore.instance
.collection('users')
.doc(userId)
.get()
.then((result) {
setState(() {
userImageUrl = result.data()!['imgPro'];
getUsername = result.data()!['username'];
});
});
}
#override
void initState() {
super.initState();
userId = FirebaseAuth.instance.currentUser!.uid;
userEmail = FirebaseAuth.instance.currentUser!.email!;
print("userid is $userId and email is $userEmail");
carobj.getData().then((results) {
setState(() {
cars = results;
});
});
getMyData();
}
#override
Widget build(BuildContext context) {
Widget? showCarsList() {
if (cars != null) {
return ListView.builder(
itemCount: cars!.docs.length,
padding: EdgeInsets.all(8),
itemBuilder: (context,index) {
carUserId= (cars!.docs[index].data() as Map)['uId'];
getimage();
return Card(
child: Column(
children: [
ListTile(
leading:GestureDetector(
onTap: (){},
child: Container(
width: 60,
height:60,
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
image: NetworkImage(
((usersImg!)),scale: 0.9),
fit:BoxFit.fill
)
),
),
) ,
title: GestureDetector(
onTap: (){
},
child: Text((cars!.docs[index].data() as Map)['username'].toString()),
),
subtitle: GestureDetector(
onTap: (){},
child:
Row(
children: [
((cars!.docs[index].data() as Map)['carLocation'] != null)
? Text((cars!.docs[index].data() as Map)['carLocation'] ,
style:TextStyle(color: Colors.black.withOpacity(0.6))
):Text("unknown" ,
style:TextStyle(color: Colors.black.withOpacity(0.6))
),
SizedBox(width: 4.0,),
Icon(Icons.location_pin,color: Colors.grey,)
],
),
),
trailing:
(cars!.docs[index].data() as Map)['uId']==userId?
Row(
mainAxisSize: MainAxisSize.min,
children: [
GestureDetector(
onTap: (){
},
child: Icon(Icons.edit),
),
SizedBox(width: 20,),
GestureDetector(
onDoubleTap: (){
},
child: Icon(Icons.delete_forever),
),
],
):
Row( mainAxisSize: MainAxisSize.min,
children: [],
)
,
)
],
),
);
},
);
}
}
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.refresh, color: Colors.white),
onPressed: () {},
),
actions: [
TextButton(
onPressed: () {},
child: Padding(
padding: EdgeInsets.all(10),
child: Icon(
Icons.person,
color: Colors.white,
),
)),
TextButton(
onPressed: () {},
child: Padding(
padding: EdgeInsets.all(10),
child: Icon(
Icons.search,
color: Colors.white,
),
)),
TextButton(
onPressed: () {},
child: Padding(
padding: EdgeInsets.all(10),
child: Icon(
Icons.login_outlined,
color: Colors.white,
),
))
],
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blueAccent, Colors.redAccent])),
),
title: Text("home page"),
),
body: Center(
child: showCarsList(),
),
floatingActionButton: FloatingActionButton(
tooltip: 'add post',
child: Icon(Icons.add),
onPressed: () {
showDialogForAddingData();
},
),
);
}
}
Where I get image (from home screen code):
getimage()async{
await carobj.getAdData(carUserId).then((res){
setState(() {
usersImg=(res.data() as Map)['imgPro'].toString();
});
});
}
Where I set image to listview (from home screen code):
#override
Widget build(BuildContext context) {
Widget? showCarsList() {
if (cars != null) {
return ListView.builder(
itemCount: cars!.docs.length,
padding: EdgeInsets.all(8),
itemBuilder: (context,index) {
carUserId= (cars!.docs[index].data() as Map)['uId'];
getimage();
return Card(
child: Column(
children: [
ListTile(
leading:GestureDetector(
onTap: (){},
child: Container(
width: 60,
height:60,
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
image: NetworkImage(
((usersImg!)),scale: 0.9),
fit:BoxFit.fill
)
),
),
) ,
Output of the code:
You're getting the behaviour because you are calling setState in the build method. This causes the build method to get called again and it makes the network request and calls the build method again...
You can fix this by using a FutureBuilder to get the data. Follow these steps:
Update the getimage method to return the image url instead of changing the state:
Future<String> getimage() async {
var res = await carobj.getAdData(carUserId);
return (res.data() as Map)['imgPro'].toString();
}
Declare a variable _imageFuture to hold the result of the getimage network request:
Future<String> _imageFuture;
Assign the getimage method to _imageFuture in the initState:
#override
void initState() {
...
_imageFuture = getimage();
}
Update the ListTile to use a FutureBuilder which gets the image from the _imageFuture Future:
ListTile(
leading:GestureDetector(
onTap: (){},
child: FutureBuilder<String>(
future: _imageFuture,
builder: (context, AsyncSnapshot<String> snapshot) {
if (snapshot.data == null) {
return Center(child: CircularProgressIndicator());
} else {
String usersImg = snapshot.data;
return Container(
width: 60,
height:60,
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
image: NetworkImage(
((usersImg!)),scale: 0.9),
fit:BoxFit.fill
)
),
),
);
}
}
),
),
)
In set state I call _notes to fetch notes from firebase to be updated.
But when I add a note in the widget AddNote and press save button, and then go back to NoteScreen, it is not been updated automatically. I must tu swipe down to refresh, and then _notes get again called and the data is up to date.
And I also need feedback on my code, I am a beginner and an opinion about clean code practices will be of high value.
class NoteScreen extends StatefulWidget {
static const routeName = '/noteScreen';
#override
_NoteScreenState createState() => _NoteScreenState();
}
class _NoteScreenState extends State<NoteScreen> {
Widget currentPage;
String search;
bool isLoading = false;
Future<void> _notes() async {
try {
final authData = Provider.of<Auth>(context, listen: false);
await Provider.of<NoteList>(context, listen: false)
.fetchAndSetNotes(search, authData.userId);
print('Note Screen FetchAndSetNotes');
} catch (err) {
print(err.toString());
}
}
#override
void initState() {
_notes();
super.initState();
}
#override
Widget build(BuildContext context) {
final note = Provider.of<NoteList>(context);
print(note.items.length);
SystemChrome.setEnabledSystemUIOverlays([]);
return Scaffold(
resizeToAvoidBottomInset: false,
body: Stack(alignment: Alignment.topCenter, children: <Widget>[
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Colors.teal, Colors.purple])),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
actions: [
IconButton(
icon: Icon(Icons.add),
onPressed: () {
Navigator.of(context).pushNamed(AddNote.routeName);
},
),
IconButton(
icon: Icon(Icons.logout),
onPressed: () {
Provider.of<Auth>(context, listen: false).logout();
})
],
title: Text(
'NoteApp',
style: TextStyle(fontSize: 20),
),
elevation: 0.0,
backgroundColor: Colors.transparent),
body: SingleChildScrollView(
child: Column(children: [
Align(
alignment: Alignment.topCenter,
child: Container(
height: MediaQuery.of(context).size.height * 0.10,
width: MediaQuery.of(context).size.width,
child: FloatingSearchBar(
borderRadius: BorderRadius.all(Radius.circular(20)),
hint: 'Search',
actions: [],
onQueryChanged: (query) {
setState(() {
try {
search = query;
} catch (err) {
print(err.toString());
}
_notes();
});
},
builder: (context, transition) {
return ClipRRect();
},
),
)),
Align(
alignment: Alignment.bottomCenter,
child: Container(
height: MediaQuery.of(context).size.height * 0.80,
width: MediaQuery.of(context).size.width,
child: FutureBuilder(
builder: (context, snapshot) =>
snapshot.connectionState == ConnectionState.waiting
? CircularProgressIndicator()
: RefreshIndicator(
onRefresh: () => _notes(),
child: ListView.builder(
padding: EdgeInsets.all(10),
itemCount: note.items.length,
itemBuilder: (context, i) => Column(
children: [
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
NoteDisplay(
noteItem: note.items[i],
),
),
);
},
child: note.items[i] != null
? Padding(
padding: EdgeInsets.all(5),
child: NoteItem(
note.items[i].content,
note.items[i].dateTime,
note.items[i].title,
note.items[i].id),
)
: Container(
child: Center(
child: Text(
'No notes Available'),
),
),
),
],
),
),
),
),
),
),
]),
),
)),
]),
);
}
}
**AddNote widget **
class AddNote extends StatefulWidget {
static const routeName = '/addNotes';
#override
_AddNoteState createState() => _AddNoteState();
}
class _AddNoteState extends State<AddNote> {
final _form = GlobalKey<FormState>();
TextEditingController titleControler = new TextEditingController();
var _newNotes = Notes(id: null, title: '', content: '', dateTime: '');
Future<void> _saveNote() async {
final isvalid = _form.currentState.validate();
if (!isvalid) {
return;
}
_form.currentState.save();
Navigator.of(context).pop();
try {
await Provider.of<NoteList>(context, listen: false).addNotes(_newNotes);
print('add_note');
} catch (err) {
print('add_note');
print(err.toString());
}
}
#override
Widget build(BuildContext context) {
final DateTime dateTime = DateTime.now();
String formattedDate = DateFormat('yyyy-MM-dd – kk:mm').format(dateTime);
Size size = MediaQuery.of(context).size;
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Colors.teal, Colors.purple])),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
elevation: 0.0,
backgroundColor: Colors.transparent,
title: Text('Add notes '),
actions: [IconButton(icon: Icon(Icons.save), onPressed: _saveNote)],
),
body: Center(
child: Form(
key: _form,
child: Container(
height: size.height * 0.7,
margin: EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(30))),
child: Card(
margin: EdgeInsets.all(30),
elevation: 20,
child: SingleChildScrollView(
child: Container(
padding: EdgeInsets.all(10),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
TextFormField(
validator: (value) {
if (value.isEmpty) {
return 'Please enter a Title';
}
return null;
},
controller: titleControler,
decoration: InputDecoration(
hintText: 'Title',
),
onSaved: (value) {
_newNotes = Notes(
title: value,
id: _newNotes.id,
content: _newNotes.content,
dateTime: formattedDate);
print(value.toString());
},
textInputAction: TextInputAction.next,
),
SizedBox(
height: 20,
),
TextFormField(
validator: (value) {
if (value.isEmpty) {
return "The content cannon't be empty";
}
return null;
},
onSaved: (value) {
_newNotes = Notes(
title: _newNotes.title,
content: value,
id: _newNotes.id,
dateTime: formattedDate);
print(value.toString());
},
maxLines: null,
keyboardType: TextInputType.multiline,
decoration: InputDecoration(hintText: 'Note content'),
),
SizedBox(
height: 10,
),
Text('$formattedDate')
],
),
),
),
),
),
),
),
),
);
}
}
I believe what is want is a stream instead of a future. i don't know what your fetchAndSetNotes() function looks like but Instead of doing
FirebaseFirestore.instance.collection(collectionName).doc(id).get() you should do Firestore.instance.collection("collectionName").doc(id).snapshots() which will give you a stream. you use a StreamBuilder (considering you are using provider, you should use stream provider) instead of FutureBuilder.
When the user starts my app the first time, he can toggle between the registration form and the sign in form. After completing one of this forms, he should see the loading screen until the user is created or signed in. In my case, unfortunately, the loading screen doesn't disappear, although the user is created.
How can I fix this problem?
the sign in form:
class SignIn extends StatefulWidget {
final Function toggleView;
SignIn({this.toggleView});
#override
_SignInState createState() => _SignInState();
}
class _SignInState extends State<SignIn> {
final AuthService _auth = AuthService();
final _formKey = GlobalKey<FormState>();
bool loading = false;
// text field state
String email = '';
String password = '';
String error = '';
static const color = const Color(0xFF2F80ED);
#override
Widget build(BuildContext context) {
return loading
? Loading()
: Scaffold(
backgroundColor: Colors.white,
resizeToAvoidBottomInset: false,
body: Stack(
children: <Widget>[
Container(
child: Positioned(
top: 0,
right: 0,
child: Image.asset(
'assets/canvas-1-ws.png',
),
),
),
Positioned(
top: 25.0,
left: 5.0,
child: IconButton(
icon: Icon(
Icons.close,
color: Colors.black,
size: 35.0,
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Welcome(),
),
);
},
),
),
Container(
child: Positioned(
bottom: 0,
left: 0,
child: Image.asset(
'assets/canvas-2-ws.png',
),
),
),
Center(
child: Stack(
children: <Widget>[
Padding(
padding: EdgeInsets.only(
left: 50.0,
right: 50.0,
),
child: Container(
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Login',
style: TextStyle(
color: Colors.black,
fontFamily: 'Roboto Black',
fontSize: 35.0,
),
),
SizedBox(
height: 30.0,
),
TextFormField(
style: TextStyle(
fontFamily: 'Roboto Light',
color: Colors.black,
),
cursorColor: Colors.black,
decoration: InputDecoration(
fillColor: Colors.black,
hintText: 'Email',
hintStyle: TextStyle(
fontFamily: 'Roboto Light',
color: Colors.black,
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black,
width: 1.0,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black,
width: 1.0,
),
),
),
validator: (val) => val.isEmpty
? 'Bitte gültige Email'
: null,
onChanged: (val) {
setState(() => email = val);
},
),
SizedBox(
height: 20.0,
),
TextFormField(
style: TextStyle(
fontFamily: 'Roboto Light',
color: Colors.black,
),
cursorColor: Colors.black,
decoration: InputDecoration(
fillColor: Colors.black,
hintText: 'Passwort',
hintStyle: TextStyle(
fontFamily: 'Roboto Light',
color: Colors.black,
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black,
width: 1.0,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.black,
width: 1.0,
),
),
),
obscureText: true,
validator: (val) => val.isEmpty
? 'Bitte gültiges Passwort'
: null,
onChanged: (val) {
setState(() => password = val);
}),
SizedBox(
height: 45.0,
),
ButtonTheme(
minWidth: 200,
height: 50,
child: RaisedButton(
elevation: 0,
color: color,
textColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30.0),
side: BorderSide(color: color),
),
child: Container(
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: color,
spreadRadius: 10.0,
blurRadius: 20.0,
offset: Offset(0, 3),
),
],
),
child: Text(
'Login',
style: TextStyle(
fontFamily: 'Roboto Light',
fontSize: 25.0),
),
),
onPressed: () async {
if (_formKey.currentState.validate()) {
setState(() => loading = true);
dynamic result = await _auth
.signInWithEmailAndPassword(
email, password);
if (result == null) {
setState(() {
error = 'Falsche Email/Password';
loading = false;
});
}
}
},
),
),
],
),
),
),
),
],
),
),
],
),
);
}
}
the registration form is almost similar...
the file, where the user can toggle between the forms:
class Welcome extends StatefulWidget {
final Function toggleView;
Welcome({this.toggleView});
#override
_WelcomeState createState() => _WelcomeState();
}
class _WelcomeState extends State<Welcome> {
static const color = const Color(0xFF2F80ED);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Stack(
children: <Widget>[
Positioned(
top: 0,
right: 0,
child: Image.asset('assets/canvas-1-ws.png'),
),
Positioned(
bottom: 0,
left: 0,
child: Image.asset('assets/canvas-2-ws.png'),
),
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset(
'assets/Skiclublogo_transparent.png',
scale: 4,
),
SizedBox(
height: 40.0,
),
ButtonTheme(
minWidth: 200,
height: 50,
child: RaisedButton(
elevation: 0,
color: color,
textColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30.0),
side: BorderSide(color: color),
),
child: Container(
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: color,
spreadRadius: 10.0,
blurRadius: 20.0,
offset: Offset(0, 3),
),
],
),
child: Text(
'Registrieren',
style: TextStyle(
fontFamily: 'Roboto Light', fontSize: 25.0),
),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Register(),
),
);
}),
),
SizedBox(
height: 40.0,
),
ButtonTheme(
minWidth: 200,
height: 50,
child: RaisedButton(
elevation: 0,
color: color,
textColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30.0),
side: BorderSide(color: color),
),
child: Container(
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: color,
spreadRadius: 10.0,
blurRadius: 20.0,
offset: Offset(0, 3),
),
],
),
child: Text(
'Login',
style: TextStyle(
fontFamily: 'Roboto Light', fontSize: 25.0),
),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SignIn(),
),
);
}),
),
],
),
),
],
),
);
}
}
the auth file, where the user is created or logged in:
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
// create user obj based on FirrebaseUser
User _userFromFirebaseUser(FirebaseUser user) {
return user != null ? User(uid: user.uid) : null;
}
// auth change user stream
Stream<User> get user {
return _auth.onAuthStateChanged.map(_userFromFirebaseUser);
}
// sign in anonmously
Future signInAnon() async {
try {
AuthResult result = await _auth.signInAnonymously();
FirebaseUser user = result.user;
// create a new document for the user with the uid
await DatabaseService(uid: user.uid).updateUserData(
user.displayName, user.email, user.photoUrl, user.uid);
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
// sign in email & password
Future signInWithEmailAndPassword(String email, String password) async {
try {
AuthResult result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
FirebaseUser user = result.user;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
// register with email & password
Future registerWithEmailAndPassword(
String email, String password, String name) async {
try {
AuthResult result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
FirebaseUser user = result.user;
// update user info
UserUpdateInfo userUpdateInfo = UserUpdateInfo();
userUpdateInfo.displayName = name;
await user.updateProfile(userUpdateInfo);
await user.reload();
user = await FirebaseAuth.instance.currentUser();
// create a new document for the user with the uid
await DatabaseService(uid: user.uid).updateUserData(
user.displayName, user.email, user.photoUrl, user.uid);
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
// sign out
Future signOut() async {
try {
return await _auth.signOut();
} catch (e) {
print(e.toString());
return null;
}
}
// current user
void inputData() async {
final FirebaseUser user = await _auth.currentUser();
final uid = user.uid;
// here you write the codes to input the data into firestore
}
}
the wrapper file, where the app checks if the user is new:
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
print(user);
// return either Home or Authenticate widget
if (user == null) {
return Welcome();
} else {
return DrawerNav();
}
}
}
the loading screen file:
class Loading extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Center(
child: SpinKitWave(
color: Colors.black,
size: 50.0,
),
),
);
}
}
Thank you very much when you've read through all of this!
You don't always call setState after the user is created.
This will work
onPressed: (){ if (_formKey.currentState.validate()) {
setState(() => loading = true);
dynamic result = await _auth
.signInWithEmailAndPassword(
email, password);
setState(() => loading = false);
if (result == null) {
setState(() {
error = 'Falsche Email/Password';
});
}
}},
Ill explain you my friend
set a boolean variable named loading
loading = false;
if loading is true display loading screen else display your form ui using ternary operator
while creating user change the state of loading so that loading screen will appear
setState(() { loading=true; });
after user is created change the state of loading again to false
setState(() { loading=false; });
This is the template in build function
loading==false?yourui():loading()
when i try to press on the send button to do the http request[which is done successflly] i got the below error, how can i solve it?!!
[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: setState() called after dispose(): _ForgetPasswordDialogState#95548(lifecycle state: defunct, not mounted
import 'dart:io';
import 'package:Zabatnee/common_app/provider/user_details_provider.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class ForgetPasswordDialog extends StatefulWidget {
static const routeName = '/customeDialog';
final String title,description;
ForgetPasswordDialog({
this.title,
this.description,});
#override
_ForgetPasswordDialogState createState() => _ForgetPasswordDialogState();
}
class _ForgetPasswordDialogState extends State<ForgetPasswordDialog> {
final emailController = TextEditingController();
var _isLoading = false;
_showDialog(String title, String message) {
showDialog(
barrierDismissible: false,
context: context,
builder: (ctx) => WillPopScope(
onWillPop: () async => false,
child: new AlertDialog(
elevation: 15,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8))),
title: Text(
title,
style: TextStyle(color: Colors.white),
),
content: Text(
message,
style: TextStyle(color: Colors.white),
),
backgroundColor: Theme.of(context).primaryColor,
actions: <Widget>[
FlatButton(
child: Text(
'OK',
style: TextStyle(color: Theme.of(context).accentColor),
),
onPressed: () {
Navigator.of(context).pop();
setState(
() {
_isLoading = false;
},
);
},
)
],
),
),
);
}
Future<void> _forgetPassword (String email) async {
try{
setState(() {
_isLoading = true;
});
await Provider.of<UserDetailsProvider>(context, listen: false).forgetPassword(email);
print('code are sent via email');
} on HttpException catch (error) {
_showDialog('Authentication Failed', error.message);
} on SocketException catch (_) {
_showDialog('An error occured',
'please check your internet connection and try again later');
} catch (error) {
_showDialog('Authentication Failed',
'Something went wrong, please try again later');
}
setState(() {
_isLoading = false;
});
}
#override
void dispose() {
emailController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16)
),
elevation: 8,
backgroundColor: Theme.of(context).accentColor,
child: dialogContent(context),
);
}
dialogContent(BuildContext context){
return Container(
padding: EdgeInsets.only(
top: 50,
right: 16,
left: 16,
bottom: 16,
),
margin: EdgeInsets.all(8),
width: double.infinity,
decoration: BoxDecoration(
color: Theme.of(context).accentColor,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(17),
boxShadow: [
BoxShadow(
color: Colors.black87,
blurRadius: 10.0,
offset: Offset(0.0,10.0),
)
]
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
widget.title,
style: TextStyle(
fontSize: 24,
),
),
SizedBox(
height: 20,
),
Text(widget.description, style: TextStyle(fontSize: 16),
),
SizedBox(
height: 20,
),
TextField(
controller: emailController,
style: TextStyle(color: Colors.white),
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
border: OutlineInputBorder(
borderSide: BorderSide(color:Theme.of(context).primaryColor)
),
fillColor: Colors.black87,
hintStyle: TextStyle(color:Colors.grey),
hintText: 'please enter your email',
labelText: 'Email',
labelStyle: TextStyle(color:Colors.white)
),
),
SizedBox(
height: 20,
),
Container(
width: double.infinity,
child: RaisedButton(
child: Text('Send'),
onPressed: (){
_isLoading
? CircularProgressIndicator()
: print(emailController.text);
_forgetPassword(emailController.text);
Navigator.of(context).pop();
}
),
),
Container(
width: double.infinity,
child: RaisedButton(
child: Text('Cancel'),
onPressed: (){
Navigator.of(context).pop();
}
),
),
],
),
);
}
}
Replace your
setState((){});
with
if (mounted) setState(() {});.
The mounted checks Whether the [State] object is currently in a tree. That way, you would be able to avoid the error.
thanks to Peter Haddad
We have built code to retrieve data from my cloud Store collection called "user"
at the moment as you can see into the widget we will show the use with a specific email "info#text.it"
class MainWelcome extends StatefulWidget {
#override
_MainWelcomeState createState() => _MainWelcomeState();
}
class _MainWelcomeState extends State<MainWelcome> {
final databaseReference = Firestore.instance;
Future<QuerySnapshot> getData() async {
return await Firestore.instance.collection("user").where("email", isEqualTo: "info#text.it").getDocuments();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getData(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.documents.length,
itemBuilder: (BuildContext context, int index) {
return Column(
children: [
Text(snapshot.data.documents[index].data["name"]),
Text(snapshot.data.documents[index].data["food"]),
Text(snapshot.data.documents[index].data["email"]),
],
);
});
} else if (snapshot.connectionState == ConnectionState.none) {
return Text("No data");
}
return CircularProgressIndicator();
},
);
}
}
where isEqualTo "info#text.it" I need to replace it with a variable to get into the widget each of the user subscribed into my form.
this is the form:
void createRecord() async {
await databaseReference.collection("user")
.add({
'name': '$name',
'email': '$email',
'password': '$password',
'food': '$food',
'image': '$image',
}).then((value){
print(value.documentID);
});
Widget mailField() {
return Center(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: Container(
width: 360,
child: TextField(
style: TextStyle(color: Colors.white),
keyboardType: TextInputType.emailAddress,
onChanged: (value) {
email = value;
},
decoration: InputDecoration(
icon: new Icon(
Icons.mail,
color: Colors.white,
),
hintText: 'La tua email',
focusColor: Colors.white,
hintStyle: TextStyle(color: Colors.white),
prefixStyle: TextStyle(color: Colors.white),
labelText: 'La tua email',
labelStyle: TextStyle(
color: Colors.white,
),
contentPadding:
EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.white, width: 1.0),
borderRadius: BorderRadius.all(Radius.circular(5)),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.white, width: 1.0),
borderRadius: BorderRadius.all(Radius.circular(5.0)),
),
),
),
),
),
);
}
Widget submitButton() {
return Container(
child: SizedBox(
width: 220,
height: 40,
child: RaisedButton(
elevation: 5,
onPressed: () async {
setState(() {
showProgress = true;
createRecord();
});
try {
final newuser = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
if (newuser != null) {
Navigator.push(context, PageTransition(
type: PageTransitionType.fade,
child: WelcomeScreen(),
));
setState(() {
showProgress = false;
});
}
} catch (e) {
return Container(
child: Text('Error'),
);
}
},
child: Text(
'Registrati',
style: TextStyle(color: Colors.black),
),
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Colors.black12, width: 1)),
),
),
);
}
Since you are using Firebase Authentication, then get the email from there:
Future<QuerySnapshot> getData() async {
var firebaseUser = await FirebaseAuth.instance.currentUser();
return await Firestore.instance.collection("user").where("email", isEqualTo: firebaseUser.email).getDocuments();
}