How to handle user authentication state in flutter - firebase

I'm having this very strange behaviour with firebase and flutter.
My goal is show a screen when the users are logged in, and a different one where they can sign in.
See my main function below
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: Size(360, 690),
allowFontScaling: false,
builder: () => MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.red,
),
home: StreamBuilder(
stream: FirebaseAuth.instance.userChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
return FirebaseAuth.instance.currentUser == null
? StartupPage()
: AppHome();
}
return Text("");
}),
),
);
}
}
The login screen:
class LoginPage extends HookWidget {
final GlobalKey<FormState> _key = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
final _emailController =
useTextEditingController.fromValue(TextEditingValue.empty);
final _passwordController =
useTextEditingController.fromValue(TextEditingValue.empty);
return Scaffold(
backgroundColor: Color(0xFFA46EE2),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
child: Image.asset(
"assets/images/LOGO.png",
height: 60.h,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _key,
child: Column(
children: [
Text(
"Welcome back!",
style: primaryHeadline,
),
Padding(
padding: const EdgeInsets.all(1.0),
child: CustomTextField(
hintText: "Email",
secured: false,
isEmail: false,
onSave: (data) {},
onValidate: (value) => validator(value, "Email"),
controller: _emailController,
),
),
Padding(
padding: const EdgeInsets.all(1.0),
child: CustomTextField(
hintText: "Password",
secured: true,
isEmail: false,
onSave: (data) {},
onValidate: (value) => validator(value, "Password"),
controller: _passwordController,
),
),
SizedBox(height: 10),
InkWell(
onTap: () async {
if (_key.currentState.validate()) {
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: _emailController.text,
password: _passwordController.text,
);
// Navigator.of(context).pushReplacement(
// MaterialPageRoute(
// fullscreenDialog: true,
// builder: (context) => AppHome(),
// ),
// );
}
},
child: Container(
height: 40.h,
decoration: BoxDecoration(
color: defaultBackgroundColor,
borderRadius: BorderRadius.circular(defaultRadius)),
alignment: Alignment.center,
child: Text(
'Log in',
style: defaultWhiteText,
),
),
)
],
),
),
),
],
),
);
}
}
The register screen
class RegisterPage extends HookWidget {
final GlobalKey<FormState> _key = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
final _emailController =
useTextEditingController.fromValue(TextEditingValue.empty);
final _passwordController =
useTextEditingController.fromValue(TextEditingValue.empty);
return Scaffold(
backgroundColor: Color(0xFFF5EFFC),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
child: Image.asset(
"assets/images/ELEMENT_02.png",
height: 60.h,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _key,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(1.0),
child: CustomTextField(
hintText: "Email",
secured: false,
isEmail: false,
onSave: (data) {},
onValidate: (value) => validator(value, "Email"),
controller: _emailController,
),
),
Padding(
padding: const EdgeInsets.all(1.0),
child: CustomTextField(
hintText: "Password",
secured: true,
isEmail: false,
onSave: (data) {},
onValidate: (value) => validator(value, "Password"),
controller: _passwordController,
),
),
SizedBox(height: 10),
InkWell(
onTap: () async {
if (_key.currentState.validate()) {
await FirebaseAuth.instance
.createUserWithEmailAndPassword(
email: _emailController.text,
password: _passwordController.text,
);
}
},
child: Container(
height: 40.h,
decoration: BoxDecoration(
color: defaultBackgroundColor,
borderRadius: BorderRadius.circular(defaultRadius)),
alignment: Alignment.center,
child: Text(
'Signup now',
style: defaultWhiteText,
),
),
)
],
),
),
),
],
),
);
}
}
I'm having this behaviour:
when I register with email and password, then sign out, the StartupPage is displayed as expected.
When I sign in, then sign out, the AppHome is not displayed.
What did I do wrong?

When you signOut, currentUser becomes null.
FirebaseAuth.instance.currentUser == null
? StartupPage()
: AppHome();
This will return StartupPage() after signing out.
So your code is working fine I guess.

Related

showing all users images in listview with firebase

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

How can I update note NoteScreen widget when I add an item in AddNote widget?

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.

how to create a countdown timer in flutter for phone number authentication

I am new to flutter. I am having a flutter and firebase authentication system where users will input their phone number and an otp will be sent to them. I then want to implement a functionality whereby there will be a countdown shown for the otp timeout, so that a users can resend an otp again after timeout.
Currently my code is sending otp to users perfectly, what i want to implement is how to show a timer countdown for the otp timeout.
This is my code
LoginScreen.dart
class LoginScreen extends StatefulWidget {
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
TextEditingController _controller = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Phone Auth'),
),
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(children: [
Container(
margin: EdgeInsets.only(top: 60),
child: Center(
child: Text(
'Phone Authentication',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 28),
),
),
),
Container(
margin: EdgeInsets.only(top: 40, right: 10, left: 10),
child: TextField(
decoration: InputDecoration(
hintText: 'Phone Number',
prefix: Padding(
padding: EdgeInsets.all(4),
child: Text('+234'),
),
),
maxLength: 10,
keyboardType: TextInputType.number,
controller: _controller,
),
)
]),
Container(
margin: EdgeInsets.all(10),
width: double.infinity,
child: FlatButton(
color: Colors.blue,
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => OTPScreen(_controller.text)));
},
child: Text(
'Next',
style: TextStyle(color: Colors.white),
),
),
)
],
),
),
);
}
}
OTP process Screen
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:pinput/pin_put/pin_put.dart';
class OTPScreen extends StatefulWidget {
final String phoneNumber;
OTPScreen({#required this.phoneNumber});
#override
_OTPScreenState createState() => _OTPScreenState();
}
class _OTPScreenState extends State<OTPScreen> {
final GlobalKey<ScaffoldState> _scaffoldkey = GlobalKey<ScaffoldState>();
String _verificationCode;
final TextEditingController _pinPutController = TextEditingController();
final FocusNode _pinPutFocusNode = FocusNode();
final BoxDecoration pinPutDecoration = BoxDecoration(
color: const Color.fromRGBO(43, 46, 66, 1),
borderRadius: BorderRadius.circular(10.0),
border: Border.all(
color: const Color.fromRGBO(126, 203, 224, 1),
),
);
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldkey,
appBar: AppBar(
title: Text(
'OTP Verification',
style: TextStyle(fontWeight: FontWeight.normal),
),
),
body: SingleChildScrollView(
child: Column(
children: [
Container(
margin: EdgeInsets.only(top: 35),
child: Column(
children: [
Text(
"Code is sent to " + widget.phoneNumber,
style: TextStyle(
fontSize: 20,
color: Color(0xFF818181),
),
),
SizedBox(
height: 20,
),
Text(
'Enter your 6-digit code',
style: TextStyle(fontSize: 20),
),
],
),
),
Padding(
padding: const EdgeInsets.all(30.0),
child: PinPut(
fieldsCount: 6,
textStyle: const TextStyle(fontSize: 25.0, color: Colors.white),
eachFieldWidth: 40.0,
eachFieldHeight: 55.0,
focusNode: _pinPutFocusNode,
controller: _pinPutController,
submittedFieldDecoration: pinPutDecoration,
selectedFieldDecoration: pinPutDecoration,
followingFieldDecoration: pinPutDecoration,
pinAnimationType: PinAnimationType.fade,
onSubmit: (pin) async {
try {
await FirebaseAuth.instance
.signInWithCredential(PhoneAuthProvider.credential(
verificationId: _verificationCode, smsCode: pin))
.then((value) async {
if (value.user != null) {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => Home()),
(route) => false);
}
});
} catch (e) {
FocusScope.of(context).unfocus();
_scaffoldkey.currentState
.showSnackBar(SnackBar(content: Text('invalid OTP')));
}
},
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: () {
//Check if timeout has elaspsed, if true then enable button for user to click
},
child: Text(
'Resend Code',
style: TextStyle(color: Color(0xFF81C784)),
)),
InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SetupProfile()));
},
borderRadius: BorderRadius.circular(33),
child: Image.asset(
'assets/images/forward.png',
height: 50,
width: 50,
),
),
],
),
),
],
),
),
);
}
_verifyPhone() async {
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: '+234${widget.phoneNumber}',
verificationCompleted: (PhoneAuthCredential credential) async {
await FirebaseAuth.instance
.signInWithCredential(credential)
.then((value) async {
if (value.user != null) {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => Home()),
(route) => false);
}
});
},
verificationFailed: (FirebaseAuthException e) {
print(e.message);
},
codeSent: (String verficationID, int resendToken) {
setState(() {
_verificationCode = verficationID;
});
},
codeAutoRetrievalTimeout: (String verificationID) {
setState(() {
_verificationCode = verificationID;
});
},
timeout: Duration(seconds: 120));
}
#override
void initState() {
// TODO: implement initState
super.initState();
_verifyPhone();
}
}

Problems with using a URL in NetworkImage (flutter)

I am trying to pass a URL that I receive from Firebase to my NetworkImage widget on the AppBar. However, my code keeps passing a null value to my NetworkImage widget. How can I pass the URL properly to the widget.
class DashboardScreen extends StatefulWidget {
#override
_DashboardScreenState createState() => _DashboardScreenState();
}
class _DashboardScreenState extends State<DashboardScreen> {
Future inputData() async {
final FirebaseAuth auth = FirebaseAuth.instance;
final FirebaseUser user = await auth.currentUser();
final uid = user.uid;
DocumentReference _stock = Firestore.instance.document('users/$uid');
DocumentSnapshot snapshot = await _stock.get();
Map<String, dynamic> data = snapshot.data;
return data['image_url'];
}
String myAvatarUrl;
void converter() {
inputData().then((result) {
myAvatarUrl = result;
print(myAvatarUrl);
});
}
#override
Widget build(BuildContext context) {
converter();
return Scaffold(
appBar: AppBar(
leading: Padding(
padding: EdgeInsets.only(top: 12.0, bottom: 12.0, left: 13.0),
child: CircleAvatar(
backgroundImage: NetworkImage(myAvatarUrl),
child: FlatButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SearchsettingsScreen(),
),
);
},
),
backgroundColor: Colors.white,
),
),
centerTitle: true,
title: Text(
'My Dashboard',
),
backgroundColor: Theme.of(context).primaryColor,
automaticallyImplyLeading: false,
actions: [
DropdownButton(
underline: Container(),
icon: Icon(
Icons.more_vert,
color: Theme.of(context).primaryIconTheme.color,
),
items: [
DropdownMenuItem(
child: Container(
child: Row(
children: <Widget>[
Icon(
Icons.exit_to_app,
),
SizedBox(
width: 8.0,
),
Text('Logout'),
],
),
),
value: 'logout',
),
],
onChanged: (itemIdentifier) {
if (itemIdentifier == 'logout') {
FirebaseAuth.instance.signOut();
}
},
),
],
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ReusableDashboardSettingCard(),
ReusableDashboardContactsCard(),
],
),
);
}
}
This is the error I get on my console:
The following assertion was thrown building DashboardScreen(dirty, state: _DashboardScreenState#e1615):
'package:flutter/src/painting/_network_image_io.dart': Failed assertion: line 26 pos 14: 'url != null': is not true.
Your main errors are:
you are not managing that there is the possibility that myAvatarUrl can be null
you are not telling to Flutter to rebuild your widget when data will arrive using setState((){})
I suggest you take a look at Codelabs and Fetch data from the internet.
I've not tested it but it should work
import 'package:flutter/material.dart';
class DashboardScreen extends StatefulWidget {
#override
_DashboardScreenState createState() => _DashboardScreenState();
}
class _DashboardScreenState extends State<DashboardScreen> {
String myAvatarUrl;
Future inputData() async {
final FirebaseAuth auth = FirebaseAuth.instance;
final FirebaseUser user = await auth.currentUser();
final uid = user.uid;
DocumentReference _stock = Firestore.instance.document('users/$uid');
DocumentSnapshot snapshot = await _stock.get();
Map<String, dynamic> data = snapshot.data;
return data['image_url'];
}
#override
void initState() {
super.initState();
// When data will arrive
inputData().then((value) {
setState(() => myAvatarUrl = value);
print(myAvatarUrl);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: myAvatarUrl == null
? const SizedBox()
: Padding(
padding: EdgeInsets.only(top: 12.0, bottom: 12.0, left: 13.0),
child: CircleAvatar(
backgroundImage: NetworkImage(myAvatarUrl),
child: FlatButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SearchsettingsScreen(),
),
);
},
),
backgroundColor: Colors.white,
),
),
centerTitle: true,
title: Text(
'My Dashboard',
),
backgroundColor: Theme.of(context).primaryColor,
automaticallyImplyLeading: false,
actions: [
DropdownButton(
underline: Container(),
icon: Icon(
Icons.more_vert,
color: Theme.of(context).primaryIconTheme.color,
),
items: [
DropdownMenuItem(
child: Container(
child: Row(
children: <Widget>[
Icon(
Icons.exit_to_app,
),
SizedBox(
width: 8.0,
),
Text('Logout'),
],
),
),
value: 'logout',
),
],
onChanged: (itemIdentifier) {
if (itemIdentifier == 'logout') {
FirebaseAuth.instance.signOut();
}
},
),
],
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ReusableDashboardSettingCard(),
ReusableDashboardContactsCard(),
],
),
);
}
}

Flutter stay logged in with phone number

How can I register with a phone number and stay logged in until I log out. I'm using flutter and I already can register a user with a phone number but when the app is re-opened it starts at the login page.
The idea is to stay logged in after sign-up and even after app closed. and I also can't make circular Indicator to show that the app is loading (I would love it if you showed me how to do this as well)
Here is my code.
import 'package:country_code_picker/country_code_picker.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'BottomBarPages/home.dart';
class SignIn extends StatefulWidget {
#override
_SignInState createState() => _SignInState();
}
class _SignInState extends State<SignIn> {
TextEditingController phoneController = new TextEditingController();
String phoneNumber = "";
bool shower = false;
String smsCode;
String verificationCode;
void _onCountryChange(CountryCode countryCode) {
this.phoneNumber = countryCode.toString();
print("New Country selected: " + countryCode.toString());
}
void check() {
final myPhone = this.phoneNumber + phoneController.text;
print("Full Text: " + myPhone);
}
Future<void> man() async{
}
Future<void> submit() async {
final myPhone = this.phoneNumber + phoneController.text;
final PhoneVerificationCompleted verificationCompleted =
(AuthCredential credential) {
setState(() {
print(credential);
});
};
final PhoneVerificationFailed verificationFailed =
(AuthException exception) {};
final PhoneCodeSent phoneCodeSent = (String verId, [int forceCodeResend]) {
this.verificationCode = verId;
smsCodeDialog(context).then((value) => print("signed"));
};
final PhoneCodeAutoRetrievalTimeout autoRetrievalTimeout = (String verId) {
this.verificationCode = verId;
};
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: myPhone,
timeout: const Duration(seconds: 5),
verificationCompleted: verificationCompleted,
verificationFailed: verificationFailed,
codeSent: phoneCodeSent,
codeAutoRetrievalTimeout: autoRetrievalTimeout);
}
Future<bool> smsCodeDialog(BuildContext context) {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
'Enter Code',
style: TextStyle(color: Colors.lightGreen, fontSize: 24),
),
content: TextField(
keyboardType: TextInputType.number,
onChanged: (Value) {
smsCode = Value;
},
),
contentPadding: EdgeInsets.all(10),
actions: [
FlatButton(
onPressed: () {
FirebaseAuth.instance.currentUser().then((user) {
if (user != null) {
Navigator.of(context).pop();
Navigator.push(context,
MaterialPageRoute(builder: (context) => Home()));
} else {
Navigator.of(context).pop();
signIn();
}
});
},
child: Text(
'Verify',
style: TextStyle(fontSize: 20, color: Colors.lightGreen),
))
],
);
});
// CircularProgressIndicator(
// valueColor: new AlwaysStoppedAnimation<Color>(Colors.lightGreen),
// value: 0.25,
// );
}
signIn() {
AuthCredential phoneAuthCredential = PhoneAuthProvider.getCredential(
verificationId: verificationCode, smsCode: smsCode);
FirebaseAuth.instance
.signInWithCredential(phoneAuthCredential)
.then((user) => Navigator.push(
context, MaterialPageRoute(builder: (context) => Home())))
.catchError((e) => print(e));
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
},
child: Scaffold(
body: Column(
//mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(
flex: 1,
child: Container(
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
stops: [0.1, 0.3, 1.0],
colors: [
Colors.lightGreen[300],
Colors.white,
Colors.lightGreen[50]
],
),
),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 15),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
height: 150,
child: Image.asset('images/phone.png'),
),
SizedBox(
height: 20,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50),
border: Border.all(color: Colors.lightGreen)),
width: double.infinity,
height: 40,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Row(
// crossAxisAlignment: CrossAxisAlignment.start,
//mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CountryCodePicker(
dialogTextStyle: TextStyle(fontSize: 20),
onChanged: _onCountryChange,
initialSelection: 'US',
favorite: ['+251', 'ET'],
),
Padding(
padding: const EdgeInsets.only(bottom: 10),
child: SizedBox(
width: 150,
child: TextFormField(
controller: phoneController,
keyboardType: TextInputType.phone,
decoration: InputDecoration(
border: InputBorder.none,
),
),
),
),
],
),
),
),
),
SizedBox(
height: 20,
),
MaterialButton(
onPressed: submit,
minWidth: MediaQuery.of(context).size.width - 80,
height: 45,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
color: Colors.lightGreen,
splashColor: Colors.green,
child: Text(
"Confirm",
style: TextStyle(color: Colors.white, fontSize: 18),
),
),
Padding(
padding:
EdgeInsets.symmetric(vertical: 14, horizontal: 64),
child: Text(
"you'll receive a 6 digit code click Confirm to verify",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22,
color: Colors.lightGreen,
),
),
),
],
),
),
),
),
],
),
),
);
}
}
Please help me.
When your application opens, check if the user is already logged in or not using:
User user = FirebaseAuth.instance.currentUser;
if user is not null then you should navigate the app to the home or some other screen. Try this code:
#override
void initState() {
super.initState();
getUser();
}
getUser() {
User firebaseUser = FirebaseAuth.instance.currentUser;
if (firebaseUser != null)
WidgetsBinding.instance.addPostFrameCallback((_) =>
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => HomePage())));
}

Resources