Related
I have created a contact form with a few textformfields. When I'm using the keyboard I want to also use the the arrows at the top of the keyboard to move to next textformfield. I have already implemented TextInputAction.next so that when clicking on "return" you go to next textformfield, but I also want to include the arrows to do the same to go up/down.
The image below shows the arrows I want to include action on.
This is my current textformfield code:
Widget _createTextField(controller, keyboardType, int maxLength, int maxLines, String hintText, double screenWidth) {
return Expanded(
child: TextFormField(
scrollPadding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
textCapitalization: TextCapitalization.sentences,
textInputAction: TextInputAction.next,
style: TextStyle(fontSize: (screenWidth > maxMobileScreenWidth) ? 16 : 12),
controller: controller,
keyboardType: keyboardType,
maxLength: maxLength,
maxLines: maxLines,
validator: (value) {
if (value!.isEmpty) {
return '* Required field';
}
return null;
},
decoration: InputDecoration(
counterText: "",
filled: true,
hintText: hintText,
hintStyle: const TextStyle(color: Colors.black45),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(10.0)),
fillColor: const Color(0xff9ccb40),
),
),
);
}
You must use the Form() widget, on top of all your TextFromField.
you can use Form(child:Column(children:[])
suggest you to read this article
I just start to learn flutter, and I'm curious about how can i setState() dynamically in flutter
in React Native i usually used a function like this to setState dynamically:
class foo extends React.Component{
state={
username:null,
password:null
}
toggleSetState(whatState, value){
this.setState({ [whatState]: value })
}
render() {
return (
<View>
<TextInput
value={this.state.username}
onChangeText={(text)=>{toggleSetState(username, text)}
/>
<TextInput
value={this.state.password}
onChangeText={(text)=>{toggleSetState(password, text)}
/>
</View>
);
}
}
what is an equivalent of above code in Flutter?
I've tried this in Flutter but it seems doesn't work
class _LoginFormState extends State<LoginForm> {
String username, password;
void ToogleState(typedata, text){
setState(() {
typedata = text;
});
}
//Widget
TextField(
onChanged: (text){ToogleState(username, text); print(username);},
decoration: InputDecoration(
hintText: 'input username', labelText: 'Username'
),
),
}
after some research and trying, i can achieve what i want with the code below:
class _LoginFormState extends State<LoginForm> {
String username, password;
//create an object
var loginForm = {};
final myController = TextEditingController();
void ToogleState(typedata, text){
setState(() {
//i can assign any different variable with this code
loginForm[typedata] = text;
//output of LoginForm: {username: foo, password: bar}
});
}
//widget
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
TextField(
onEditingComplete: (){print(loginForm);},
onChanged: (text){ToogleState("username", text); print(loginForm['username']);},
decoration: InputDecoration(
hintText: 'input username', labelText: 'Username'
),
),
TextField(
onEditingComplete: (){print(loginForm);},
onChanged: (text){ToogleState("password", text); print(loginForm['password']);},
decoration: InputDecoration(
hintText: 'input password', labelText: 'Password'
),
),
],
),
);
}
You just need to make a variable to hold the value. I am confused why you are calling setState when you are not modifying ephemeral state
Here are some helpful docs
https://flutter.dev/docs/development/data-and-backend/state-mgmt/ephemeral-vs-app
class _LoginFormState extends State<LoginForm> {
String _username = "";
String __password = "";
void ToogleState( text){
setState(() {
_username = text;
});
}
//Widget
TextField(
onChanged: (text){ToogleState( text); print(username);},
decoration: InputDecoration(
hintText: 'input username', labelText: 'Username'
),
),
}
I am trying to add done button in number type input for a TextFormField in flutter but I could not able to do that.
TextFormField(
key: Key(keyValue),
initialValue: valueBuilder,
onSaved: (text) {
fieldsController.text = text.trim();
},
inputFormatters: [inputFormatters],
keyboardType: TextInputType.phoneNumber,)
I want create a keyboard like the below. For the input text form field.
Change
keyboardType: TextInputType.number
to
keyboardType: TextInputType.numberWithOptions(signed: true, decimal: true)
You don't need a done button just wrap MaterialApp with a GestureDetector
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus &&
currentFocus.focusedChild != null) {
FocusManager.instance.primaryFocus.unfocus();
}
},
child: MaterialApp(
title: "My title",
home:MyHomeScreen(),
),
);
I've just created a package for add basic actions to the current keyboards .
You can take a look here :
https://pub.dartlang.org/packages/keyboard_actions
Usage :
import 'package:flutter/material.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
//...
FocusNode _nodeText1 = FocusNode();
FocusNode _nodeText2 = FocusNode();
FocusNode _nodeText3 = FocusNode();
FocusNode _nodeText4 = FocusNode();
FocusNode _nodeText5 = FocusNode();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Keyboard Actions Sample"),
),
body: FormKeyboardActions(
keyboardActionsPlatform: KeyboardActionsPlatform.ALL, //optional
keyboardBarColor: Colors.grey[200], //optional
nextFocus: true, //optional
actions: [
KeyboardAction(
focusNode: _nodeText1,
),
KeyboardAction(
focusNode: _nodeText2,
closeWidget: IconButton(
icon: Icon(Icons.close),
onPressed: () {},
),
),
KeyboardAction(
focusNode: _nodeText3,
onTapAction: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text("Custom Action"),
actions: <Widget>[
FlatButton(
child: Text("OK"),
onPressed: () => Navigator.of(context).pop(),
)
],
);
});
},
),
KeyboardAction(
focusNode: _nodeText4,
displayCloseWidget: false,
),
KeyboardAction(
focusNode: _nodeText5,
closeWidget: Padding(
padding: EdgeInsets.all(5.0),
child: Text("CLOSE"),
),
),
],
child: Padding(
padding: const EdgeInsets.all(15.0),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
TextField(
keyboardType: TextInputType.number,
focusNode: _nodeText1,
decoration: InputDecoration(
hintText: "Input Number",
),
),
TextField(
keyboardType: TextInputType.text,
focusNode: _nodeText2,
decoration: InputDecoration(
hintText: "Input Text with Custom Close Widget",
),
),
TextField(
keyboardType: TextInputType.number,
focusNode: _nodeText3,
decoration: InputDecoration(
hintText: "Input Number with Custom Action",
),
),
TextField(
keyboardType: TextInputType.text,
focusNode: _nodeText4,
decoration: InputDecoration(
hintText: "Input Text without Close Widget",
),
),
TextField(
keyboardType: TextInputType.number,
focusNode: _nodeText5,
decoration: InputDecoration(
hintText: "Input Number with Custom Close Widget",
),
),
],
),
),
),
),
);
}
add this to your TextField
TextField(
...
keyboardType: TextInputType.numberWithOptions(signed: true),
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
],
...
),
This is how to add Done button to your TextFormField when keyboard is opened:
textInputAction: TextInputAction.done,
Please keep in mind that iOS doesn’t support Done on NUMERIC keyboards.
I used this way to handle done button in IOS
keyboardType: Platform.isIOS?
TextInputType.numberWithOptions(signed: true, decimal: true)
: TextInputType.number,
// This regex for only amount (price). you can create your own regex based on your requirement
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,4}'))],
There is no done button in ios keyboard. Solution is that use "keyboard_actions" plugin otherwise add layout top of keyboard check this
https://blog.usejournal.com/keyboard-done-button-ux-in-flutter-ios-app-3b29ad46bacc
I used the signed option to display the done button. Then I used a TextInputFormatter with a RegEx to limit what the user can input.
new TextFormField(
controller: myController,
keyboardType: TextInputType.numberWithOptions(signed:true, decimal: true),
textInputAction: TextInputAction.done,
onFieldSubmitted: (term) {
_showSnackBar(context, double.parse(myController.text));
},
onEditingComplete: () {
_showSnackBar(context, double.parse(myController.text));
},
decoration: const InputDecoration(
helperText: "Actual",
hintText: "Units used",
border: UnderlineInputBorder(),
icon: Icon(Icons.keyboard),
),
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d*$')),
],
),
There is no done button in iOS but we can check the length of the input and clear focus to hide the numberpad keyboard.
Implement like below,(it'll work with fix length of number value)
onChanged: (val) {
if (val.length == 10) { //10 is the length of the phone number you're allowing
FocusScope.of(context).requestFocus(FocusNode());
}
}
I'm facing a small problem. As you can see, i have set maxLength 1 of TextField in Flutter, But i'm unable to hide bottom label of text counter.
To hide counter value from TextField or TextFormField widget while using maxLength attribute, try following:
TextField(
decoration: InputDecoration(
hintText: "Email",
counterText: "",
),
maxLength: 40,
),
In this, I have set counterText attribute inside InputDecoration property with empty value. Hope it will help.
This is the proper approach, it will prevent extra space below the Text Field, and also avoid extra code to set a null widget.
You can use input formatters in TextField
The following is:
inputFormatters:[
LengthLimitingTextInputFormatter(1),
]
Thank You!
you can use InputDecoratoin to hide letter counter.
TextFormField(
decoration: InputDecoration(
labelText: "username",
counterStyle: TextStyle(height: double.minPositive,),
counterText: ""
)
You can hide the counter by adding counterText: '', inside the textfield decoration. It will simply display an empty String.
Simply set counter to Offstage() will do the trick.
TextField(
maxLines: 1,
decoration: InputDecoration(
counter: Offstage(),
),
),
You can do:
TextField(
maxLength: 10,
buildCounter: (BuildContext context, { int currentLength, int maxLength, bool isFocused }) => null,
)
Most of the answers seem to work. Another way would be to assign the counter with a shrink SizeBox.
TextField(decoration: InputDecoration(
hintText: "Email",
counter: SizedBox.shrink()
),
maxLength: 40,
),
Add counterText: "", to InputDecoration
TextField(
decoration: InputDecoration(
counterText: "",
),
maxLength: 10,
),
Whenever you don't need something in flutter just put an empty container!
TextField(
decoration: InputDecoration(
hintText: "Email",
counter: Container(),
),
maxLength: 20,
),
Simply set buildCounter to null.
It is a callback that generates a custom [InputDecorator.counter] widget
TextField(
maxLength: (some length),
buildCounter: (BuildContext context, {int currentLength, int maxLength, bool isFocused}) => null,
);
This alone solved my problem!
TextField(
decoration: InputDecoration(
labelText: "Table Number",
counterText: ""
)
For null-safety use this:
TextField(
maxLength: 10,
buildCounter: (BuildContext context, {int? currentLength, int? maxLength, bool? isFocused}) => null,
)
You can use input formatters in TextField setting a limit to input and this is the best approach if you just want to hide the counter making a input limit.
import 'package:flutter/services.dart';
inputFormatters:[
LengthLimitingTextInputFormatter(1),
]
Or you can customize the decoration making a counter = Container():
decoration: InputDecoration(
hintText: "Email",
counter: Container(),
),
You if prefer to customize the buildCounter, here is how to do it properly (you can also customise the font, color etc). When you text field loses the focus the counter limits will disappears. Or you can just set
TextField(
controller: _clienteTextEditingController,
maxLength: 50,
buildCounter: (BuildContext context,
{int currentLength, int maxLength, bool isFocused}) {
return isFocused
? Text(
'The Input Limits are: $currentLength/$maxLength ',
style: new TextStyle(
fontSize: 10.0,
),
semanticsLabel: 'Input constraints',
)
: null;
},
),
you can use InputDecoratoin to hide the letter counter.
TextField(
keyboardType: TextInputType.number,
maxLength: 10,
decoration: InputDecoration(
**counterText:""**)
)
Another solution is to hide the counter widget by using SizedBox:
TextFormField(
...
decoration: InputDecoration(
contentPadding: EdgeInsets.all(10.0),
counter: new SizedBox(
height: 0.0,
)),
...
)
You can follow the below codes.
TextField(
controller: myController,
maxLength: 3,
buildCounter: (BuildContext context, {int currentLength, int maxLength, bool isFocused}) =>null
)
Container(
height: 48,
alignment: Alignment.centerLeft,
padding: EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
border: Border.all(
color: CustomColors.kToDark,
),
color: CustomColors.White,
borderRadius: BorderRadius.all(Radius.circular(8))),
child: TextFormField(
textAlign: TextAlign.left,
cursorColor: CustomColors.kToDark,
maxLength: 30,
controller: _titleController,
keyboardType: TextInputType.text,
decoration: InputDecoration(
counterText: "",
border: InputBorder.none,
isDense: true,
contentPadding: EdgeInsets.all(0))),
),
Use counterText: "" inside InputDecoration()
In case you are still looking for the answer in 2020 here goes:
decoration: InputDecoration(
counter: Spacer(),
labelText: "National ID #",
border: InputBorder.none,
hintText: 'e.g 01-1234567A12',
hintStyle: TextStyle(color: Colors.grey)),
In the TextField, use a Spacer() as counter... hope this helps, i dont know if it breaks anything else but mine works fine.
Not pretty but works by removing the counter:
TextFormField(
buildCounter: (
BuildContext context, {
required int currentLength,
int? maxLength,
required bool isFocused,
}) => null,
Use a SizedBox.shrink() for the counter widget in InputDecoration:
Use a SizedBox with zero height and width:
TextField(
maxLength: 400,
decoration: InputDecoration(
counter: SizedBox(
width: 0,
height: 0,
),),)
As mentioned by #user10481267 in the best answer would be use buildCounter property. This gives extreme flexibility and you can even decide dynamically to show the counter or not.
I was building a dynamic form with the required properties in JSON. My implementation is as follows:
TextFormField(
buildCounter: (BuildContext context,
{
int currentLength,
int maxLength,
bool isFocused}) {
if (isFocused)
return formFields[index]["max"] == null
? null
: Text(
'$currentLength / $maxLength',
semanticsLabel: 'character count',
);
else
return null;
},
maxLength: formFields[index]["max"] ?? 100,
decoration: new InputDecoration(
labelText: formFields[index]["hint"] ?? "",
fillColor: Colors.green,
border: new OutlineInputBorder(
borderRadius: new BorderRadius.circular(15.0),
borderSide: new BorderSide(),
),
)
)
It may sound easy but How can we do a multi-line editable textfield in flutter? TextField works only with a single line.
Edit: some precisions because seems like it's not clear.
While you can set multiline to virtually wrap the text content, it's still not multiline. It's a single line displayed into multiple lines.
If you want to do something like this then you can't. Because you don't have access to ENTER button. And no enter button means no multiline.
To use auto wrap, just set maxLines as null:
TextField(
keyboardType: TextInputType.multiline,
maxLines: null,
)
If the maxLines property is null, there is no limit to the number of lines, and the wrap is enabled.
If you want your TextField be adapted to the user input then do this:
TextField(
keyboardType: TextInputType.multiline,
minLines: 1,//Normal textInputField will be displayed
maxLines: 5,// when user presses enter it will adapt to it
);
here you can set the max lines to whatever you want and you are good to go.
In my opinion setting the maxlines to null is not a good choice that's why we should set it to some value.
Although other people already mentioned that the keyboard type "TextInputType.multiline" can be used, I wanted to add my implementation of a TextField that automatically adapts its height when a new line is entered, as it is often desired to immitate the input behaviour of WhatsApp and similar apps.
I'm analyzing the number of '\n' chatacters in the input for this purpose each time the text is changed. This seems to be an overkill, but unfortunately I didn't find a better possibility to achieve this beahivour in Flutter so far and I didn't notice any performance problems even on older smartphones.
class _MyScreenState extends State<MyScreen> {
double _inputHeight = 50;
final TextEditingController _textEditingController = TextEditingController();
#override
void initState() {
super.initState();
_textEditingController.addListener(_checkInputHeight);
}
#override
void dispose() {
_textEditingController.dispose();
super.dispose();
}
void _checkInputHeight() async {
int count = _textEditingController.text.split('\n').length;
if (count == 0 && _inputHeight == 50.0) {
return;
}
if (count <= 5) { // use a maximum height of 6 rows
// height values can be adapted based on the font size
var newHeight = count == 0 ? 50.0 : 28.0 + (count * 18.0);
setState(() {
_inputHeight = newHeight;
});
}
}
// ... build method here
TextField(
controller: _textEditingController,
textInputAction: TextInputAction.newline,
keyboardType: TextInputType.multiline,
maxLines: null,
)
TextFormField(
minLines: 2,
maxLines: 5,
keyboardType: TextInputType.multiline,
decoration: InputDecoration(
hintText: 'description',
hintStyle: TextStyle(
color: Colors.grey
),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0)),
),
),
),
While this question is rather old, there is no extensive answer that explains how to dynamically resize the TextField with little developer effort. This is especially of major importance when the TextField is either placed in a flexbox such as ListView, SingleChildScrollView, etc. (the flexbox will not be able to determine the intrinsic size of an expandable TextField).
As suggested by many other users, build your TextField like so:
TextField(
textInputAction: TextInputAction.newline,
keyboardType: TextInputType.multiline,
minLines: null,
maxLines: null, // If this is null, there is no limit to the number of lines, and the text container will start with enough vertical space for one line and automatically grow to accommodate additional lines as they are entered.
expands: true, // If set to true and wrapped in a parent widget like [Expanded] or [SizedBox], the input will expand to fill the parent.
)
How to cope with the missing intrinsic height of the TextField?
Wrap the TextField in a IntrinsicHeight class to provide the dynamically computed intrinsic height of the expandable TextField to its parent (when requested via e.g. flexbox).
1. Fixed height:
(A) Based on lines:
TextField(
minLines: 3, // Set this
maxLines: 6, // and this
keyboardType: TextInputType.multiline,
)
(B) Based on height:
SizedBox(
height: 200, // <-- TextField expands to this height.
child: TextField(
maxLines: null, // Set this
expands: true, // and this
keyboardType: TextInputType.multiline,
),
)
2. Flexible height:
Use a Column and wrap the TextField in Expanded:
Column(
children: [
Expanded(
child: TextField(
maxLines: null, // Set this
expands: true, // and this
keyboardType: TextInputType.multiline,
),
),
],
)
(Optional) Set decoration:
You can se this decoration to any of the above TextField:
decoration: InputDecoration(
hintText: 'Write a message',
filled: true,
)
You have to use this line in the TextField widget :
maxLines: null,
if didn't work , just note that you have to delete this :
textInputAction: TextInputAction.next
it's disable multi line property action in the keyboard ..
use this
TextFormField(
keyboardType: TextInputType.multiline,
maxLines: //Number_of_lines(int),)
This Code Worked for me, Also I'm able to use ENTER for web & mobile.
#override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width;
double height = MediaQuery.of(context).size.height;
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
child: ConstrainedBox(
// fit: FlexFit.loose,
constraints: BoxConstraints(
maxHeight: height,//when it reach the max it will use scroll
maxWidth: width,
),
child: const TextField(
keyboardType: TextInputType.multiline,
maxLines: null,
minLines: 1,
decoration: InputDecoration(
fillColor: Colors.blueAccent,
filled: true,
hintText: "Type ",
border: InputBorder.none,
),
),
),
)
]);
}
TextField has maxLines property.
Use Expanded widget for dynamic feels
Expanded(
child: TextField(
keyboardType: TextInputType.multiline,
minLines: 1,
maxLines: 3,
),
)
if above once not worked for you then try add minLines also
TextField(
keyboardType: TextInputType.multiline,
minLines: 3,
maxLines: null);
For autowrap just use null for maxLines
TextFormField(
keyboardType: TextInputType.multiline,
maxLines: null,
)
or
TextField(
keyboardType: TextInputType.multiline,
maxLines: null,
)
Official doc states:
The maxLines property can be set to null to remove the restriction on the number of lines. By default, it is one, meaning this is a single-line text field.
NOTE: maxLines must not be zero.
Specify TextInputAction.newline to make a TextField respond to the enter key and accept multi-line input:
textInputAction: TextInputAction.newline,
use this
Expanded(
child: TextField(
controller: textMessageController,
keyboardType: TextInputType.multiline,
textCapitalization: TextCapitalization.sentences,
minLines: 1,
maxLines: 3,
onChanged: ((value) {
setState(() {
_messageEntrer = value;
});
}),
decoration: InputDecoration(
hintText: "Type your message here",
hintMaxLines: 1,
contentPadding:
const EdgeInsets.symmetric(horizontal: 8.0, vertical: 10),
hintStyle: TextStyle(
fontSize: 16,
),
fillColor: Colors.white,
filled: true,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: const BorderSide(
color: Colors.white,
width: 0.2,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: const BorderSide(
color: Colors.black26,
width: 0.2,
),
),
),
),
),