TextEditingController doesn't clear the text after .clear() - firebase

So im trying to clear the textform after sending a message and if the message is empty the send button should be disabled , what i'm having is the onPressed for the button is never null , it's never disabled , and the text is cleared but if i send again it sends the previous text before clearing if that makes sense
so if i send Hello , it shows as empty but the button still active , if i resend again without entering anything it sends the same cleared text , any idea what i could be doing wrong ?
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class NewMessage extends StatefulWidget {
#override
_NewMessageState createState() => _NewMessageState();
}
class _NewMessageState extends State<NewMessage> {
final _controller = TextEditingController();
String _enteredMessage = '';
void _sendMessage() async {
setState(() {
_controller.clear();
});
FocusScope.of(context).unfocus();
final user = FirebaseAuth.instance.currentUser;
final userData = await FirebaseFirestore.instance
.collection('users')
.doc(user.uid)
.get();
FirebaseFirestore.instance.collection('chat').add({
'text': _enteredMessage,
'createdAt': Timestamp.now(),
'userId': user.uid,
'username': userData['username'],
});
}
void sayHello() {
print('hello');
}
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(top: 8),
padding: EdgeInsets.all(8),
child: Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: InputDecoration(labelText: 'Send a message...'),
onChanged: (value) {
setState(() {
_enteredMessage = value;
});
},
),
),
IconButton(
icon: Icon(Icons.send, color: Theme.of(context).primaryColor),
onPressed: _enteredMessage.trim().isEmpty ? null : _sendMessage,
)
],
),
);
}
}

Problem
The reason you are having the error is that you are storing in the input in the enteredText variable but are clearing the controller's text, both of which are different. Which is why your enteredText still holds and send the same string after clearing.
Solution
You don't have to use a separate variable to hold your input text if you have given a controller. The controller already holds the value of the input. Make the following changes:
IconButton(
...,
onPressed: (){
_controller.text = _controller.text.trim();
if(_controller.text.isNotEmpty) {
_sendMessage();
}
}
)
Use the text from _controller with _controller.text
void _sendMessage() async {
FocusScope.of(context).unfocus();
final user = FirebaseAuth.instance.currentUser;
final userData = await FirebaseFirestore.instance
.collection('users')
.doc(user.uid)
.get();
FirebaseFirestore.instance.collection('chat').add({
'text': _controller.text, //<-- Update here
'createdAt': Timestamp.now(),
'userId': user.uid,
'username': userData['username'],
});
setState(() { //<-- Clear at the end
_controller.clear();
});
}
Finally
You can now get rid of the enteredText variable and the onChange callback.

Related

How to pass data between classes? Flutter

I have a class which I get email data from the user. It's basicly a textfield so I wont post the textfield code. After I get the email and password data, I check if these data is in my Firestore database. If it is, I want to pass this email data to my other class which I get the other informations about customer.
Here is the code of trying to check if user exists when I click the button.This is my LoginScreen() class' Material Button's onPressed action. I only get the email from the user here and want to pass this email data to my CustomerInfo class if the user already exists in my FirebaseAuth.Also the CustomerScreen class is for the show all customer information on the screen. I will add some code to push the customer information after I successfully get the data form database.
try {
final user = await _auth.signInWithEmailAndPassword(
email: email!, password: password!);
if (user != null) {
CustomerInfo(email: email);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CustomerScreen()));
}
} catch (e) {
print(e);
}
Also I have another class which I'm trying to get customers data.
class CustomerInfo {
String? email;
String? name;
String? surname;
String? avatarLink;
int? balance;
CollectionReference customers =
FirebaseFirestore.instance.collection('customers');
CustomerInfo({this.email});
Future getCustomerData() async {
print("Email: $email");
await customers.where('email', isEqualTo: email).get().then((value) {
value.docs.forEach((result) {
name = result['name'];
surname = result['surname'];
balance = result['balance'];
avatarLink = result['image'];
});
});
}
}
Also CustomerInfo class:
import 'package:banking_app_firebase/constants.dart';
import 'package:banking_app_firebase/networking.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'login_screen.dart';
class CustomerScreen extends StatefulWidget {
const CustomerScreen({Key? key}) : super(key: key);
#override
_CustomerScreenState createState() => _CustomerScreenState();
}
class _CustomerScreenState extends State<CustomerScreen> {
String? name;
String? surname;
int? balance;
String? image;
void getData() async {
CustomerInfo customerInfo = CustomerInfo();
await customerInfo.getCustomerData();
name = customerInfo.name;
print("Name: $name");
}
#override
void initState() {
// TODO: implement initState
super.initState();
getData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
iconTheme: IconThemeData(
color: Colors.black,
),
title: Text(
"Account Summary",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
actions: [
Container(
padding: EdgeInsets.only(right: 10),
child: Row(
children: [
Icon(
CupertinoIcons.bell,
color: Colors.black,
),
SizedBox(
width: 10,
),
CircleAvatar(
backgroundImage: NetworkImage(
"https://cdn.pixabay.com/photo/2016/03/23/04/01/woman-1274056_960_720.jpg"),
),
],
),
),
],
elevation: 0,
),
body: Container(
child: Column(
children: [
Align(
alignment: Alignment.topLeft,
child: Text(
"Deneme",
style: kInfoTextDecoration,
),
),
Align(
alignment: Alignment.topLeft,
child: Text(
"Deneme",
style: kInfoTextDecoration,
),
),
],
),
),
);
}
}
How can I pass the email data to my CustomerInfo class so I can use getCustomerData()? Using CustomerInfo(email:email) did not work.
I am sorry, I think I misunderstood your question.
Is this what you are trying to do? You are passing email correctly. You just need to call getCustomerData()
try {
final user = await _auth.signInWithEmailAndPassword(
email: email!, password: password!);
if (user != null) {
var customerInfo = CustomerInfo(email: email);
await customerInfo.getCustomerData();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CustomerScreen(customerInfo: customerInfo)));
}
} catch (e) {
print(e);
}
Then in CustomerScreen it would look like this
class CustomerScreen extends StatefulWidget {
final CustomerInfo customerInfo;
const CustomerScreen({Key? key, this.customerInfo}) : super(key: key);
#override
_CustomerScreenState createState() => _CustomerScreenState();
}
class _CustomerScreenState extends State<CustomerScreen> {
String? name;
String? surname;
int? balance;
String? image;
void getData() {
name = widget.customerInfo.name;
surname = widget.customerInfo.surname;
balance = widget.customerInfo.balance;
image = widget.customerInfo.image;
}
But I would recommend you take a look into FutureBuilder.
this will not work as the email field is not static so you can't use CustomerInfo.getCustomerData(). You need to create an instance of custom user and then use the getCustomerData() method.
For example: var data = await CustomerInfo(email: email).getCustomerData();
or if you are going to use the CustomerInfo()` multiple times you can do the following:
final CustomerInfo info = CustomerInfo(email: email);
var data = info.getCustomerData();
or something similar to that.
If you are trying to display the info inside of the CustomScreen() I think it might be easier to just create a CustomerInfo() there and get whatever data you need.
All I can do is guess as you haven't provided a sample for CustomScreen() or what it is meant to do. If you are trying to the customer data in the CustomScreen() then it might be better to use a FutureBuilder() to show your user that the data is loading then display it once the future completes rather than just having an async callback before pushing the route making the user think that they didn't click the button.
It depends on your preferences. For example, usually, I create a global.dart and store temporary variables there. for example in your global.dart:
CustomerInfo selectedUser;
then you can set it wherever you want. for example:
import './global.dart' as global;
try {
final user = await _auth.signInWithEmailAndPassword(
email: email!, password: password!);
if (user != null) {
CustomerInfo(email: email);
global.selectedUser = CustomeInfo(SET PARAMETERS);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CustomerScreen()));
}
} catch (e) {
print(e);
}
the you can use it by calling global.selectedUser wherever you want.
Other approach is using Provider but it is useful when you want to change the variable in many files.
Also, you can use firebase methods for getting active user and his/her info like getEmail() but I do not recommend it because of unnecessary API call.
Get the email info from _auth.
try {
final user = await _auth!.signInWithEmailAndPassword(
email: email!, password: password!);
if (user.user !=null) { // added user.user
CustomerInfo(email: user.user!.email); //added user.user!.email
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CustomerScreen()));
}
} catch (e) {
print(e);
}

Why calling an async function whose defination is given in another program returns null or Future<type> instance for the first time as output?

Hello Im very to the flutter framework, so please let me know if im going wrong anywhere and the appropriate way of doing the things.
this is a drawerPage.dar file
In this file im trying to call a function getData for retrieving the data from firebase,this fucntion is in Database.dart file.
Database.dart
In the Database.dart file i wrote the getData function inside which im retrieving a particular record from the firebase and storing in a global variable. And then im trying to print the global variable in the drawerPage.dart file.But here when ever i run the program, for the first time the variable is having a null value and upon hot reload the actual value is getting stored in the variable.Please let me know how can i get rid of this problem.
output
drawerPageOutput
drawerPage.dart
import 'package:attendee/constants.dart';
import 'package:attendee/models/userdeails.dart';
import 'package:attendee/pages/profile.dart';
import 'package:attendee/services/authentication_service.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:attendee/services/database.dart';
import 'package:provider/provider.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:attendee/pages/userdetails.dart';
class StudentDashboard extends StatefulWidget {
#override
_StudentDashboardState createState() => _StudentDashboardState();
}
class _StudentDashboardState extends State<StudentDashboard> {
userdetails userdetail;
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
final AuthenticationService _auth = AuthenticationService();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
DatabaseService().getData('email');
final drawerHeader = UserAccountsDrawerHeader(
accountName: Text(userName),
accountEmail: Text('${result}'),
currentAccountPicture
: CircleAvatar(
child: FlutterLogo(size: 42.0),
backgroundColor: Colors.white,
);
final drawerItems = ListView(
children: <Widget>[
drawerHeader,
ListTile(
title: Row(
children: <Widget>[
Icon(Icons.perm_identity_outlined),
Text(' Profile'),
],
),
onTap: () => Navigator.of(context).push(MaterialPageRoute(builder: (context)=>Profile())),
),
ListTile(
title: Text('To page 2'),
onTap: () => Navigator.of(context).push(_NewPage(2)),
),
ListTile(
title:Row(
children: <Widget>[
Icon(Icons.exit_to_app_rounded),
Text(' Logout'),
],
),
onTap: () async {
await _auth.signOut();
Navigator.of(context).pushNamed('/homepage');
},
),
],
);
return StreamProvider<List<userdetails>>.value(
value: DatabaseService().students,
initialData: [],
child: SafeArea(
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.lightGreen,
title: Text('Student Welcome'),
actions: <Widget>[
TextButton.icon(
onPressed: () async {
await _auth.signOut();
Navigator.of(context).pushNamed('/homepage');
},
icon: Icon(Icons.person),
label: Text('Logout'))
],
),
body:
UserDetails(),
drawer: GestureDetector(
onTap: display,
child: Drawer(
child: drawerItems,
),
),
),
),
);
}
display() async{
await DatabaseService().getData('email');
}
}
// <Null> means this route returns nothing.
class _NewPage extends MaterialPageRoute<Null> {
_NewPage(int id)
: super(builder: (BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page $id'),
elevation: 1.0,
),
body: Center(
child: Text('Page $id'),
),
);
});
}
database.dart
import 'package:attendee/models/userdeails.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_icons/flutter_icons.dart';
import '../constants.dart';
class DatabaseService{
final String uid;
DatabaseService({this.uid});
//collection reference
final CollectionReference user_details=FirebaseFirestore.instance.collection('users');`
final CollectionReference tutor_details` `=FirebaseFirestore.instance.collection("tutors");`
Future updateStudentData(String fullname,String mobilenumber,String `email,String rollno,String tutorid,String role) async {`
return await user_details.doc(uid).set({
'fullname' : fullname,
'mobilenumber': mobilenumber,
'email' : email,
'rollno': rollno,
'tutorid': tutorid,
'role' : role,//FMERT series
});
}
Future updateTutorData(String fullname,String mobilenumber,String `email,String rollno,String tutorid,String role) async {`
return await tutor_details.doc(uid).set({
'fullname' : fullname,
'mobilenumber': mobilenumber,
'email' : email,
'rollno': rollno,
'tutorid': tutorid,
'role' : role,//FMERT series
});
}
//studentDetails from snapshot
List<userdetails> _studentDetailsFromSnapshot(QuerySnapshot snapshot){
return snapshot.docs.map((doc){
return userdetails(
fullname: doc.data()['fullname'] ?? '',
mobilenumber: doc.data()['mobilenumber'] ?? '',
email: doc.data()['email'] ?? '',
rollno: doc.data()['rollno'] ?? '',
tutorid: doc.data()['tutorid'] ?? '',
//role: doc.data()['role'] ?? '',
);
}).toList();
}
//get students stream
Stream<List<userdetails>> get students {
return user_details.snapshots()
.map(_studentDetailsFromSnapshot);
}
//tutorsDetails from snapshot
List<userdetails> _tutorDetailsFromSnapshot(QuerySnapshot snapshot){
return snapshot.docs.map((doc){
return userdetails(
fullname: doc.data()['fullname'] ?? '',
mobilenumber: doc.data()['mobilenumber'] ?? '',
email: doc.data()['email'] ?? '',
rollno: doc.data()['rollno'] ?? '',
tutorid: doc.data()['tutorid'] ?? '',
);
}).toList();
}
//get tutors stream
Stream<List<userdetails>> get tutors {
return user_details.snapshots()
.map(_studentDetailsFromSnapshot);
}
void display() {
tutor_details.get().then((querySnapshot) {
querySnapshot.docs.forEach((result) {
print(result.data());
});
});
}
getData (String string) async{
String userId = await FirebaseAuth.instance.currentUser.uid;
final document = isTutor ? `FirebaseFirestore.instance.doc('tutors/$userId') :`
await FirebaseFirestore.instance.doc('users/$userId');
document.get().then((DocumentSnapshot) async {
if(string =='role') {
checkRole = DocumentSnapshot.data()[string].toString();
print('$checkRole inside getData Function');
//return checkRole;
print(checkRole);
}
else {
print(result);
result = await DocumentSnapshot.data()[string].toString();
print('${DocumentSnapshot.data()[string].toString()} in the `database else block');`
//return result;
}
//print(document("name"));
});
}
}
After changes
terminaloutput
draweroutput
""when ever i run the program, for the first time the variable is having a null value and upon hot reload the actual value is getting stored in the variable""
When we try to get data from http / https request, it takes some time. Meanwhile the page gets loaded and you get null values.
You can use Provider package to resolve this issue, or try the below code. Please add the below code in your drawerPage.dart.
What I have done below is made getData() return type. Only on receiving a value from this function, _loadOnce will change to false & final screen will be shown.
Database.dart
Future<bool> getData (String string) async{
String userId = await FirebaseAuth.instance.currentUser.uid;
final document = isTutor ? `FirebaseFirestore.instance.doc('tutors/$userId') :`
await FirebaseFirestore.instance.doc('users/$userId');
document.get().then((DocumentSnapshot) async {
if(string =='role') {
checkRole = DocumentSnapshot.data()[string].toString();
print('$checkRole inside getData Function');
//return checkRole;
print(checkRole);
return true;
}
else {
print(result);
result = await DocumentSnapshot.data()[string].toString();
print('${DocumentSnapshot.data()[string].toString()} in the `database else block');`
//return result;
return false;
}
//print(document("name"));
});
}
}
/// create a new variable.
bool _loadOnce = true;
/// shift your code `DatabaseService().getData('email');`
#override
void didChangeDependencies() {
if(_loadOnce == true) {
DatabaseService().getData('email').then((value) {
if(value == true){
setState(() {
_loadOnce = false;
});
} else {
/// you can write your code here
setState(() {
_loadOnce = false;
});
}
)}
}
super.didChangeDependencies();
}
Below code will show a spinner till the time all the code gets executed and values are retreived.
/// in your main page under Scaffold
body: _loadOnce == true
? Center(
child: CircularProgressIndicator(
backgroundColor: Theme.of(context).primaryColor,
),
)
: UserDetails(),

The getter 'uid' was called on null - but only half the time

So I have this screen in my Flutter app that is supposed to show all the notes for a particular user. I have the Firestore structured so that there is a collection of notes and each user has one document named their uid. Then all their notes are store in a collection (usernotes) under their document.
The problem I am having here is that when you try to access the notes page in the app, you get the error
The getter 'uid' was called on null.
Receiver: null
Tried calling: uid
But when I simply click run from the Flutter app, everything works just fine. You can see all the notes on the screen. Here is my note screen.
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'welcome_screen.dart';
class NoteScreen extends StatefulWidget {
static const String id = 'note_screen';
#override
_NoteScreenState createState() => _NoteScreenState();
}
class _NoteScreenState extends State<NoteScreen> {
final _auth = FirebaseAuth.instance;
User loggedInUser;
#override
void initState() {
super.initState();
getCurrentUser();
}
void getCurrentUser() async {
try {
final user = await _auth.currentUser;
if (user != null) {
loggedInUser = user;
}
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Field Notes'),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.chat),
tooltip: 'Messages',
onPressed: () {},
),
IconButton(
icon: const Icon(Icons.exit_to_app),
tooltip: 'Log Out',
onPressed: () {
_auth.signOut();
Navigator.pushNamed(context, WelcomeScreen.id);
},
),
],
),
body: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('notes').doc(loggedInUser.uid).collection('usernotes')
.snapshots(),
builder: (ctx, streamSnapShot) {
if(!streamSnapShot.hasData) return const Text('Loading...');
if (streamSnapShot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
final noteData = streamSnapShot.data.docs;
return ListView.builder(
itemCount: noteData.length,
itemBuilder: (ctx, index) => Container(
padding: EdgeInsets.all(8),
child: Text(noteData[index]['text']),
),
);
},
),
floatingActionButton:
FloatingActionButton(child: Icon(Icons.add), onPressed: () {
FirebaseFirestore.instance.collection('notes').doc(loggedInUser.uid).collection('usernotes').add({
'text' : 'This was added by clicking the button!'
});
}),
);
}
}
currentUser is of type User therefore you do not need to use await since it doesn't return a Future. You can do the following:
FirebaseFirestore.instance.collection('notes').doc(_auth.currentUser.uid).collection('usernotes').snapshots(),
And:
FloatingActionButton(child: Icon(Icons.add), onPressed: () {
FirebaseFirestore.instance.collection('notes').doc(_auth.currentUser.uid).collection('usernotes').add({'text' : 'This was added by clicking the button!'});
});

Firebase_auth photoURL not working. Returning default person image

flutter 1.20.4;
flutter_login_facebook: ^0.2.1;
firebase_auth: ^0.18.1;
firebase_core: ^0.5.0;
provider: ^4.3.2;
Display name and email is working, but photoURL returns this:
I tried to build it on different devices and tried to login to different facebook account.
https://graph.facebook.com/1757577617747458/picture
My HomeScreen
class _HomeState extends State<Home> {
StreamSubscription<User> homeStateSubscription;
#override
void initState() {
var authBloc = Provider.of<AuthBloc>(context, listen: false);
homeStateSubscription = authBloc.currentUser.listen((fbUser) {
if (fbUser == null) {
Navigator.of(context)
.pushReplacement(MaterialPageRoute(builder: (context) => Login()));
}
});
super.initState();
}
#override
Widget build(BuildContext context) {
var authBloc = Provider.of<AuthBloc>(context);
return Scaffold(
body: Center(
child: StreamBuilder<User>(
stream: authBloc.currentUser,
builder: (context, snapshot) {
if (!snapshot.hasData) return CircularProgressIndicator();
print(snapshot.data.email);
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(snapshot.data.displayName,
style: TextStyle(fontSize: 35.0)),
SizedBox(
height: 20.0,
),
CircleAvatar(
backgroundImage: NetworkImage(
snapshot.data.photoURL + '?width=500&height500'),
radius: 60.0,
),
SizedBox(
height: 100.0,
),
SignInButton(Buttons.Facebook,
text: 'Sign out of Facebook',
onPressed: () => authBloc.logout())
],
);
}),
));
}
}
This is Bloc
class AuthBloc {
final authService = AuthService();
final fb = FacebookLogin();
Stream<User> get currentUser => authService.currentUser;
loginFacebook() async {
final res = await fb.logIn(permissions: [
FacebookPermission.publicProfile,
FacebookPermission.email
]);
switch (res.status) {
case FacebookLoginStatus.Success:
print('It worked');
//Get Token
final FacebookAccessToken fbToken = res.accessToken;
//Convert to Auth Credential
final AuthCredential credential =
FacebookAuthProvider.credential(fbToken.token);
//User Credential to Sign in with Firebase
final result = await authService.signInWithCredentail(credential);
print('${result.user.displayName} is now logged in');
break;
case FacebookLoginStatus.Cancel:
print('The user canceled the login');
break;
case FacebookLoginStatus.Error:
print('There was an error');
break;
}
}
logout() {
authService.logout();
}
}
My Auth Service
class AuthService {
final _auth = FirebaseAuth.instance;
Stream<User> get currentUser => _auth.authStateChanges();
Future<UserCredential> signInWithCredentail(AuthCredential credential) =>
_auth.signInWithCredential(credential);
Future<void> logout() => _auth.signOut();
}
I had the same problem. It seems that FB changed the way to get the profile picture.
Source: https://developers.facebook.com/docs/graph-api/reference/user/picture/
"Apps in Development mode that make tokenless requests on ASIDs will receive a silhouette image in response."
Using the token after sign in with Facebook can be used to retrieve image from graph API. The problem is that I needed to download the image and show up in profile widget.

Navigate to a page after user sign up in the android app in flutter using FirebaseAuth

I am working on phone authentication using flutter and firebase. So, when a user 'Register or Sign Up' for the app an OTP is received and then when he clicks the 'Login' button the phone number gets saved in the firebase but the page does not get loaded. After clicking the Login button, the account gets created but the page doesn't change and I have to close the app and open it again and the main page gets displayed. Please, tell me how to do it.
Code for "Class SignUpView" :
class SignUpView extends StatefulWidget {
#override
_SignUpViewState createState() => _SignUpViewState();
}
class _SignUpViewState extends State<SignUpView> {
final formKey = new GlobalKey<FormState>();
String phoneNo, verificationId, smsCode;
bool codeSent = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Form(
key: formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 25, right: 25),
child: TextFormField(
keyboardType: TextInputType.phone,
decoration: InputDecoration(hintText: "Enter Phone Number"),
onChanged: (val) {
setState(() {
this.phoneNo = val;
});
},
),
),
codeSent ? Padding(
padding: EdgeInsets.only(left: 25, right: 25),
child: TextFormField(
keyboardType: TextInputType.phone,
decoration: InputDecoration(hintText: "Enter OTP"),
onChanged: (val) {
setState(() {
this.smsCode = val;
});
},
),
): Container(),
Padding(
padding: EdgeInsets.only(left: 25, right: 25),
child: RaisedButton(
child: Center(
child: codeSent ? Text("Login") : Text("Login"),
),
onPressed: () {
codeSent? AuthService().signInWithOTP(smsCode, verificationId):verifyPhone(phoneNo);
},
),
),
],
),
),
);
}
Future<void> verifyPhone(phoneNo) async {
final PhoneVerificationCompleted verified = (AuthCredential authResult) {
AuthService().signIn(authResult);
};
final PhoneVerificationFailed verificationFailed = (
AuthException authException) {
print('${authException.message}');
};
final PhoneCodeSent smsSent = (String verId, [int forceResend]) {
this.verificationId = verId;
setState(() {
this.codeSent = true;
});
};
final PhoneCodeAutoRetrievalTimeout autoTimeOut = (String verId) {
this.verificationId = verId;
};
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: phoneNo,
timeout: const Duration(seconds: 5),
verificationCompleted: verified,
verificationFailed: verificationFailed,
codeSent: smsSent,
codeAutoRetrievalTimeout: autoTimeOut);
}
}
The part of code where I need to add the navigation is:
onPressed: () {
codeSent? AuthService().signInWithOTP(smsCode, verificationId):verifyPhone(phoneNo);
}
There is another part of code- Class AuthService:
class AuthService {
handleAuth() {
return StreamBuilder(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (BuildContext context, snapshot) {
if(snapshot.hasData) {
return Home_Page();
}
else {
return first_screen(); //Login();
}
},
);
}
signOut() {
FirebaseAuth.instance.signOut();
}
signIn(AuthCredential authCreds) {
if(authCreds != null){
FirebaseAuth.instance.signInWithCredential(authCreds);
}
}
signInWithOTP(smsCode, verId) {
AuthCredential authCreds = PhoneAuthProvider.getCredential(verificationId: verId, smsCode: smsCode);
signIn(authCreds);
}
}
I tried to add navigation inside:
onPressed: () {
codeSent? AuthService().signInWithOTP(smsCode, verificationId):verifyPhone(phoneNo);
Navigator.of(context).pushReplacementNamed('/create_account');
}
But this didn't worked as the above code would navigate to the page and the account won't be created.
I want that when the user type the OTP and then click the Login button, then his phone number should get verified and account should be created on firebase and then the user should be displayed another page. You can either use: Navigator.of(context).pushReplacementNamed('/create_account'); or Account_setup_page() for displaying the page.
I'd really be thankful for all the help I can get.
First of all you need to setup a stream for authentication changes in your AuthService class, i.e.
Stream<FirebaseUser> get user {
return _auth.onAuthStateChanged;
}
Then in your home screen (better in a wrapper widget) you could listen to that stream, so if the user is logged in, it will be redirected to the home screen, else it will be redirected to the sign in screen. This can be accomplished with the following code:
final user = Provider.of<FirebaseUser>(context); // listener for auth state
if (user == null) {
return SignUpView(); // or the sign in view
} else {
return Home(user: user,);
// home screen with the user as argument for easier access in the future
}
Maybe in the future when you will implement the sign out feature, this piece of code will automatically redirect the user to the sign in page

Resources