Hitting done on keyboard clears the text in flutter - flutter

I have a TextField() and, a controller, which I'm passing it to the TextField controller. My problem is that every time I hit done in my keyboard it clears my text.
I have followed this question: Flutter keyboard done button causes textfield content to vanish
question to solve my problem but nothing is working out for me. I'm clueless hence putting this question again on the StackOverflow.
MY CODE:
class ReferralPage extends StatelessWidget {
final TextEditingController controller = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
margin: EdgeInsets.only(top: 40.0, left: 16.0, right: 16.0),
child: Card(
color: Colors.white,
child: Padding(
padding: EdgeInsets.all(24.0),
child: TextField(
controller: controller,
cursorColor: Theme.of(context).primaryColor,
decoration: InputDecoration(hintText: 'Referral Code'),
)
)
),
decoration: BoxDecoration(boxShadow: [
new BoxShadow(color: Color.fromRGBO(173, 179, 191, 0.3), blurRadius: 20.0, offset: new Offset(0, 12))
])
)
);
}
}
As you can see my TextEditingController() is not inside the Widget build so there is no sense of happening that.
The only solution I found to this problem was to remove the controller but I don't want to do this, I want to use my controller for other purposes. I don't know why this is happening.
Any help would be appreciated. Thanks :)

You are having this issue because you are making use of a stateless widget. Change your stateless widget to a stateful widget so that your controller won't get rebuilt.

Related

Why does my widget rebuild when I use keyboard

I have this issue of rebuilding widget when The keyboards shows up. I tried to use the sizer package but never could figure out how to get it to work
when I go back from this screen everything in the previous screen will rebuild, Please note: If I don't click on the typeaheadwidget such that the keyboard doesn't show up the state is preserved in the previous screen but as soon as the keyboard pops up the widgets get rebuilt
Could you please check ?
class SearchScreen extends StatefulWidget {
#override
_SearchScreenState createState() => _SearchScreenState();
}
class _SearchScreenState extends State<SearchScreen> {
TextEditingController pickUpTextEditingController = TextEditingController();
TextEditingController dropOffTextEditingController = TextEditingController();
#override
void initState() {
super.initState();
}
#override
#mustCallSuper
Widget build(BuildContext context) {
String placeAddress =
Provider.of<AppData>(context).pickUpLocation.placeName ?? "";
pickUpTextEditingController.text = placeAddress;
return Scaffold(
resizeToAvoidBottomInset: false,
body: Stack(
children: [
Container(
height: 250.0,
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black,
blurRadius: 6.0,
spreadRadius: 0.5,
offset: Offset(0.7, 0.7),
)
],
),
child: Padding(
padding: EdgeInsets.only(
left: 25.0, top: 30.0, right: 25.0, bottom: 20.0),
child: Column(
children: [
SizedBox(height: 5.0),
Stack(
children: [
GestureDetector(
onTap: () {
Navigator.pop(
//send back data
context,
dropOffTextEditingController.text);
},
child: Icon(Icons.arrow_back)),
Center(
child: Text(
"Set Drop Off",
style: TextStyle(
fontSize: 18.0, fontFamily: "Brand-Bold"),
),
)
],
),
SizedBox(height: 16.0),
Row(
children: [
Image.asset("images/images/pickicon.png",
height: 16.0, width: 16.0),
SizedBox(width: 18.0),
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.grey[400],
borderRadius: BorderRadius.circular(5.0),
),
child: Padding(
padding: EdgeInsets.all(3.0),
child: TextField(
controller: pickUpTextEditingController,
decoration: InputDecoration(
hintText: "PickUp Location",
fillColor: Colors.grey[400],
filled: true,
border: InputBorder.none,
isDense: true,
contentPadding: EdgeInsets.only(
left: 11.0, top: 8.0, bottom: 8.0),
),
),
),
))
],
),
SizedBox(height: 10.0),
Row(
children: [
Image.asset("images/images/desticon.png",
height: 16.0, width: 16.0),
SizedBox(width: 18.0),
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.grey[400],
borderRadius: BorderRadius.circular(5.0),
),
child: Padding(
padding: EdgeInsets.all(3.0),
child: TypeAheadField(
itemBuilder: null,
onSuggestionSelected: null,
suggestionsCallback: null,
),
),
),
),
],
),
],
),
),
),
],
),
);
}
}
You should not try to control when the build method is called. Flutter will call build when it decides it needs to (e.g. keyboard appears, device rotated, parent rebuilds, etc).
Instead, you should make sure that your build method is a "pure" function. In Flutter specifically, this means that you should not perform any action with "side-effects" (basically anything which modifies the state of the app).
For example:
Widget build(BuildContext context) {
final x = 2 + 3; // fine, nothing else is modified
final state = context.watch<MyModel>(); // also fine, only reading data
controller.text = "hello"; // BAD, modifies the state of the app
return ...;
}
Instead, you should move your logic with side effects into other lifecycle methods (e.g. initState(), didChangeDepencencies(), etc).
For example, if you want to set your text field to a particular string when it first appears, you can use initState:
class _SearchScreenState extends State<SearchScreen> {
#override
void initState() {
super.initState();
final data = context.read<AppData>();
controller.text = data.pickUpLocation.placeName ?? "";
}
Widget build(BuildContext context) {
// ...
}
}
Now build() can be called whenever it has to be, without resetting the state of your text field.
Note that, even if there was some way to prevent your widget from being rebuilt, this is also likely not what you want, since the UI would not update to accommodate the keyboard.
the only reason why your widgets got rebuilds after keyboard pop up.
is that one or more of your widgets size depends on MediaQuery.
you can try to ge your screen size from LayoutBuilder as an alternative for MediaQuery.
Give the textfield an initial value like this:
initvalue:Provider.of<AppData>(context).pickUpLocation.placeName ?? ""
and use onchange method in text field instead of text editing controller like this:
onchange(value){
Provider.of<AppData>(context).pickUpLocation.placeName=value;}
I am also facing the same issue, My blocBuidler is getting rebuilt every time when click on textfield or keyboard is appear.
In my case, I was calling the event in parent BlocBuilder so whenever I pressed on textfields the parent BlocBuilder is called the event, so it builds state of child BlocBuilder
Make sure you are also doing the same thing. If you are doing the same thing please check the state whether it is already built or not.
(BlocProvider.of<YourBlocName>(context).state is YouBlocState) ? Print('do nothing'): BlocProvider.of<YourBlocName>(context).add(youBlocEvent);
When you tap the TextField widget, it makes the keyboard show up. And when the keyboard shows up, your screen size changes. This causes the rebuild

Not able to import variable from a different class in Flutter

I am going off of a login screen tempalte,a nd am trying to get a widget class for a button to just show the username input as an alert on the screen. The usernameinput widget is defined but when I import it, it does not work.
class _InputEmailState extends State<InputEmail> {
final myController = new TextEditingController();
This is the part where I define the input, and this is where I import the class in the button widget:
import 'package:login_minimalist/widget/inputEmail.dart';
When I try and reference the myController.text value, I get the error
The getter 'myController' isn't defined for the class '_ButtonLoginState'.
import 'package:flutter/material.dart';
import 'package:login_minimalist/widget/inputEmail.dart';
class ButtonLogin extends StatefulWidget {
#override
ButtonLoginState createState() => ButtonLoginState();
}
class ButtonLoginState extends State<ButtonLogin> {
#override
Here is the button widget code:
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 40, right: 50, left: 200),
child: Container(
alignment: Alignment.bottomRight,
height: 50,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.blue[300],
blurRadius: 10.0, // has the effect of softening the shadow
spreadRadius: 1.0, // has the effect of extending the shadow
offset: Offset(
5.0, // horizontal, move right 10
5.0, // vertical, move down 10
),
),
],
color: Colors.white,
borderRadius: BorderRadius.circular(30),
),
child: FlatButton(
onPressed: () {
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
// Retrieve the text the user has entered by using the
// TextEditingController.
content: Text(InputEmailState.getUsername),
);
},
);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Sign in',
style: TextStyle(
color: Colors.lightBlueAccent,
fontSize: 14,
fontWeight: FontWeight.w700,
),
),
Icon(
Icons.arrow_forward,
color: Colors.lightBlueAccent,
),
],
),
),
),
);
And here is the Input code:
class InputEmailState extends State<InputEmail> {
final myController = new TextEditingController();
getUsername() {
return(myController.text);
}
#override
void dispose() {
// Clean up the controller when the widget is disposed.
myController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 50, left: 50, right: 50),
child: Container(
height: 60,
width: MediaQuery.of(context).size.width,
child: TextField(
controller: myController,
style: TextStyle(
color: Colors.white,
),
decoration: InputDecoration(
border: InputBorder.none,
fillColor: Colors.lightBlueAccent,
labelText: 'Student ID',
labelStyle: TextStyle(
color: Colors.white70,
fontSize: 20,
),
),
),
),
);
}
If I understand your question correctly, you're trying to access _ButtonLoginState from another class/file. However, in Dart, classes, members, variables, etc. that begin with an underline ("_") are considered private. You can't access them from a different file (except in some special situations with libraries).
To solve this, you can change the name of the class to ButtonLoginState and it should work.
EDIT: In response to more info:
You don't seem to have fully understood the concepts of State in a StatefulWidget. I would strongly recommend taking a good look through the Flutter guide on the subject.
There are many different ways of managing state and what I am going to explain is almost definitely not the best option most of the time (this was the introductory approach some time ago, but I can't even find the example anymore), however, it does work. For a more general option, I recommend Provider.
In your case, the problem starts with this: Text(InputEmailState.getUsername). You're not calling InputEmailState.getUsername, you're simply passing a reference to it. You need to include parentheses to actually call it - InputEmailState.getUsername().
However, this isn't the whole issue. You're trying to access this function using the name of the class, which means you're trying to use it as a static method. However, its an instance method (ie: you need a specific instance of the class to access it. This is the state I was talking about.
To simply get access to the state object of a specific widget, you can use a Key (generally a GlobalKey). You can define this in a parent widget, for example, and pass it as the key parameter of your InputEmail widget and keep a reference in the parent class. Then, to get the username, you can call <key>.currentState.getUsername() which will return the instance value. The exact implementation varies, and I don't have your code to know how it should be implemented.
As I say, this isn't really the recommended approach anymore. I strongly recommend getting to grips with the state concept, then it should be obvious what the best approach is.

getSelectedText on inactive InputConnection flutter

I have this weird problem,When i try to enter value to textfield the keyboard comes up when i try to type the the keyboard goes away , i am also doing fill up Username,Place,mobile that i fetching from sharedprefrence and it's working,but when i try to enter age,height,weight keyboard comes up & goes with in seconds,this is the error/warning i am getting,there is no problem in flutter doctor,
W/IInputConnectionWrapper(23904): getSelectedText on inactive InputConnection
W/IInputConnectionWrapper(23904): getTextAfterCursor on inactive InputConnection
W/IInputConnectionWrapper( 8756): beginBatchEdit on inactive InputConnection
W/IInputConnectionWrapper( 8756): endBatchEdit on inactive InputConnection
Code & View
class RegistrationScreenState extends State<RegistrationScreen> {
TextEditingController mobile = TextEditingController();
TextEditingController Username = TextEditingController();
TextEditingController Place = TextEditingController();
TextEditingController age = TextEditingController();
TextEditingController height = TextEditingController();
TextEditingController weight = TextEditingController();
void initState() {
getDetails();
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery
.of(context)
.size;
return Scaffold(
appBar: AppBar(
title: Text("Registration", style: TextStyle(color: Colors.black)),
backgroundColor: Colors.orange,
),
body: SingleChildScrollView(
child: Stack(
children: [
Column(
children: [
Container(
child: Image.asset('assets/images/gym.png',
height: 150,
width: double.infinity,
fit: BoxFit.fitWidth),
),
SizedBox(
height: 50,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: Username,
decoration: InputDecoration(
prefixIcon: Icon(Icons.perm_identity),
),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: mobile,
decoration: InputDecoration(
prefixIcon: Icon(Icons.mobile_screen_share),
),
),
),
),
],
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
"User Information",
style: TextStyle(
fontSize: 15, fontWeight: FontWeight.bold),
)),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: Place,
decoration: InputDecoration(),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: age,
decoration: InputDecoration(
hintText: 'Age',
),
),
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: height,
decoration: InputDecoration(
hintText: 'Height(in cm)',
),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: weight,
decoration: InputDecoration(
hintText: 'Weight(in kg)',
),
),
),
),
],
),
],
),
],
),
),
);
}
// get & fill Shareprefrece data to textfield
getDetails() async {
Future user = SharedPrefrence().getUserMobile();
Future name = SharedPrefrence().getUserName();
Future place = SharedPrefrence().getUserPlace();
user.then((data) async {
var mobile_no = data;
setState(() {
if (mobile_no.isNotEmpty) {
mobile.text = mobile_no;
}
else
{
mobile.text = "";
}
});
});
name.then((data) async {
var user_name = data;
setState(() {
if (user_name.isNotEmpty) {
Username.text = user_name;
}
});
});
place.then((data) async {
var user_place = data;
setState(() {
if (user_place.isNotEmpty) {
Place.text = user_place;
}
});
});
}
}
I am not sure that if this will help you but I was also facing the same issue.
I was getting this error when I was using the TextFormField with in Form and the issue was that I created the global form key with in the build method.
class _AddTaskState extends State<AddTask> {
final _formKey = GlobalKey<FormState>(); // <-
String name;
String description;
#override
Widget build(BuildContext context) {
// first I was using that here.
return Scaffold(
In my case I had TextFeild in AnimatedSwitcher. The problem was i had key: UniqueKey() inside my widget that containes the TextFeild. So When ever i touched the text feid UniqueKey() gets called and then the widget tree gets regernated.
If you are using form validator, move your form key out of your build context. This should solve your issue.
Instead of -
class SingInScreen extends StatelessWidget {
final _sigInFormKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
Size size = getRelativeSize(context);
Use this-
`final _sigInFormKey = GlobalKey<FormState>();
class SingInScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
Size size = getRelativeSize(context);`
Known issue, tracked here,it's still there. There is no explanation on what's causing it.
I did a little bit of digging. It looks like Android is generating these warnings because we are holding the InputConnection incorrectly in the Engine's TextInputPlugin. I haven't really figured out what we're doing wrong, though.
source
I was also getting the same issue when tapping on the TextField.
Use auto focus = true, and issue will be resolved.
TextField( autofocus: true, );
I "forced" the formatting, in my case for example my form controller was an integer so I did it :
TextFormField(
controller: _formTextControllers.mycontroller,
keyboardType: TextInputType.number,
decoration: InputDecoration(
labelText: 'label',
),
inputFormatters: [
**FilteringTextInputFormatter.digitsOnly,**
MyInputFormatter(),
],
),
Try using the textfield methods like onChanged, onSubmitted,...
Also try using focus nodes.
Use
FocusScope.of(context).requestFocus(FocusNode());
I've been scratching my head over the same issue.
The thing that resolved it for me was removing the media query from within the build method. I guess calling the keyboard messes with this if its running inside the build method and funny things happen.
Size size = MediaQuery
.of(context)
.size;
Try doing without it, or passing it into the widget as a static property above the build method
So okay I have found a solution for this in my use case.
My keyboard used to disappear as soon as it appeared on my screen. This not the bug in the framework, let us understand what happens in the background.
When the keyboard pops up, the screen needs to rebuild its components accordingly, so it forces a rebuild. Therefore the entire widgets are re-rendered including the TextFormField or TextField, which closes the Keyboard.
To solve this problem, we have to find a way to uniquely identify our widget which is rebuilding, and stop flutter from recreating that widget.
This can be done by using keys, first find the parent widget under which our TextFormField/TextField falls, and provide a unique key to it. Remember NOT to initialize the key on the key parameter of the widget. Instead declare it outside the build method and then use it. This makes sure that the unique does not change every time the build method is called.
I hope that this helps.
Thank You!
In my case, I had this problem while my array of TextFormFields were children of DataCells of a DataTable which this DataTable was itself child of a ExpansionPanel widget.
The problem got fixed when I replaced the parent ExpansionPanelList with a Column and removed the ExpansionPanel widget.
I thought that explaining my own case might help somebody with similar issues.
In my case, I was using a global navigation key for navigation. when I was on an inner page the moment I touch Textfeild or TextFormFeild it pop to the navigation initial root, the problem that cause this problem was I declared the Navigation module inside a Stateless widget. When I changed it to stateful the issue is solved.

Flutter ListView doesnt update when the underlying list is updated

I am building this card game app which has a list of Cards (containers with special effects) and the list is managed and updated by the Provider/Consumer mechanism.
It looks like this
class LowerSectionWithScrollingCardList extends StatefulWidget {
#override
_LowerSectionWithScrollingCardListState createState() =>
_LowerSectionWithScrollingCardListState();
}
class _LowerSectionWithScrollingCardListState
extends State<LowerSectionWithScrollingCardList> {
#override
Widget build(BuildContext context) {
return Consumer<GameState>(builder: (context, gameState, child) {
print('lower list ${gameState.gcurrentPlayers[0].ownList}');
return Expanded(
flex: 34,
child: Container(
color: Colors.white,
child: ListView(
children: gameState.gcurrentPlayers[0].ownList,
scrollDirection: Axis.horizontal,
),
),
);
});
}
}
gameState.gcurrentPlayers[0].ownList is the first player which is us, and ownlist is the actual list of widgets or cards which gets updated by clicking some other buttons in the app.
the list is updated by this method exactly
void ggiveCardToCurrentPlayer(int howMuch){
for(int i=0;i<howMuch;i++)
ggetPlayerWithCurrentTurn().ownList.add(gplayingCards.removeLast());
notifyListeners();
}
Now after the "notifylisteners" is called, I am 100% sure that the Consumer is updated with the new data, because the print statement in the build method prints the newly added cards.
Finally, the issue is that the listView itself doesn't update while the list it renders has those added cards.
I checked out a few posts regarding a similar issue and they suggest that one add keys to the Data items, in my case the data items are my cards, and I added keys to them. No change.
class RegularUnoCard extends StatelessWidget{
final Color _color;
final String _value;
final Key _key;
RegularUnoCard(this._color, this._value,this._key);
#override
Widget build(BuildContext context) {
return Container(
key: _key,
margin: EdgeInsets.symmetric(
vertical: _cardMarginVer, horizontal: _cardMarginHor),
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(_cardCornerRadii),
border: Border.all(color: _color, width: 4, style: BorderStyle.solid),
boxShadow: [
BoxShadow(
color: _color,
spreadRadius: (_value == plus2) ? 8 : 2,
blurRadius: 5)
],
color: Colors.white,
),
child: Container(
height: _cardHeight,
width: _cardWidth,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(60),
color: _color,
),
child: Center(
child: getLogo(),
),
),
);
}
I hope this the correct way of putting keys in the Cards.
I also read that one must call setState() but I dont have any place to call Setstate from within my listView.
I have tried replacing the ownList logic with Provider.of(context).playerlist[0].ownlist etc etc but that too doesnt work
I hope I have supplied enough data for this evaluation. Please comment if more information is required. Thanks a lot for your time and suggestions.
I read more about the problem, the source which was helpful was this
Basically my list was being updated but I was providing the reference of the array, and since flutter works on immutable data, it did not detect my array change. So all I had to do was to build a new list from the existing reference array.
children: List<Widget>.from(gameState.gcurrentPlayers[0].ownList),
The final ListView should look like
#override
Widget build(BuildContext context) {
return Consumer<GameState>(builder: (context, gameState, child) {
print('lower list ${gameState.gcurrentPlayers[0].ownList}');
return Expanded(
flex: 34,
child: Container(
color: Colors.white,
child:ListView(
children: List<Widget>.from(gameState.gcurrentPlayers[0].ownList),
scrollDirection: Axis.horizontal,
),
),
);
},key: UniqueKey(),);
}
}
Now my card game is updating with the new cards!!

How do I programmatically open and close drawer using keys in Flutter?

I am looking to open and close a drawer programmatically when I press on an icon. I am trying to use keys, but I am not having much luck. I found this article - Flutter: How to open Drawer programmatically ,but I am having issues.
I have a column of icons (see screenshot) that I include in one file and my drawer is in another, both of which are obviously included in a third (which is a fullscreen google map). I am looking at using a global key, but I am unsure where to put everything.
In my map page I have
class _TheMapState extends State<TheMap> with WidgetsBindingObserver {
GlobalKey<ScaffoldState> _drawerKey = GlobalKey();
...
...
When I try to add the key in my drawer page, it doesn't recognise it, so I add the GlobalKey line to my drawer page. I also have to do that to my icon options page too, otherwise the app won't build, due to the errors. There are no errors when I do this, but nothing happens when I click on the button.
Here is where I added the key to the drawer.
Widget mapDrawer() {
return Drawer(
key: _drawerKey,
...
...
Here is my icon code, which is in a separate file.
Widget _buildShowHideDrawerIcon() {
return Container(
child: Opacity(
opacity: 0.60,
child: Container(
padding: EdgeInsets.all(7.0),
decoration: BoxDecoration(
color: Colors.white,
//border: Border.all(color: Colors.black12, width: 1.0),
boxShadow: [
BoxShadow(
color: Colors.black,
offset: Offset(0.0, 0.0),
blurRadius: 3.0,
),
],
),
child: InkWell(
onTap: () {
print("SHOW/HIDE DRAWER ICON PRESSED");
setState(() {
_drawerKey.currentState.openDrawer();
});
},
child: Icon(
Icons.swap_horiz,
color: Colors.black87,
size: 24.0,
semanticLabel: 'Show ,or hide sliding drawer',
),
),
),
),
);
}
I would appreciate any help on this.
Assign the key to Scaffold, not Drawer
Scaffold(
key: _drawerKey,
drawer: Drawer(),
You should assign the key to Scaffold, not Drawer
Scaffold(
key: _drawerKey,
And replace the line:
_drawerKey.currentState.openDrawer();
With
_drawerKey.currentState?.openDrawer();