TextFormField value disappear and How to get textFormField value? - flutter

Please help for my 2 questions.
1st Question. Why text value disappear?[FIXED] by #Praneeth
I added gif
please click
I used this way my code,
In my widget I called UsernameTextField class.
Widget looks like this,
Widget build > WillPopScope > Scaffold > Form > ListView > children>
Container (below container) I added key: _scaffoldKey, and key: formKey, also.
Container(
padding: EdgeInsets.all(16.0),
margin: EdgeInsets.only(top: 30.0),
child: UsernameTextField(),
),
UsernameTextField()
class UsernameTextField extends StatefulWidget{
final usernameController = TextEditingController();
#override
State<StatefulWidget> createState() {
return UsernameTextFieldState(usernameController);
}
}
class UsernameTextFieldState extends State<UsernameTextField>{
final usernameController;
UsernameTextFieldState(this.usernameController);
#override
Widget build(BuildContext context) {
return AppTextField(
decoration: InputDecoration(
contentPadding: const EdgeInsets.all(20.0),
labelText: AppTranslations.of(context)
.text("loginpage_username"),
),
myController: usernameController,
textInputType: TextInputType.emailAddress
);
}
}
AppTextField() class, I used this class for my every TextField Widget
class AppTextField extends StatelessWidget {
final InputDecoration decoration;
final myController;
final TextInputType textInputType;
AppTextField({
this.decoration,
this.myController,
this.textInputType
});
#override
Widget build(BuildContext context) {
return TextFormField(
controller: myController,
keyboardType: textInputType,
textAlign: TextAlign.left,
decoration: decoration
);}}
2nd Question. How to get textField value?
In my button onPressed() method I called, I called validation method, But result is null
usernameValidation(){
String username = UsernameTextField().usernameController.text;
print(username);
}

First convert UsernameTextField class to a Stateful one by extending as a StatefulWidget instead of StatelessWidget.
Then you can get value from usernameController.text
UPDATE
class UsernameTextField extends StatefulWidget{
final usernameController = TextEditingController();
UsernameTextField(this.usernameController)
#override
State<StatefulWidget> createState() {
return UsernameTextFieldState(usernameController);
}
}
class UsernameTextFieldState extends State<UsernameTextField>{
#override
Widget build(BuildContext context) {
return AppTextField(
decoration: InputDecoration(
contentPadding: const EdgeInsets.all(20.0),
labelText: AppTranslations.of(context)
.text("loginpage_username"),
),
myController: widget.usernameController,
textInputType: TextInputType.emailAddress
);
}
}

Related

How to extract TextFormField as reusable widget in flutter

I want to make my flutter project highly manageable, apply clean code and maintain DRY concept strictly. There are a lot of input elements in any flutter project. So I want to make this element as a separate widget so that if I want to change in future then I will change in one place. Here is my approach:
import 'package:flutter/material.dart';
import '../utility/validatation.dart';
class RegistrationPage extends StatefulWidget {
static const String routeName = '/registrationPage';
#override
State<RegistrationPage> createState() => _RegistrationPageState();
}
class _RegistrationPageState extends State<RegistrationPage> {
final _formKey = GlobalKey<FormState>();
final TextEditingController nameInput = TextEditingController();
final TextEditingController businessName = TextEditingController();
final TextEditingController productTypeId = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: new Form(
key: _formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
Container(
height: 70,
margin: EdgeInsets.only(bottom: 50),
child: Image(image: AssetImage('assets/logo.png')),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 30.0),
child: TextInput(inputController: nameInput, label: 'আপনার নাম'),
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_register(context);
}
},
child: Text('Next'),
)
]),
),
);
}
void _register(BuildContext context) {}
}
class TextInput extends StatelessWidget {
const TextInput({
Key? key,
required this.inputController,
required this.label,
}) : super(key: key);
final TextEditingController inputController;
final String label;
#override
Widget build(BuildContext context) {
return TextFormField(
controller: inputController,
keyboardType: TextInputType.text,
decoration: const InputDecoration(
border: UnderlineInputBorder(),
prefixIcon: Icon(Icons.phone),
labelText: label,
),
validator: (value) {
return Validation.required(value);
},
);
}
}
But I got this error:
What is wrong in my code? Is there any problem in my approach or should I stop thinking to refactor my code as I do? Please also suggest if there is any smarter way to make code more clean and manageable.
Oh I see so you have this
class TextInput extends StatelessWidget {
const TextInput({
Key? key,
required this.inputController,
required this.label,
}) : super(key: key);
final TextEditingController inputController;
final String label;
#override
Widget build(BuildContext context) {
return TextFormField(
controller: inputController,
keyboardType: TextInputType.text,
// Notice the const here right?
// So the idea is that decoration objects could rebuild to either change one thing or the other, so 'label' here cannot be a constant
//So to solve this InputDecoration should not have const.
decoration: const InputDecoration(
border: UnderlineInputBorder(),
prefixIcon: Icon(Icons.phone),
labelText: label,
),
validator: (value) {
return Validation.required(value);
},
);
}
}
Since you are using a variable in InputDecoration, you should not declare InputDecoration with const keyword.

TextEditingController returns me null , not considered the input I typed inside text field

questionController.text always returns me a null value. rather than Whatever I insert in textformfield.
class _AddQuestionState extends State<AddQuestion> {
TextEditingController questionController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextFormField(
controller: questionController,
decoration: InputDecoration(
hintText: 'description',
hintStyle: TextStyle(
color: Colors.grey
),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0)),
),
),),
AddUser(questionController.text),]
}}
When I called this class and print the output, it returns me null.
Using AddUser(questionController.text);
I printed the output using print(question) but it returns me empty string.
class AddUser extends StatelessWidget {
final String question;
AddUser(this.question);
#override
Widget build(BuildContext context) {
// Create a CollectionReference called users that references the firestore collection
CollectionReference users = FirebaseFirestore.instance.collection('Questions').doc("cse").collection("CSE");
Future<void> addUser() {
print(question);
// Call the user's CollectionReference to add a new user
return users
.add({
'question': question, // John Doe
})
.then((value) => print("User Added"))
.catchError((error) => print("Failed to add user: $error"));
}
What happen is when it first build the screen your AddQuestion widget questionController is set to empty string which is passed to the AddUser() widget.
If [controller] is null, then a [TextEditingController] will be constructed automatically and its text will be initialized to [initialValue] or the empty string.
When you changed the value in your questionController, your AddUser() widget didnt know the changes. By adding setState it will rebuild the whole AddQuestion widget and passed the new value to your AddUser widget.
Try this example to have an undestanding
class MyWidget extends StatefulWidget {
const MyWidget({Key? key}) : super(key: key);
#override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
TextEditingController questionController = TextEditingController();
String? questionValue;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
TextFormField(
controller: questionController,
decoration: InputDecoration(
hintText: 'description',
hintStyle: TextStyle(color: Colors.grey),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0)),
),
),
),
SizedBox(height: 20),
TextButton(
onPressed: () {
print("questionController: ${questionController.text}");
setState(() {
questionValue = questionController.text;
});
},
child: Text("SETSTATE BUTTON"),
),
SizedBox(height: 20),
Text("questionValue: $questionValue"),
SizedBox(height: 20),
AddUser(questionController.text),
],
),
),
);
}
}
class AddUser extends StatelessWidget {
final String question;
AddUser(this.question);
#override
Widget build(BuildContext context) {
return Text("question: $question");
}
}

Accessing state of Statefull Widget

I have a situtation where i would like to access to text of a text controller, from the Widget part of a statefull widget.
So what i would like to do is is write a method getText() that returns the current text in my _textController.
I know how to do this from the other way around. If i need to get data from my widget from the State part of my widget is use "widget.", but i dont know how to do this the other way around.
class MyTextWidget extends StatefulWidget {
String getText() {
// how can i access the _textController.text from here?
}
#override
_MyTextWidgetState createState() => _MyTextWidgetState();
}
class _MyTextWidgetState extends State<MyTextWidget> {
final TextEditingController _textController = TextEditingController();
_MyTextWidgetState();
#override
void dispose() {
_textController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(2.0),
child: TextField(
controller: _textController,
key: ValueKey('MyTextWidgetinput_Key'),
maxLines: null,
autofocus: true,
keyboardType: TextInputType.multiline,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: const BorderRadius.all(Radius.circular(15.0))),
),
));
}
}
Simply move it up and access it in your state class using widget._textController:
class MyTextWidget extends StatefulWidget {
final TextEditingController _textController = TextEditingController();
String getText() {
_textController.text // do something with it
// how can i access the _textController.text from here?
}
#override
_MyTextWidgetState createState() => _MyTextWidgetState();
}
class _MyTextWidgetState extends State<MyTextWidget> {
_MyTextWidgetState();
#override
void dispose() {
widget._textController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(2.0),
child: TextField(
controller: widget._textController,
key: ValueKey('MyTextWidgetinput_Key'),
maxLines: null,
autofocus: true,
keyboardType: TextInputType.multiline,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: const BorderRadius.all(Radius.circular(15.0))),
),
));
}
}

Flutter - How implement one E-Mail TextFormField widget for using in multiple screens?

I'm developing a app with a register, login and reset password screen. In all this screens the user must enter his e-mail address. Now i will not implement the e-mail address textfield for every single screen. So i will implement a email textfield widget for every screen like the code below.
import 'package:flutter/material.dart';
import 'package:email_validator/email_validator.dart';
class EMailTextFormField extends StatefulWidget {
#override
_EMailTextFormFieldState createState() => _EMailTextFormFieldState();
}
class _EMailTextFormFieldState extends State<EMailTextFormField> {
final _email = TextEditingController();
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 3.0),
child: TextFormField(
decoration: InputDecoration(
icon: Icon(Icons.email, size: IconTheme.of(context).size, color: IconTheme.of(context).color),
labelText: 'E-Mail...',
counterText: '',
),
keyboardType: TextInputType.emailAddress,
controller: _email,
validator: _validateEmail,
maxLength: 70,
),
);
}
String _validateEmail(String email) {
// validate E-Mail function...
}
}
My problem is now that i can't use the TextEditingController (_email) outside this widget in the different screens (register, login and reset password) like this as a example:
final FirebaseUser user = (await _auth.createUserWithEmailAndPassword(
email: _email.text.toString(), password: _password.text.toString())).user;
The error is "Undefined name _email" because the _email TextEditingController is in the EMailTextFormField widget, but how can i give the value of the _email field from EMailTextFormField widget to the other screens (register, login and reset password)?
Can anyone help me i found so far no solution.
You can do this using onSaved callback.
EMailTextFormField:
class EMailTextFormField extends StatefulWidget {
final void Function(String email) onSaved;
const EMailTextFormField({Key key, this.onSaved}) : super(key: key);
#override
EMailTextFormFieldState createState() => EMailTextFormFieldState();
}
class EMailTextFormFieldState extends State<EMailTextFormField> {
final _email = TextEditingController();
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 3.0),
child: TextFormField(
decoration: InputDecoration(
icon: Icon(Icons.email, size: IconTheme.of(context).size, color: IconTheme.of(context).color),
labelText: 'E-Mail...',
counterText: '',
),
keyboardType: TextInputType.emailAddress,
controller: _email,
validator: _validateEmail,
maxLength: 70,
onSaved: widget.onSaved, //callback
),
);
}
String _validateEmail(String email) {
// validate E-Mail function...
}
}
Page where you will use EMailTextFormField:
class EmailPage extends StatefulWidget {
EmailPage({Key key}) : super(key: key);
#override
_EmailPageState createState() => _EmailPageState();
}
class _EmailPageState extends State<EmailPage> {
String _email;
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Email Page'),
),
body: Form(
key: _formKey,
child: Column(
children: <Widget>[
EMailTextFormField(
onSaved: (String email) => _email = email,
),
RaisedButton(
child: Text('Go'),
onPressed: (){
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
print(_email);
}
},
)
],
),
),
);
}
}

How to correctly extract/composite custom widgets and handle their child fields

In my widget tree, I have several TextField widgets. All have the same decoration but different onChanged actions:
Widget _buildTextField() {
return TextField (
decoration: InputDecoraction(
border: OutlineInputBorder()
),
onChanged: (text) {
doSth();
}
);
}
Now I want to reduce the code duplication and was trying to extract the TextField with the duplicated decoration field into a CustomTextField. I read that with Flutter composition is over inheritance, so I tried to compose it this way:
class CustomTextField extends StatefulWidget {
#override
_CustomTextFieldState createState() => _CustomTextFieldState();
}
class _CustomTextFieldState extends State<CustomTextField> {
#override
Widget build(BuildContext context) {
return Container(
child: TextField(
decoration: InputDecoration( // <-- the decoration field
border: OutlineInputBorder(),
),
),
);
}
}
At the other side I did:
Widget _buildTextField() {
return CustomTextField ( // <-- new CustomTextField without decoration
onChanged: (text) { // <-- Problem: "Parameter is not defined"
doSth();
}
);
}
But now the onChanged call is not accepted. So, what is the correct way to extract own widgets and handle the child fields?
You need to register a callback function
class CustomTextField extends StatefulWidget {
final Function onChange;
const CustomTextField({Key key, this.onChange}) : super(key: key);
#override
_CustomTextFieldState createState() => _CustomTextFieldState();
}
class _CustomTextFieldState extends State<CustomTextField> {
#override
Widget build(BuildContext context) {
return Container(
child: TextField(
decoration: InputDecoration(
// <-- the decoration field
border: OutlineInputBorder(),
),
onChanged: widget.onChange,
),
);
}
}
and then
child: CustomTextField(
onChange: (item) {
print(item);
},
),