Unable to upload data to firebase realtime database from my flutter application - firebase

I am trying to add a module in my flutter project where users will be able to upload their addresses from. I'm using a http package for this. On saving the data, however, only an empty string is being uploaded onto the database.
Here's my code:
This is the validator code from the text form editing field:
validator: (value) {
if (value!.isEmpty) {
return 'Please enter a description.';
}
if (value.length < 10) {
return 'Should be at least 10 characters long.';
}
return null;
},
onSaved: (value) {
addressEditingController.text = value!;
_editedAddress = Address(address: value);
},
),
),
IconButton(onPressed:(){
if(_form.currentState!.validate()){
uploadAddress();
Here is the uploadAddress() function:
void uploadAddress() {
setState(() {
_editedAddress = Address(
address: _editedAddress.address);
});
Provider.of<addAddress>(context, listen: false)
.addProduct(_editedAddress)
.catchError((error) {
return showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text("An error occurred"),
content: Text(error.toString()),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(ctx).pop();
},
child: Text("Okay"))
],
));
}).then((_) {
setState(() {
_isLoading = false;
});
});
}
Here's the addProduct() function:
Future<void> addProduct(Address address) {
final url = Uri.https(
'internship-75b58-default-rtdb.firebaseio.com', '/addresses/${user!.phoneNumber}.json');
return http
.post(url,
body: json.encode({
'address':address.address,
}))
.then((response) {
final newProduct = Address(
address: address.address,);
notifyListeners();
}).catchError((error) {
print(error);
throw error;
});
}
Any help would be greatly appreciated:)

Related

Can't register user in firebase (firebase_auth/unknown: null)

I am registering a user in firebase and each time I try to register it shows me the mentioned error and donot send credentials to the firebase. Although it is getting the credentials from the firebase for login but shows error while storing values in firebase. Below is the code for only register that is getting the email address and password. I have another question that like password and email how could I store other details in firebase e.g Age, Gender etc. Kindly help me go through this.
class _ClientRegistrationScreenState extends State<ClientRegistrationScreen> {
bool showSpinner = false;
final _auth = FirebaseAuth.instance;
File image;
//final ImagePicker _picker = ImagePicker();
String password;
String confirmPassword;
String email;
String name;
bool _passwordVisible = false;
bool _confirmPasswordVisible = false;
#override
void initState() {
_passwordVisible = false;
_confirmPasswordVisible = false;
}
final _formKey = GlobalKey<FormState>();
Expanded(
child: Center(
child: TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return '*Email Address Required';
}
return null;
},
),
),
),
),
Expanded(
child: Center(
child: TextFormField(
onChanged: (value){
password = value;
},
validator: (value) {
if (value == null || value.isEmpty) {
return '*Password Required';
}
if (password != confirmPassword) {
return 'Password Donot Match';
}
return null;
},
onPressed: () {
setState(() {
_passwordVisible = !_passwordVisible;
});
},
),
),
),
),
),
Expanded(
child: Center(
child: TextFormField(
onChanged: (value){
confirmPassword = value;
},
validator: (value) {
if (value == null || value.isEmpty) {
return '*Confirm Password';
}
if (password != confirmPassword) {
return 'Password Donot Match';
}
return null;
},
onPressed: () {
setState(() {
_confirmPasswordVisible = !_confirmPasswordVisible;
});
},
),
),
),
),
),
RoundedButton(
colour: Colors.yellow,
opColour: Colors.yellow.withOpacity(0.2),
title: 'Register',
buttonTextColor: Colors.black,
onPressed: () async {
if (_formKey.currentState.validate()) {
setState(() {
showSpinner = true;
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Processing Data')),
);
}
try {
final newUser = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
if(newUser!=null){
Navigator.pushNamed(context, MainEmployeeScreen.id);
print(name);
}
setState(() {
showSpinner = false;
});
}
catch(e){
print(e);
}
}
),
Did you initialize firebase in main function?
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}

Check if phone exists before signup/signin - Flutter Phone Authentication [duplicate]

This question already exists:
Firebase Authentication using Phone number (Error: Missing Session Info)
Closed 2 years ago.
I want to check if phone exists before signing in or signing up a user. With email registration, I used the following and I was able to tell if an email exists or not.
final url =
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/$verifyPassword?key={API_KEY}';
Similarly, for phone numbers, I used the following:
final url ='https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPhoneNumber?key={API_KEY}';
final response = await http.post(
url,
body: json.encode(
{
'phoneNumber': number
},
),
);
However, I am getting the following error message:
Extracted data is {error: {code: 400, message: MISSING_SESSION_INFO, errors: [{message: MISSING_SESSION_INFO, domain: global, reason: invalid}]}}
I want to know why does it work for email but not for phone registration?
Also, is this the right way to check if a phone exists?
Here's my full code:
enum Status { Uninitialized, Authenticated, Authenticating, Unauthenticated }
class AuthProvider with ChangeNotifier {
FirebaseAuth _auth = FirebaseAuth.instance;
User _user;
Status _status = Status.Uninitialized;
TextEditingController phoneNo;
String smsOTP;
String verificationId;
String errorMessage = '';
bool logedIn = false;
bool loading = false;
Status get status => _status;
TextEditingController address = TextEditingController();
AuthProvider.initialize() {
readPrefs();
}
Future signOut() async {
_auth.signOut();
_status = Status.Unauthenticated;
notifyListeners();
return Future.delayed(Duration.zero);
}
Future<void> readPrefs() async {
await Future.delayed(Duration(seconds: 3)).then((v) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
logedIn = prefs.getBool('logedIn') ?? false;
if (!logedIn) {
print('User is not logged in');
_status = Status.Unauthenticated;
} else {
print('User is logged in');
_user = _auth.currentUser;
_status = Status.Authenticated;
}
notifyListeners();
});
}
Future<void> verifyPhone(BuildContext context, String number,String password) async {
//To be used in the verifyPhone method
final PhoneCodeSent smsOTPSent = (String verId, [int forceCodeResend]) {
this.verificationId = verId;
smsOTPDialog(context, number,password).then((value) {
_status = Status.Authenticated;
});
};
try {
await _auth.verifyPhoneNumber(
phoneNumber: number.trim(),
codeAutoRetrievalTimeout: (String verId) {
//Starts the phone number verification process for the given phone number.
//Either sends an SMS with a 6 digit code to the phone number specified, or sign's the user in and [verificationCompleted] is called.
this.verificationId = verId;
},
codeSent: smsOTPSent,
// timeout: const Duration(seconds: 20),
//If user is automatically verified (without having to type the code)
verificationCompleted: (AuthCredential credential) async {
Navigator.of(context).pop();
UserCredential result =
await _auth.signInWithCredential(credential);
User user = result.user;
if (user != null) {
//TO DO:// Here you need to save the phone and password to DB
print('Adding user to DB');
final url = 'https://mobile-12.firebaseio.com/users/$number.json';
try {
await http.post(
url,
body: json.encode({
'password': password,
'phoneNumber':user.phoneNumber,
}),
);
_status = Status.Authenticated;
} catch (error) {
print(error);
}
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomeScreen(
)));
} else {
print("Error");
}
},
verificationFailed: (FirebaseAuthException exceptio) {
print('${exceptio.message} + something is wrong');
});
} catch (e) {
handleError(e, context,number,password);
errorMessage = e.toString();
notifyListeners();
}
notifyListeners();
}
Future<bool> smsOTPDialog(BuildContext context,String number,String password) {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Enter SMS Code'),
content: Container(
height: 85,
child: Column(children: [
TextField(
onChanged: (value) {
this.smsOTP = value;
},
),
(errorMessage != ''
? Text(
errorMessage,
style: TextStyle(color: Colors.red),
)
: Container())
]),
),
contentPadding: EdgeInsets.all(10),
actions: <Widget>[
FlatButton(
child: Text("Confirm"),
textColor: Colors.white,
color: Colors.blue,
onPressed: () async {
final code = this.smsOTP.trim();
AuthCredential credential = PhoneAuthProvider.credential(
verificationId: verificationId, smsCode: code);
UserCredential result =
await _auth.signInWithCredential(credential);
User user = result.user;
if (user != null) {
print('user already exist');
// //TO DO:// Save the phone number and password to DB
print('Adding user to Db in the manual OTP route');
final url = 'https://mobile-12.firebaseio.com/users/$number.json';
try {
await http.post(
url,
body: json.encode({
'password': password,
'phoneNumber':user.phoneNumber,
}),
);
} catch (error) {
print('INSIDE ERROR');
print(error);
}
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool("logedIn", true);
logedIn = true;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomeScreen(
)));
loading = false;
notifyListeners();
} else {
print("No OTP was added");
loading = true;
notifyListeners();
Navigator.of(context).pop();
}
},
)
],
);
});
}
//Sign-In Method checks to see if a phone exists.
signIn(BuildContext context, String number, String password,AuthMode authMode) async {
try {
//Check to see if the phone number is available
final url = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPhoneNumber?key=';
final response = await http.post(
url,
body: json.encode(
{
'phoneNumber': number
},
),
);
final extractedData = json.decode(response.body) as Map<String, dynamic>;
print('Extracted data is ' + extractedData.toString());
//Register and send OTP if new user
if (extractedData == null && authMode == AuthMode.Signup) {
print('Inside NULL no errors');
// //Verify phone
verifyPhone(context, number, password);
}
//If tries to login but phone not available
else if(extractedData == null && authMode == AuthMode.Login)
{
_showErrorDialog('Phone number does not exist. Please Sign Up', context);
}
else if (extractedData['error'] != null) {
_showErrorDialog('Something went wrong! Please try again!', context);
}
//If someone signup but their phone already exist
else if(extractedData != null && authMode == AuthMode.Signup)
{
_showErrorDialog('Your phone already exists. Please Login!', context);
}
//If available, proceed to homepage
else {
print('User found');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomeScreen(
)));
}
} catch (e) {
handleError(e, context,number,password);
}
}
handleError(error, BuildContext context,String number,String password) {
errorMessage = error.toString();
print('ERROR IS ' + errorMessage);
notifyListeners();
}
Future<bool> _showErrorDialog(String message,BuildContext context) {
return showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text('An Error Occurred!'),
content: Text(message),
actions: <Widget>[
FlatButton(
child: Text('Okay'),
onPressed: () {
Navigator.of(ctx).pop();
},
)
],
),
);
}
}

Flutter Uploading Text File to Storage

I'm trying to create a .txt file that contains the information from TextFieldController.
class FileUtils {
static Future<String> get getFilePath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
static Future<File> get getFile async {
final path = await getFilePath;
return File('$path/myfile.txt');
}
static Future<File> saveToFile(String data) async {
final file = await getFile;
final neww = file.writeAsString(data);
return file.writeAsString(data);
}
static Future<String> readFromFile() async {
try {
final file = await getFile;
String fileContents = await file.readAsString();
return fileContents;
} catch (e) {
return "";
}
}
}
So, in the code above, I'm trying to create a .txt file that will be stored in Firebase Storage. In the code below, the user will input the information and that information will be saved to text file using the FileUtils' saveToFile function.
_dialog(BuildContext context) async {
await Future.delayed(Duration(seconds: 1));
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Please write something'),
content: TextField(
controller: _textFieldController,
decoration: InputDecoration(hintText: ""),
maxLines: 5,
maxLength: 250,
),
actions: <Widget>[
new FlatButton(
child: new Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
new FlatButton(
child: new Text('Submit'),
onPressed: () {
FileUtils.saveToFile(_textFieldController.text);
Navigator.of(context).pop();
},
)
],
);
});
}
And I tried to store the text file using:
final test = FileUtils.getFile;
FirebaseStorage.instance.ref().child('test').putFile(test).onComplete;
but I failed. So, I need help storing .txt file to Firebase. I'm confused so I hope I was able to explain my problem.

How to call a function ASA the app runs in flutter?

I have an application that do login with the help of firestore database and I want to do autologin so I made a boolean and set it to false in the database and made the login function set it to true as he or she sign in, so I want to check if the person have already signed in or not as the app runs, any ideas :) ?
here my code:
void getUserData() async {
try {
var firebaseUser = await FirebaseAuth.instance.currentUser();
firestoreInstance
.collection("Students")
.document(usernameController.text)
.get()
.then((value) {
setState(() {
email = (value.data)['email'];
password = (value.data)['password'];
gender = (value.data)['gender'];
loggedin = (value.data)['token'];
});
});
} catch (e) {
print(e.toString);
}
}
You dont have to use a boolean to check if the user is logged in or not. Firebase authentication already offers that. You can check inside the initState:
#override
void initState() {
super.initState();
FirebaseAuth.instance.currentUser().then((res) {
print(res);
if (res != null) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => Home(uid: res.uid)),
);
}
else
{
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SignUp()),
);
}
});
}
Checks if there is a current user or not and navigates to the required page.
If you have different types of users, then you have to identify them in the database. So authenticate in firebase authentication, and use a userType field in the database:
void registerToFb() {
firebaseAuth
.createUserWithEmailAndPassword(
email: emailController.text, password: passwordController.text)
.then((result) {
firestoreInstance.collection("users").document(result.user.uid).setData({
"email": emailController.text,
"name": nameController.text,
"userType" : "Students"
}).then((res) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => Home(uid: result.user.uid)),
);
});
}).catchError((err) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Error"),
content: Text(err.message),
actions: [
FlatButton(
child: Text("Ok"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
});
}

flutter validate form asynchronously

new TextFormField(
validator: (value) async{
if (value.isEmpty) {
return 'Username is required.';
}
if (await checkUser()) {
return 'Username is already taken.';
}
},
controller: userNameController,
decoration: InputDecoration(hintText: 'Username'),
),
I have a form for user, and I want to check if the user already exists in the firestore datebase.
Future checkUser() async {
var user = await Firestore.instance
.collection('users')
.document(userNameController.text)
.get();
return user.exists;
}
This is my function to check if the user document already exists in the database.
But validator gives me this error.
[dart] The argument type '(String) → Future' can't be assigned to the parameter type '(String) → String'.
How should I fix this issue?
At this time I think that you can't associate a Future to a validator.
What you can do is this verifying the data on a button click or in another way and set the state on the validator response var.
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body: Form(
key: _formKey,
child: Column(children: [
new TextFormField(
validator: (value) {
return usernameValidator;
},
decoration: InputDecoration(hintText: 'Username')),
RaisedButton(
onPressed: () async {
var response = await checkUser();
setState(() {
this.usernameValidator = response;
});
if (_formKey.currentState.validate()) {}
},
child: Text('Submit'),
)
])));
}
I needed to do this for username validation recently (to check if a username already exists in firebase) and this is how I achieved async validation on a TextFormField ( without installation of any additional packages). I have a "users" collection where the document name is the unique username ( Firebase can't have duplicate document names in a collection but watch out for case sensitivity)
//In my state class
class _MyFormState extends State<MyForm> {
final _usernameFormFieldKey = GlobalKey<FormFieldState>();
//Create a focus node
FocusNode _usernameFocusNode;
//Create a controller
final TextEditingController _usernameController = new TextEditingController();
bool _isUsernameTaken = false;
String _usernameErrorString;
#override
void initState() {
super.initState();
_usernameFocusNode = FocusNode();
//set up focus node listeners
_usernameFocusNode.addListener(_onUsernameFocusChange);
}
#override
void dispose() {
_usernameFocusNode.dispose();
_usernameController.dispose();
super.dispose();
}
}
Then in my TextFormField widget
TextFormField(
keyboardType: TextInputType.text,
focusNode: _usernameFocusNode,
textInputAction: TextInputAction.next,
controller: _usernameController,
key: _usernameFormFieldKey,
onEditingComplete: _usernameEditingComplete,
validator: (value) => _isUsernameTaken ? "Username already taken" : _usernameErrorString,)
Listen for focus changes on the widget i.e when it loses focus. You can also do something similar for "onEditingComplete" method
void _onUsernameFocusChange() {
if (!_usernameFocusNode.hasFocus) {
String message = UsernameValidator.validate(_usernameController.text.trim());
//First make sure username is in valid format, if it is then check firebase
if (message == null) {
Firestore.instance.collection("my_users").document(_usernameController.text.trim()).get().then((doc) {
if (doc.exists) {
setState(() {
_isUsernameTaken = true;
_usernameErrorString = null;
});
} else {
setState(() {
_isUsernameTaken = false;
_usernameErrorString = null;
});
}
_usernameFormFieldKey.currentState.validate();
}).catchError((onError) {
setState(() {
_isUsernameTaken = false;
_usernameErrorString = "Having trouble verifying username. Please try again";
});
_usernameFormFieldKey.currentState.validate();
});
} else {
setState(() {
_usernameErrorString = message;
});
_usernameFormFieldKey.currentState.validate();
}
}
}
For completeness, this is my username validator class
class UsernameValidator {
static String validate(String value) {
final regexUsername = RegExp(r"^[a-zA-Z0-9_]{3,20}$");
String trimmedValue = value.trim();
if (trimmedValue.isEmpty) {
return "Username can't be empty";
}
if (trimmedValue.length < 3) {
return "Username min is 3 characters";
}
if (!regexUsername.hasMatch(trimmedValue)) {
return "Usernames should be a maximum of 20 characters with letters, numbers or underscores only. Thanks!";
}
return null;
}
}
I had the same problem while using Firebase's Realtime Database but I found a pretty good solution similar to Zroq's solution. This function creates a simple popup form to have the user input a name. Essentially, I was trying to see if a particular name for a specific user was already in the database and show a validation error if true. I created a local variable called 'duplicate' that is changed anytime the user clicks the ok button to finish. Then I can call the validator again if there is an error, and the validator will display it.
void add(BuildContext context, String email) {
String _name;
bool duplicate = false;
showDialog(
context: context,
builder: (_) {
final key = GlobalKey<FormState>();
return GestureDetector(
onTap: () => FocusScope.of(context).requestFocus(new FocusNode()),
child: AlertDialog(
title: Text("Add a Workspace"),
content: Form(
key: key,
child: TextFormField(
autocorrect: true,
autofocus: false,
decoration: const InputDecoration(
labelText: 'Title',
),
enableInteractiveSelection: true,
textCapitalization: TextCapitalization.sentences,
onSaved: (value) => _name = value.trim(),
validator: (value) {
final validCharacters =
RegExp(r'^[a-zA-Z0-9]+( [a-zA-Z0-9]+)*$');
if (!validCharacters.hasMatch(value.trim())) {
return 'Alphanumeric characters only.';
} else if (duplicate) {
return 'Workspace already exists for this user';
}
return null;
},
),
),
actions: <Widget>[
FlatButton(
child: const Text("Ok"),
onPressed: () async {
duplicate = false;
if (key.currentState.validate()) {
key.currentState.save();
if (await addToDatabase(_name, email) == false) {
duplicate = true;
key.currentState.validate();
} else {
Navigator.of(context).pop(true);
}
}
},
),
FlatButton(
child: const Text('Cancel'),
onPressed: () {
Navigator.of(context).pop(false);
},
),
],
),
);
});
}

Resources