I am getting some strange issue where one of TextField always gets clears if you tap on it.
class MyEditText extends StatefulWidget {
static String tag = "MyEditText";
#override
MyEditTextState createState() => MyEditTextState();
}
class MyEditTextState extends State<MyEditText> {
String results = "";
final TextEditingController controller = new TextEditingController();
final TextEditingController controller1 = new TextEditingController();
#override
Widget build(BuildContext context) {
final email = TextField(
decoration: InputDecoration(
hintText: 'Enter Email',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0)),
);
final password = TextField(
obscureText: true,
decoration: InputDecoration(
hintText: 'Enter Password',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
),
);
return new Scaffold(
appBar: new AppBar(
automaticallyImplyLeading: false,
title: new Text("EditText Sample"),
backgroundColor: Colors.yellow,
),
body: new Container(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[email, password],
),
),
);
}
}
I am using statful widget for it and all classes from where this screen launch also statful.
Note: If I comment out all TextEditingController and its usage, everything works fine, SO I m not getting what is wrong with TextEditingController
Thanks for the updated code.
The reason your TextEditingController get cleared is because you declare the variables inside of State<MyEditText>. When the State gets re-initialized - those variables do, too.
I can see 2 ways to solve this:
#1 - Move controllers out of the State to the parent class, passing them as arguments
Controllers are declared and maintained outside of MyEditText widget - in the parent class.
class MyEditText extends StatefulWidget {
MyEditText({ Key key, this.emailController, this.passwordController }): super(key: key);
final TextEditingController emailController;
final TextEditingController passwordController;
static String tag = "MyEditText";
#override
MyEditTextState createState() => MyEditTextState();
}
class MyEditTextState extends State<MyEditText> {
String results = "";
#override
Widget build(BuildContext context) {
// ...
TextField(
controller: widget.emailController,
// ...,
),
TextField(
controller: widget.passwordController,
// ...,
),
// ...
}
}
Then you declare controllers in your parent class and pass them as arguments to MyEditText:
final emailController = TextEditingController();
final passwordController = TextEditingController();
// ...
MyEditText(
emailController: emailController,
passwordController: passwordController,
)
#2 - Reuse controllers from the old state on didUpdateWidget call
Controllers can be declared outside of MyEditText class, but if they were not - widget creates and maintains TextEditingController on its own.
class MyEditText extends StatefulWidget {
MyEditText({ Key key, this.emailController, this.passwordController }): super(key: key);
final TextEditingController emailController;
final TextEditingController passwordController;
static String tag = "MyEditText";
#override
MyEditTextState createState() => MyEditTextState();
}
class MyEditTextState extends State<MyEditText> {
TextEditingController _emailController;
TextEditingController _passwordController;
#override
void initState() {
super.initState();
if (widget.emailController == null)
_emailController = TextEditingController();
if (widget.passwordController == null)
_passwordController = TextEditingController();
}
#override
void didUpdateWidget(MyEditText oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.emailController == null && oldWidget.emailController != null)
_emailController = TextEditingController.fromValue(oldWidget.emailController.value);
else if (widget.emailController != null && oldWidget.emailController == null)
_emailController = null;
if (widget.passwordController == null && oldWidget.passwordController != null)
_passwordController = TextEditingController.fromValue(oldWidget.passwordController.value);
else if (widget.passwordController != null && oldWidget.passwordController == null)
_passwordController = null;
}
#override
Widget build(BuildContext context) {
// ...
TextField(
controller: _emailController ?? widget.emailController,
// ...,
),
TextField(
controller: _passwordController ?? widget.passwordController,
// ...,
),
// ...
}
// ...
}
Both methods are similar except that the second one regulates State<MyEditText> variables on its own.
I will leave it to you to decide which one is more suitable in your case.
Let me know if this helped.
TextEditingController controller = TextEditingController();
TextEditingController controller1 = TextEditingController();
final email = TextField(
controller: emailController,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
prefixIcon: Icon(Icons.person_outline, color: Colors.grey),
hintText: 'Enter Email',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0)),
);
final password = TextField(
controller1: passwordController,
obscureText: true,
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock_open, color: Colors.grey),
hintText: 'Enter Password',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
),
);
clearName() {
controller.text = '';
controller1.text = '';
}
//call the clearName function wherever needed
Can you try this way
TextFormField(
cursorColor: Colors.white,
autofocus: false,
keyboardType:
TextInputType.emailAddress,
controller: _textEditingControllerEmail,
),
TextFormField(
autofocus: false,
controller:_textEditingControllerPassword,
cursorColor: Colors.white,
obscureText: true,
),
Related
class UserInputArea extends StatefulWidget {
#override
State<UserInputArea> createState() => _UserInputAreaState();
}
class _UserInputAreaState extends State<UserInputArea> {
#override
Widget build(BuildContext context) {
String convertedText='';
setState(() {
convertedText = Provider.of<UserText>(context, listen: true).convertedText;
print('convertedText :: $convertedText');
});
return Card(
elevation: 10,
child: Container(
padding: EdgeInsets.all(10),
child: TextField(
decoration: InputDecoration(hintText: convertedText.isNotEmpty ? convertedText : 'Enter text'),
keyboardType: TextInputType.multiline,
maxLines: 5,
onChanged: (value){
Provider.of<UserText>(context, listen: false).updateText(value);
},
),
),
);
}
}
Need to update hintText field whenever convertedText gets updated.
This update is happening only if screen refreshed somehow (In Appbar, if click on home-button-icon the data get updated in TextField), Using Provider package that should listen the changes and update the required feild, didnot work. So converted page to Stateful widget and addedd setState() & moved convertedText variable inside it. But still its not working, and not able to figure it out, what is exactly missing here? Anyhelp appreciated. Thanks in advance
Please use TextEditingController class
your code will be somthing like this
class UserInputArea extends StatefulWidget {
#override
State<UserInputArea> createState() => _UserInputAreaState();
}
class _UserInputAreaState extends State<UserInputArea> {
final TextEditingController nameController = TextEditingController();
#override
void initState() {
nameController.text = "test";
super.initState();
//Here you should write your func to change the controller value
Future.delayed(const Duration(seconds: 2), () {
nameController.text = 'test after chabging';
});
}
#override
Widget build(BuildContext context) {
return Card(
elevation: 10,
child: Container(
padding: EdgeInsets.all(10),
child: TextField(
controller: nameController,
decoration: InputDecoration(hintText: convertedText.isNotEmpty ? convertedText : 'Enter text'),
keyboardType: TextInputType.multiline,
maxLines: 5,
),
),
);
}
}
in the write it code above when you will enter the page the hint text will be test after 2 seconds the value will be "test after chabging" without any problem you do not need setState(() {}) I tired it and it works
I think that putting SetState() into the method and calling the method from onChanged could solve the issue. And moving it from Widget build. Something like this:
class UserInputArea extends StatefulWidget {
#override
State<UserInputArea> createState() => _UserInputAreaState();
}
class _UserInputAreaState extends State<UserInputArea> {
String convertedText='';
void _updateField() {
setState(() {
convertedText = Provider.of<UserText>(context, listen: true).convertedText;
print('convertedText :: $convertedText');
});
#override
Widget build(BuildContext context) {
return Card(
elevation: 10,
child: Container(
padding: EdgeInsets.all(10),
child: TextField(
decoration: InputDecoration(hintText: convertedText.isNotEmpty ? convertedText : 'Enter text'),
keyboardType: TextInputType.multiline,
maxLines: 5,
onChanged: (value){
Provider.of<UserText>(context, listen: false).updateText(value);
_updateField();
},
),
),
);
}
}
enter image description hereI have a probelm with the PageController. As soon as I enter a text and then change the page and then come back again, it deletes all the information I entered before. How can I fix this?
Here its my Code about de override:
class AAddTitleComponent extends StatefulWidget {
#override
State<AAddTitleComponent> createState() => _AAddTitleComponentState();
}
class _AAddTitleComponentState extends State<AAddTitleComponent>
with WidgetsBindingObserver {
TextEditingController vehicleTitleTextEditingController =
TextEditingController();
TextEditingController vehiclePSDescriptionTextEditingController =
TextEditingController();
String downloadUrlImage = '';
String vehicleID = DateTime.now().millisecondsSinceEpoch.toString();
File? image;
Future pickImage(ImageSource source) async {
try {
final image = await ImagePicker().pickImage(source: source);
if (image == null) return;
final imageTemp = File(image.path);
setState(() => this.image = imageTemp);
} on PlatformException catch (e) {
print('Kein Bild ausgewählt $e');
}
sharedPreferences = await SharedPreferences.getInstance();
await sharedPreferences.setString(
'vehicleTitleInfo', vehicleTitleTextEditingController.text.toString());
await sharedPreferences.setString('image', downloadUrlImage);
}
#override
void initState() {
vehicleTitleTextEditingController = TextEditingController();
super.initState();
}
and here its the content:
children: [
SizedBox(height: MediaQuery.of(context).viewPadding.top),
SizedBox(height: 64),
Text("Fahrzeug Titel",
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 30)),
SizedBox(height: 16),
TextField(
autocorrect: true,
autofocus: true,
keyboardType: TextInputType.text,
textInputAction: TextInputAction.done,
controller: vehicleTitleTextEditingController,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
borderSide: BorderSide(
color: Colors.black,
width: 0,
),
),
fillColor: Colors.white,
filled: true,
hintText: 'Fahrzeug Titel eingeben',
labelText: 'Fahrzeug Titel',
labelStyle: TextStyle(color: Colors.black),
// alignLabelWithHint: false,
),
)
as soon as I change the pages it deletes everything for me.
as soon as I change the pages it deletes everything for me.
I am giving this solution based on the assumption that the page controller is used as a bottom navigation system or tab bar navigation system:
class MyWidget extends StatefulWidget {
const MyWidget({Key key});
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget>
with AutomaticKeepAliveClientMixin {
#override
Widget build(BuildContext context) {
super.build(context);
return Scaffold(
.....
);
}
#override
bool get wantKeepAlive => true;
}
Can we refactor TextField/TextFormField in Flutter without rebuilding the Widget?
I tried to refactor the TextField Widget for a form that I have created to collect some data. But, when I dismiss my keyboard, the data is losing because the widget is rebuilding. Is there any way to fix it? Pls, Let me know...
See the code below of the Refactored TextField
import 'package:flutter/material.dart';
class ContentInputWidget extends StatelessWidget {
const ContentInputWidget({
Key? key,
required this.text,
required this.controller,
this.keyboardType = TextInputType.text,
}) : super(key: key);
final String text;
final TextInputType keyboardType;
final TextEditingController controller;
#override
Widget build(BuildContext context) {
print('Content Input Widget Rebuild');
return TextField(
decoration: InputDecoration(
labelText: text,
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(15),
),
),
),
controller: controller,
keyboardType: keyboardType,
maxLines: null,
);
}
}
I've used Provider and Consumer to get the data from the text field. And I got it by using a Providers Model Class
See below
class ContentUpdater extends ChangeNotifier {
String description = 'Description comes here';
String name = 'Name';
String prayerRequest = 'Request Comes here';
String postDate = '01-01-2022';
void updatePoster(
String nameText,
String descriptionText,
String prayerReqText,
String postDtText,
) {
name = nameText;
description = descriptionText;
prayerRequest = prayerReqText;
postDate = postDtText;
notifyListeners();
}
}
Called a Function to Update Content using Provider
() { Provider.of<ContentUpdater>(context, listen: false)
.updatePoster(
nameController.text.toString(),
descriptionController.text.toString(),
requestController.text.toString(),
dateController.text.toString(),
);
}
This all works well. But the problem comes if we dismiss the keyboard by clicking the back button, the content disappears from the TextField ...
Is there any way to do without using a StateFulWidget
You can set up your TextFieldController using flutter_hooks (useTextEditingController). This will make it so the state of the TextEditingController isn't lost on rebuilds.
TextEditingController controller = useTextEditingController();
I had a similar problem a long time ago. I don't remember exactly how I made it work, but you could try adding a onChanged function to your TextField and save the data before the re-build clears the text.
final String myText;
void _onChanged() {
if (controller.text != null && controller.text != "") {
myText = controller.text;
}
}
#override
Widget build(BuildContext context) {
print('Content Input Widget Rebuild');
return TextField(
onChanged: _onChanged,
decoration: InputDecoration(
labelText: text,
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(15),
),
),
),
controller: controller,
keyboardType: keyboardType,
maxLines: null,
);
}
The problem is probably there because the controller is inside the stateless widget. When the state is changed, controller dies and gets rebuilt. Try wrapping widget inside a stateful widget and maybe it'll solve your issue.
import 'package:flutter/material.dart';
class ContentInputWidget extends StatefulWidget {
const ContentInputWidget({
Key? key,
required this.text,
required this.controller,
this.keyboardType = TextInputType.text,
}) : super(key: key);
final String text;
final TextInputType keyboardType;
final TextEditingController controller;
#override
State<ContentInputWidget> createState() => _ContentInputWidgetState();
}
class _ContentInputWidgetState extends State<ContentInputWidget> {
#override
Widget build(BuildContext context) {
print('Content Input Widget Rebuild');
return TextField(
decoration: InputDecoration(
labelText: widget.text,
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(15),
),
),
),
controller: widget.controller,
keyboardType: widget.keyboardType,
maxLines: null,
);
}
}
I am trying to use Riverpod state management. I have two TextFormField and I want to set the value of a Text by taking the sum of the values entered in each of the fields using a StateNotifierProvider.
In the following code, CashCounterData is a data model to be used by the StateNotifier, CashCounter. The notifier has two methods, setCount and setCash that are called in the onChanged method of each TextFormField.
final cashProvider = StateNotifierProvider<CashCounter, CashCounterData>((ref) => CashCounter());
class CashCounter extends StateNotifier<CashCounterData> {
CashCounter() : super(_initialData);
static const _initialData = CashCounterData(0, 0);
void setCount(int value){
state = CashCounterData(value, state.cash);
}
void setCash(value){
state = CashCounterData(state.count, value);
}
int get count => state.count;
int get cash => state.cash;
}
class CashCounterData {
final int count;
final int cash;
const CashCounterData(this.count, this.cash);
}
Next, I implemented the UI and am trying to tie in the StateNotifierProvider defined above. However, when I enter values into each TextFormField, the Text widget is always displaying 0.
class CalculatableTextFormField extends HookWidget {
#override
Widget build(BuildContext context) {
final cashCounterProvider = useProvider(cashProvider.notifier);
final TextEditingController _count = TextEditingController();
final TextEditingController _cash = TextEditingController();
return Scaffold(
body: Form(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'${cashCounterProvider.count + cashCounterProvider.cash}'
),
TextFormField(
controller: _count,
keyboardType: TextInputType.number,
onChanged: (value)=>cashCounterProvider.setCount(int.parse(value)),
),
TextFormField(
controller: _cash,
keyboardType: TextInputType.number,
onChanged: (value)=>cashCounterProvider.setCash(int.parse(value)),
)
],
),
),
);
}
}
What am I missing to get my desired behavior?
You are watching the notifier, not the state. The state is what gets changed, and therefore notifies listeners.
It should work if you just change:
final cashCounterProvider = useProvider(cashProvider.notifier);
to:
final cashCounterProvider = useProvider(cashProvider);
Then, in your change handlers:
onChanged: (value) => context.read(cashProvider.notifier).setCash(int.tryParse(value) ?? 0),
When using a provider in a handler like this, prefer context.read as demonstrated above to avoid unnecessary rebuilds.
You also need to use hooks if you are putting your TextEditingControllers in the build method.
final TextEditingController _count = useTextEditingController();
final TextEditingController _cash = useTextEditingController();
All together, your solution is the following:
class CalculatableTextFormField extends HookWidget {
#override
Widget build(BuildContext context) {
final cashCounterProvider = useProvider(cashProvider);
final TextEditingController _count = useTextEditingController();
final TextEditingController _cash = useTextEditingController();
return Scaffold(
body: Form(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('${cashCounterProvider.count + cashCounterProvider.cash}'),
TextFormField(
controller: _count,
keyboardType: TextInputType.number,
onChanged: (value) =>
context.read(cashProvider.notifier).setCount(int.tryParse(value) ?? 0),
),
TextFormField(
controller: _cash,
keyboardType: TextInputType.number,
onChanged: (value) =>
context.read(cashProvider.notifier).setCash(int.tryParse(value) ?? 0),
)
],
),
),
);
}
}
Say if I have ten Text fields in my flutter application,so I have to make ten Text Editing Controller to control the text fields like to get the text from them. However, it may be possible that I would have hundreds of TextField in my application, so, would I have to 100 TextEditingControllers like in below-mentioned code? If not then what is the best way to solve this?
import 'package:flutter/material.dart';
class TestScreen extends StatefulWidget {
#override
_TestScreenState createState() => _TestScreenState();
}
class _TestScreenState extends State<TestScreen> {
final TextEditingController firstController = TextEditingController();
final TextEditingController secondController = TextEditingController();
final TextEditingController thirdController = TextEditingController();
final TextEditingController fourthController = TextEditingController();
final TextEditingController fifthController = TextEditingController();
final TextEditingController sixthController = TextEditingController();
final TextEditingController seventhController = TextEditingController();
final TextEditingController eightController = TextEditingController();
final TextEditingController ninthController = TextEditingController();
final TextEditingController tenthController = TextEditingController();
#override
Widget build(BuildContext context) {
return Container(
child: Form(
child: Column(
children: <Widget>[
TextFormField(
controller: firstController,
),
TextFormField(
controller: secondController,
),
TextFormField(
controller: thirdController,
),
TextFormField(
controller: fourthController,
),
TextFormField(
controller: fifthController,
),
TextFormField(
controller: sixthController,
),
TextFormField(
controller: seventhController,
),
TextFormField(
controller: eightController,
),
TextFormField(
controller: ninthController,
),
TextFormField(
controller: tenthController,
),
],
),
),
);
}
}
To handle multiple TextFormField in flutter, we can use a widget called Form with only one GlobalKey to check and save all the data in the TextFormField.
I would like to explain it, however, I am sure that reading this flutter cookbook you will understand it much better.
Edit
As I see there are some doubts, I will explain briefly how to save the data, that would be the next step on that cookbook.
We create a Form with a GlobalKey as explained in the
cookbook.
We validate the data using _formKey.currentState.validate()
and the validator property on each TextFormField.
We save the text in each TextFormField using
_formKey.currentState.save() and the onSaved property.
The TextFormField would look like this:
TextFormField(
decoration: const InputDecoration(
hintText: 'What do people call you?',
labelText: 'Name *',
),
onSaved: (String value) {
// Use this code to save the text wherever you need it, a Map, List, db...
},
validator: (String value) {
if (value.contains('#') {
return 'Do not use the # char.';
}
return null;
},
)
I hope this helps you!