Flutter: how to maintain TextField value after dismissing the soft Keyboard - android

There is a simple structure with a Bottom Modal Sheet that contains an input TextField to add a Text into a List and a button to submit the value.
When the text field is focused, it automatically opens the soft keyboard that covers the submit button.
When I dismiss the keyboard in order to press the button, the value in the text field is still visible but the sent value is null.
If I press the button without dismissing the keyboard, the value is correctly sent.
The question is: how can I dismiss the keyboard and still be able to send the typed value after pressing the submit button?
Here is the code:
1) On the main screen, the floating action button shows the modal bottom sheet.
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
showModalBottomSheet(
context: context,
builder: (context) => AddTaskScreen(),
},
2) On the AddTaskScreen class, there is a Column with the content of the modal bottom sheet inside a container.
Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
'Add your next Task',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.lightBlueAccent,
fontSize: 20,
fontWeight: FontWeight.w400,
),
),
TextField(
textAlign: TextAlign.center,
autofocus: true,
onChanged: (value) {
newTaskTitle = value;
},
),
FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'ADD',
style: TextStyle(
color: Colors.white,
fontSize: 25,
),
),
),
color: Colors.lightBlueAccent,
onPressed: () {
print(newTaskTitle);
},
),
],
),
),
In this simplified version, the value from the TextField is printed in the console when pressing the button. If I press the button without hiding the keyboard it works fine; If I hide the keyboard it passes a null value.
Thanks in advance for your help.

I had the same issue, I resolved it by simply converting called class to extend StatefullWidget instead of StatelessWidget.
In your case converting class AddTaskScreen() to extend StatefullWidget.

Okay, the easiest way to do this is by supplying a TextEditingController to the child class.
So for your case, you can first create a TextEditingController in the Parent Class, then pass it to the child class. And in the TextField inside child class set the controller: The controller you have passed
Parent Class.....
//// other codes ////
TextEditingController textEditingController = TextEditingController();
return Scafold{
FloatingActionButton(
onPressed: () {
showModalBottomSheet(
context: context,
builder: (context) => AddTaskScreen(textEditingController),
},
};
And in the child class
class ChildClass extends StatelessWidget(
final TextEditingController textEditingController;
ChildClass({this.textEditingController});
///then inside build method///
Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
'Add your next Task',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.lightBlueAccent,
fontSize: 20,
fontWeight: FontWeight.w400,
),
),
TextField(
textAlign: TextAlign.center,
autofocus: true,
controller: textEditingController, /// here add the controller
onChanged: (value) {
newTaskTitle = value;
},
),
FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'ADD',
style: TextStyle(
color: Colors.white,
fontSize: 25,
),
),
),
color: Colors.lightBlueAccent,
onPressed: () {
print(newTaskTitle);
},
),
],
),
),
Now you can access what was written in the TextField by simply calling textEditingController.value.text from anywhere between these two classes.

Just moving the declarations of text controllers work for me.
....
class _AddPlaceScreenState extends State<AddPlaceScreen> {
final controllerTittlePlace = TextEditingController();
final controllerDescriptionPlace = TextEditingController();
final controllerLocationPlace = TextEditingController();
....

Related

How to dynamically change text size inside a card?

I have a table filled with changed number of rows and cols.
I would like to change the size of the text inside each card to be the maximum possible size so the words will still be able to read comfortably.
The words inside the cards change each time.
Thank you.
class GameCardButton extends StatelessWidget { final String word; final Color cardColor;
GameCardButton({#required this.word, this.cardColor});
#override Widget build(BuildContext context) {
return Expanded(
child: MaterialButton(
//TODO: implement onPressed
onPressed: () {},
color: Colors.white,
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.teal),
),
child: Text(
word,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15.0,
),
),
),
); } }
Wrap the Text widget like this:
Row(
children: [
Expanded(
child: FittedBox(
child: Text(
word,
textAlign: TextAlign.center,
),
),
),
],
)

Snackbar on RaisedButton click in Flutter is not working? [duplicate]

This question already has answers here:
Scaffold.of() called with a context that does not contain a Scaffold
(15 answers)
Closed 2 years ago.
I am new to Flutter, i am creating my login Screen and i want to show a Snackbar in my screen in case of OnPress of Raised Button, But i am unable to show this message to my app. How to resolve this Problem.
I also attached the error description in following to understand the main thing, i don't know how to manage it.
I used Material Button and Flat Button instead of Raised Button but Problem did not resolved.
Image
Code
import 'package:flutter/material.dart';
import 'package:flutter_hello/Signup.dart';
main() {
runApp(MaterialApp(
// theme: ThemeData(primarySwatch: Colors.red, brightness: Brightness.light),
title: "Umar",
home: new Login(),
));
}
class Login extends StatefulWidget {
#override
_LoginState createState() => _LoginState();
}
class _LoginState extends State<Login> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.all(28),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
FlutterLogo(
size: 150,
colors: Colors.red,
),
TextFormField(
obscureText: false,
// keyboardType: TextInputType.number,
decoration: InputDecoration(
prefixIcon: Icon(Icons.person, color: Colors.grey),
hintText: 'Email',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
),
),
TextFormField(
obscureText: true,
obscuringCharacter: "*",
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock_outline, color: Colors.grey),
hintText: 'Password',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
),
),
RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
side: BorderSide(color: Colors.red)),
color: Colors.red,
textColor: Colors.white,
padding: EdgeInsets.fromLTRB(40, 8, 40, 8),
onPressed: () {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text("Sending Message"),
));
},
child: Text(
"Login",
style: TextStyle(
fontSize: 20.0,
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Don't have account?"),
Padding(
padding: EdgeInsets.all(4),
),
GestureDetector(
onTap: () => Navigator.of(context).push(
MaterialPageRoute(builder: (context) => Signup())),
child: Text(
"SignUp",
style: TextStyle(
color: Colors.red, fontWeight: FontWeight.w600),
),
),
],
)
],
),
),
),
);
}
}
Error
Here is my Error Message shown in Android studio.
Handler: "onTap"
Recognizer:
TapGestureRecognizer#07f5f
════════════════════════════════════════════════════════════════════════════════════════════════════
════════ Exception caught by gesture ═══════════════════════════════════════════════════════════════
The following assertion was thrown while handling a gesture:
Scaffold.of() called with a context that does not contain a Scaffold.
No Scaffold ancestor could be found starting from the context that was passed to Scaffold.of(). This usually happens when the context provided is from the same StatefulWidget as that whose build function actually creates the Scaffold widget being sought.
There are several ways to avoid this problem. The simplest is to use a Builder to get a context that is "under" the Scaffold. For an example of this, please see the documentation for Scaffold.of():
https://api.flutter.dev/flutter/material/Scaffold/of.html
A more efficient solution is to split your build function into several widgets. This introduces a new context from which you can obtain the Scaffold. In this solution, you would have an outer widget that creates the Scaffold populated by instances of your new inner widgets, and then in these inner widgets you would use Scaffold.of().
A less elegant but more expedient solution is assign a GlobalKey to the Scaffold, then use the key.currentState property to obtain the ScaffoldState rather than using the Scaffold.of() function.
The context used was: Login
state: _LoginState#f029c
When the exception was thrown, this was the stack:
#0 Scaffold.of (package:flutter/src/material/scaffold.dart:1451:5)
#1 _LoginState.build.<anonymous closure> (package:flutter_hello/main.dart:57:28)
#2 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:992:19)
#3 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:1098:38)
#4 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:184:24)
...
Handler: "onTap"
Recognizer: TapGestureRecognizer#07f5f
debugOwner: GestureDetector
state: possible
won arena
finalPosition: Offset(196.3, 545.3)
finalLocalPosition: Offset(81.3, 20.2)
button: 1
sent tap down
Do it like this,
void showInSnackBar(String value) {
FocusScope.of(context).requestFocus(new FocusNode());
_scaffoldKey.currentState?.removeCurrentSnackBar();
_scaffoldKey.currentState.showSnackBar(new SnackBar(
content: new Text(
value,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
),
),
backgroundColor: Colors.blue,
duration: Duration(seconds: 3),
));
}
The Problem is that the context that you are using does not contain a Scaffold, because the Scaffold is created after the context. You can fix this problem by using a Builder-Widget to wrap your content.
Like this:
class _LoginState extends State<Login> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Builder(
builder: (context) {
// return your body here
},
),
);
}
}

Flutter listview: when removing an Item, the next one inherits some properties

I'm working on a proyect where I have a ListView of custom widgets named AlarmCard, each of them is populated with the information of an Alarm object.
An AlarmCard has two boolean propierties: Expanded and Loading, and this is the main code:
class _AlarmCardState extends State<AlarmCard>{
bool _expanded = false;
bool _loading = false;
static const double iconSize = 28.0;
static const Color iconColor = Colors.black87;
static const TextStyle fieldTextStyle = TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w400,
color: Color.fromRGBO(0, 0, 0, 0.6),
);
static const TextStyle clearFieldTextStyle = TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w400,
color: Color.fromRGBO(0, 0, 0, 0.2),
);
#override
Widget build(BuildContext context) {
TextStyle timeTextStyle = TextStyle(
fontSize: 54.0,
fontWeight: FontWeight.w400,
color: Colors.black54,
);
Widget card = Card(
child: Column(
children: <Widget>[
Opacity(
opacity: _loading ? 1.0 : 0.0,
child: Container(
height: 3.0,
child: ClipRRect(
child: LinearProgressIndicator(),
borderRadius: BorderRadius.only(
topRight: Radius.circular(3.0),
topLeft: Radius.circular(3.0),
),
),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(12.0, 8.0, 12.0, 8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 6.0, bottom: 8.0),
child: InkWell(
onTap: () => _selectTime(context),
child: Text(widget.alarm.timeString, style: timeTextStyle)
),
),
Switch(value: widget.alarm.enabled, onChanged: _onAlarmEnabledChange),
],
),
AnimatedCrossFade(
firstChild: _getContractedBottom(),
secondChild: _getExpandedBottom(),
duration: Duration(milliseconds: 200),
crossFadeState: _expanded ? CrossFadeState.showSecond : CrossFadeState.showFirst,
)
],
),
),
],
),
);
return _loading ?
Opacity(
opacity: 0.6,
child: IgnorePointer(
child: card
),
)
: card;
}
...
}
Of course, there are some more custom widgets as DayToggle and methods as _getContractedBottom() and _getExpandedBottom() which returns a widget.
Once we have defined the AlarmCard build method, which depends on _expanded and _loading, we build the ListView using final List<Alarm> alarmList as:
ListView.builder(
itemCount: alarmList.length,
itemBuilder: (BuildContext context, int i) {
Alarm alarm = alarmList[i];
return AlarmCard(
alarm: alarm,
ringtones: ringtoneList,
onAlarmUpdated: () => true,
onAlarmDeleted: () {
setState(() {
alarmList.remove(alarm);
});
return true;
}
);
},
)
So, the ListView works perfectly, and each item is shown as it should, it can be expanded and updated (so that _loading turns true). The problem comes when an item is deleted.
Imagine alarmList has 8 items, all of them are shown on the screen, and we expand and delete just the 6th item. The moment we press Delete, the AlarmCard turns to Loading state, and then is deleted, but once it's removed from the ListView, the 7th item 'inherits' the _expanded and _loading propierties, so it is shows the right information which corresponds to it's alarm, but it's expanded and loading, while it shouldn't.
Does somebody know where the problem could be?
I've been searching on multiple websites and stackoverflow posts but couldn't find this problem.
Thanks a lot!
as #javired98 said, the problem is in key. that video help me. but remember if you have list of your widget, the key must pass to widget when you add it to your list not define in widget class.
imageList.add(ImageElement(
key:ValueKey(imageCounter),
id: ...,
imgURL: ...,
.
.
.
));

Android like Textfield Validation in Flutter?

I would like to achieve Android like TextField validation in Flutter.
I tried the TextField docs of Flutter and the InputDecoration has a property of errorText but is displays error at the bottom of textfield. I want to achieve something like below in Flutter
You can use a TextFormField along with a custom Tooltip inside a Stack to achieve this effect. In the decoration property for the TextFormField you can use the suffixIcon property of the InputDecoration class to pass the error icon.
And you can use a bool variable to show/hide the tooltip message when the validation error occurs.
Example code for the TextFormField:
TextFormField(
decoration: InputDecoration(
//Set the different border properties for a custom design
suffixIcon: IconButton(
icon: Icon(Icons.error, color: Colors.red),
onPressed: () {
setState(() {
showTooltip = !showTooltip; //Toggles the tooltip
});
},
),
),
validator: (String value) {
if(value.isEmpty) {
setState(() {
showTooltip = true; //Toggles the tooltip
});
return "";
}
}
);
You can wrap the above code along with your custom tooltip widget code with a Stack to achieve the error tooltip effect.
Below is a quick working example. You can design your own custom tooltip widget and use it in your code.
Example:
class LoginAlternate extends StatefulWidget {
#override
_LoginAlternateState createState() => _LoginAlternateState();
}
class _LoginAlternateState extends State<LoginAlternate> {
GlobalKey<FormState> _formKey = GlobalKey();
bool showTooltip = false;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blueGrey,
body: Container(
padding: EdgeInsets.symmetric(
horizontal: 100,
vertical: 100
),
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
Stack(
alignment: Alignment.topRight,
overflow: Overflow.visible,
children: <Widget>[
TextFormField(
decoration: InputDecoration(
filled: true,
fillColor: Colors.white,
border: OutlineInputBorder(
borderSide: BorderSide.none
),
suffixIcon: IconButton(
icon: Icon(Icons.error, color: Colors.red,),
onPressed: () {
setState(() {
showTooltip = !showTooltip;
});
},
),
hintText: "Password"
),
validator: (value) {
if(value.isEmpty) {
setState(() {
showTooltip = true;
});
return "";
}
},
),
Positioned(
top: 50,
right: 10,
//You can use your own custom tooltip widget over here in place of below Container
child: showTooltip
? Container(
width: 250,
height: 50,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all( color: Colors.red, width: 2.0 ),
borderRadius: BorderRadius.circular(10)
),
padding: EdgeInsets.symmetric(horizontal: 10),
child: Center(
child: Text(
"Your passwords must match and be 6 characters long.",
),
),
) : Container(),
)
],
),
RaisedButton(
child: Text("Validate"),
onPressed: () {
_formKey.currentState.validate();
},
),
],
),
),
),
);
}
}
I think you are talking about ToolTip
You can use this library or Go through Flutter doc
new Tooltip(message: "Hello ToolTip", child: new Text("Press"));
you can use the library super_tooltip #
You can customize this type of error by using overlay widget. Error can be shown with overlay widget and that error icon can be changed using inputDecoration of TextField.
Here is the link for understanding overlay widget--
https://medium.com/saugo360/https-medium-com-saugo360-flutter-using-overlay-to-display-floating-widgets-2e6d0e8decb9

What is the best way of formatting text in this Flutter example?

I have a card that I tap to toggle DND on or off. On the card, at the moment, I have a string of text that says either "DND ON" or "DND OFF". What I am trying to achieve is to add, below "DND ON", in smaller fontsize and in italics: "Alarms, Media, Touch Sounds".
My bare bones code:
Card(
color: Color.fromARGB(255, 76, 175, 80),
elevation: 5.0,
child: InkWell(
splashColor: Colors.blueGrey,
onTap: () {
setState(() {
pressed = !pressed;
});
pressed ? _dndOn() : _dndOff();
},
child: Center(
child: Text(
pressed ? ('DND ON') : ('DND OFF'),
style: TextStyle(
color: Colors.white,
fontSize: 25.0,
),
),
),
),
),
What I have tried: I have tried defining my string in RichText, using TextTheme, and creating a column of text with various styles. Every time I get an error such as "type 'Column' is not a subtype of type 'String'" or "type 'RichText' is not a subtype of type 'String' etc.
Would you have any ideas as to how I could achieve what I want?
You can't use a column widget as the text property of the Text widget. You need to use it in a way where you define the list of children to hold, in this case, the list of Text widgets.
Card(
color: Color.fromARGB(255, 76, 175, 80),
elevation: 5.0,
child: InkWell(
splashColor: Colors.blueGrey,
onTap: () {
setState(() {
pressed = !pressed;
});
pressed ? _dndOn() : _dndOff();
},
child: Center(
child: Column(
children: [
Text(
pressed ? 'DND ON' : 'DND OFF',
style: TextStyle(
color: Colors.white,
fontSize: 25.0,
),
),
Text(
'Alarms, Media, Touch Sounds',
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
),
),
],
),
),
),
),

Categories

Resources