Flutter TextField disable input - flutter

Can I disable input in TextField with open keyboard?
I am trying TextField with readOnly property, but when I set readOnly value true my device keyboard hide. I want do this without hidding keyboard

You can try this
TextField(
readyOnly: true,
showCursor: true,
// ...
),

As for now, you can do this by adding enabled to your TextField.
TextField(
controller: _controller,
enabled: false,
);

One way to do this is like so.
class _MyWidgetState extends State<MyWidget>{
var tc = TextEditingController();
var readOnlyText = 'read only';
#override
void initState() {
super.initState();
tc.text = readOnlyText;
}
#override
Widget build(BuildContext context) {
return TextField(
controller: tc,
onChanged: (_){
tc.text = readOnlyText;
tc.selection = TextSelection.fromPosition(TextPosition(offset: tc.text.length));
},
);
}
}

Related

Button disabled state bases on multiple TextFormFields

I have a username textfield, a password textfield and a button.
When one of these fields are empty, the button should be disabled (onPressed null). When both of these fields contains text, the button should be enabled (onPressed not null).
A very basic usecase, which I cannot find a solution for.
ValueListenableBuilder only works for a single TextEditingController, and Listenable.merge cannot be uses on multiple TextEditingController´s either.
I read another solution where you could wrap multiple ValueListenableBuilders into another widget, and then use this widget. But this seems to "complex" for me for such a simple problem, and this is not a nice scaleable solution.
I´ve tried creating a getter:
get loginButtonEnabled {
if (_usernameController.text != '' && _passwordController.text != '' && !loading) {
return true;
}
return false;
}
then use it on the button:
ElevatedButton.icon(
onPressed: loginButtonEnabled ? loginClicked : null,
label: Text('Login'),
),
and when onChanged fired on my textfields, they would call setState() to update the ui.
Problem with this is, that when the user types in some text, the textfield loses focus for some reason. And furthermore, this is not an elegant solution either.
So is there a more suitable approach to this common usecase?
You can follow this snippet, I am listening both controller. You might use StatefulWidget, for that overrideinitState and dispose state
class LogForm extends StatelessWidget {
const LogForm({super.key});
#override
Widget build(BuildContext context) {
final ValueNotifier<bool> isEnabled = ValueNotifier(false);
late final TextEditingController passController;
late final TextEditingController nameController;
nameController = TextEditingController()
..addListener(() {
if (passController.text.isNotEmpty && nameController.text.isNotEmpty) {
isEnabled.value = true;
} else {
isEnabled.value = false;
}
});
passController = TextEditingController()
..addListener(() {
if (passController.text.isNotEmpty && nameController.text.isNotEmpty) {
isEnabled.value = true;
} else {
isEnabled.value = false;
}
});
return Column(
children: [
TextFormField(
controller: nameController,
),
TextFormField(
controller: passController,
),
ValueListenableBuilder<bool>(
valueListenable: isEnabled,
builder: (context, value, child) {
return ElevatedButton(
onPressed: value ? () {} : null, child: Text("Btn"));
},
)
],
);
}
}

Flutter - How to implement inline editing?

I found a similar behaviour on Safari:
The problem statement is that on tap of normal text, the text field should be editable and we can able to edit the text.
Have an example
In this scenario, we can see how on tap of initial text the editable text field is getting displayed and we can able to edit the text.
So let’s start with the process:
First, we have to initialize the variables.
bool _isEditingText = false;
TextEditingController _editingController;
String initialText = "Initial Text";
_isEditingText is the boolean variable and we have to set it false because we have to make it true when the user is tap on text.
TextEditingController -whenever a user modifies a text field with an associated TextEditingController, the text field edits and the controller notifies the listener. Listeners can then read the text and selection properties that the user has typed and updated. Basically the text editing controller is used to get the updated value from the text field.
initialText -Initial value, set to the text.
When we use any type of controller then we have to initialize and dispose of the controller.
So first initialize the controller in init state.
#override
void initState() {
super.initState();
_editingController = TextEditingController(text: initialText);
}
#override
void dispose() {
_editingController.dispose();
super.dispose();
}
‘dispose()’ is called when the State object is removed, which is permanent.
This method is used to unsubscribe and cancel all animations, streams, etc.
The framework calls this method when this state object will never build again.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("'Editable Text"),
),
body: Center(
child: _editTitleTextField(),
),
);
}
In build widget, I am simply displaying a widget _editTitleTextField().
Widget _editTitleTextField() {
if (_isEditingText)
return Center(
child: TextField(
onSubmitted: (newValue){
setState(() {
initialText = newValue;
_isEditingText =false;
});
},
autofocus: true,
controller: _editingController,
),
);
return InkWell(
onTap: () {
setState(() {
_isEditingText = true;
});
},
child: Text(
initialText,
style: TextStyle(
color: Colors.black,
fontSize: 18.0,
),
);
}
so what exactly the _editTitleTextField() widget does is, if the value of _isEditingText is false then simply show the text and on tap of text set the value of _isEditingText to true.
When _isEditingText is true then _editTitleTextField return text field. Textfield has parameter onSubmitted, so in onSubmitted method new value is assigned to initialText which is the updated value getting from _editingController.
Tada! This way we make the text editable and update the value of the text!
This can be done by having a StatefulWidget that keeps track of the state, by using a boolean for example. Let's say this bool is named editing. When editing == false you display a Text widget with the text to display, wrapped within a handler for the tap, such as a GestureDetector. When the user taps, the state is changed to editing = true. In this state you'll display a TextField with this value. You can use a TextEditingController within in this StatefulWidget and update its value there. This would allow you to up have all text selected when the user taps and changes to the editing state
I believe this is the correct answer:
class InlineEditableText extends StatefulWidget {
const InlineEditableText({
Key? key,
required this.text,
required this.style,
}) : super(key: key);
final String text;
final TextStyle style;
#override
State<InlineEditableText> createState() => _InlineEditableTextState();
}
class _InlineEditableTextState extends State<InlineEditableText> {
var _isEditing = false;
final _focusNode = FocusNode();
late String _text = widget.text;
late final TextStyle _style = widget.style;
late TextEditingController _controller;
#override
void initState() {
_controller = TextEditingController(text: _text);
_focusNode.addListener(() {
if (!_focusNode.hasFocus) {
setState(() => _isEditing = false);
} else {
_controller.selection = TextSelection(
baseOffset: 0,
extentOffset: _controller.value.text.runes.length,
);
}
});
super.initState();
}
#override
void dispose() {
_controller.dispose();
_focusNode.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onDoubleTap: () => setState(() {
_isEditing = !_isEditing;
_focusNode.requestFocus();
}),
child: TextField(
maxLines: 1,
style: _style,
focusNode: _focusNode,
controller: _controller,
onSubmitted: (changed) {
setState(() {
_text = changed;
_isEditing = false;
});
},
showCursor: _isEditing,
cursorColor: Colors.black,
enableInteractiveSelection: _isEditing,
decoration: InputDecoration(
isDense: true,
contentPadding: const EdgeInsets.symmetric(
horizontal: 0,
vertical: 4.4,
),
border: _isEditing
? const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(0)),
)
: InputBorder.none,
),
),
);
}
}

Is there any workaround for using keepalive mixin alongside the GetX package?

I created a slider-based stepper form using TabBarView which validate the input before switching. It works, but when I go back, the state was reset. This behavior leads me to an empty form when I try to collect the data at the end of the tab.
I have googled for few hours and have been tried switching the current GetView<MyController> to the classic StatefulWidget with AutomaticKeepAliveMixin with no luck, so I revert it.
I'm a bit stuck, I wonder if there is any other way to achieve this, the GetX way, if possible.
visual explanation
`
create_account_form_slider.dart
class CreateAccountFormSlider extends GetView<CreateAccountController> {
#override
Widget build(BuildContext context) {
return Expanded(
child: TabBarView(
physics: const NeverScrollableScrollPhysics(),
controller: controller.tabController,
children: [
_buildEmailForm(),
_buildNameForm(),
_buildPasswordForm(),
],
),
);
}
Widget _buildEmailForm() {
return Form(
key: controller.emailFormKey,
child: Column(
children: [
Spacer(), // Necessary to push the input to the bottom constraint, Align class doesn't work.
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: FormInput(
focusNode: controller.emailFocusNode,
margin: EdgeInsets.zero,
label: 'create_account_form_email'.tr,
hintText: 'janedoe#example.com',
textInputAction: TextInputAction.next,
keyboardType: TextInputType.emailAddress,
validator: controller.emailValidator,
onFieldSubmitted: (_) => controller.next(),
),
),
],
),
);
}
... each form has similar structure (almost identical), so i will not include it here
create_account_controller.dart
class CreateAccountController extends GetxController
with SingleGetTickerProviderMixin {
final tabIndex = 0.obs;
final emailFormKey = GlobalKey<FormState>();
FormState get emailForm => emailFormKey.currentState;
final emailFocusNode = FocusNode();
final email = ''.obs;
TabController tabController;
#override
void onInit() {
_initTabController();
super.onInit();
}
#override
void onClose() {
_disposeFocusNodes();
_disposeTabController();
super.onClose();
}
/// Initialize tab controller and add a listener.
void _initTabController() {
tabController = TabController(vsync: this, length: 3);
tabController.addListener(_tabListener);
}
/// Listen on tab change and update `tabIndex`
void _tabListener() => tabIndex(tabController.index);
/// Dispose tab controller and remove its listener.
void _disposeTabController() {
tabController.removeListener(_tabListener);
tabController.dispose();
}
/// Dispose all the focus nodes.
void _disposeFocusNodes() {
emailFocusNode.dispose();
}
/// Animate to the next slide.
void _nextSlide() => tabController.animateTo(tabIndex() + 1);
/// Animate to the next slide or submit if current tab is the last tab.
void next() {
if (tabIndex().isEqual(0) && emailForm.validate()) {
_nextSlide();
return focusScope.requestFocus(nameFocusNode);
}
...
}
/// A function that checks the validity of the given value.
///
/// When the email is empty, show required error message and when the email
/// is invalid, show the invalid message.
String emailValidator(String val) {
if (val.isEmpty) return 'create_account_form_error_email_required'.tr;
if (!val.isEmail) return 'create_account_form_error_email_invalid'.tr;
return null;
}
/// Submit data to the server.
void _submit() {
print('TODO: implement submit');
print(email());
}
}
I made it by saving the form and adding an initialValue on my custom FormInput widget then put the observable variable onto each related FormInput. No need to use keepalive mixin.
create_account_controller.dart
/// Animate to the next slide or submit if current tab is the last tab.
void next() {
if (tabIndex().isEqual(0) && emailForm.validate()) {
// save the form so the value persisted into the .obs variable
emailForm.save();
// slide to next form
_nextSlide();
// TODO: wouldn't it be nice if we use autofocus since we only have one input each form?
return focusScope.requestFocus(nameFocusNode);
}
...
}
create_account_form_slider.dart
Obx( // wrap the input inside an Obx to rebuild with the new value
() => Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: FormInput(
focusNode: controller.emailFocusNode,
label: 'create_account_form_email'.tr,
hintText: 'janedoe#example.com',
textInputAction: TextInputAction.next,
keyboardType: TextInputType.emailAddress,
validator: controller.emailValidator,
onFieldSubmitted: (_) => controller.next(),
initialValue: controller.email(), // use initial value to keep current value when user go back from the next slide
onSaved: controller.email, // persist current value into the .obs variable
),
),
),
FYI: The FormInput is just a regular TextInput, only decoration is modified. This should work with the regular flutter TextInput.
if you want to use AutomaticKeepAliveMixin in GetX like StatefulWidget. You can add the parameter 'permanent: true' in Get.put like this
Get.put<HomeController>(
HomeController(),
permanent: true,
);
Full code on HomeBinding like this
import 'package:get/get.dart';
import '../controllers/home_controller.dart';
class HomeBinding extends Bindings {
#override
void dependencies() {
Get.put<HomeController>(
HomeController(),
permanent: true,
);
}
}

Fetch Text from TextWidget flutter

I am creating an weather app in which I am providing a location string to a TextField and fetch the text inside in it. I know I can do it if I use TextField widget every time, but I want to use code reusability and that's why I have created a different Widget called TextFieldWidget in which I am providing a hint variable, which returns the Text inside it. I don't know how to do return the text. This is my code.
import 'package:flutter/material.dart';
class TextFieldWidget extends StatefulWidget {
final String _hint;
TextFieldWidget(this._hint);
#override
_TextFieldWidgetState createState() => _TextFieldWidgetState();
}
class _TextFieldWidgetState extends State<TextFieldWidget> {
TextEditingController _textEditingController;
InputDecoration _inputdecoration;
#override
void initState() {
super.initState();
_textEditingController = TextEditingController();
_inputdecoration = InputDecoration(hintText: widget._hint,floatingLabelBehavior: FloatingLabelBehavior.always);
}
#override
Widget build(BuildContext context) {
return TextField(
autocorrect: true,
controller: _textEditingController,
keyboardType: TextInputType.text,
enableSuggestions: true,
decoration: _inputdecoration,
onSubmitted: (value){
// somehow return data
// I want to access this part..
},
);
}
}
1. You have to create one method inside calling class
void _setTextValue(String value) {
// Submitted text should appear here from value
}
TextFieldWidget(_setTextValue)
2. Use this inside TextFormField then
final Function _setValue;
TextFieldWidget(this._setValue);
3. Inside onSubmitted Call
widget._setValue(value);
Finally, you will get value inside calling class
Edited:
We can add callbacks in calling widgets itself with named parameters which is correct way of doing this
Example:
class _TextFieldWidgetState extends State<TextFieldWidget> {
TextEditingController _textEditingController;
InputDecoration _inputdecoration;
#override
void initState() {
super.initState();
_textEditingController = TextEditingController();
_inputdecoration = InputDecoration(hintText: 'UserName',floatingLabelBehavior: FloatingLabelBehavior.always);
}
#override
Widget build(BuildContext context) {
return TextField(
autocorrect: true,
controller: _textEditingController,
keyboardType: TextInputType.text,
enableSuggestions: true,
decoration: _inputdecoration,
onSubmitted: (value){
widget.getUserName(value);
// somehow return data
// I want to access this part..
},
);
}
}
Call above code like
TextFieldWidget(getUserName: (value) {
// Get the username here
print(value);
}),
Note: We can add as many as named parameter like this

Flutter How to always hide keyboard when click on TextField but keep focus(Keep show cursor)

I can't control keyboard show or hide, In my project I need to always hide keyboard but keep focused to display my custom keyboard(a widget).
This is I want
And this is my problem
To hide keyboard and keep the cursor visible, set readOnly to true and showCursor to true.
TextFormField(
showCursor: true,
readOnly: true),
See flutter/issues/#16863
FYI, TextInputType.none was introduced in #83974:
TextField(
keyboardType: TextInputType.none,
...
)
You can use custom focusNode
This prevents keyboard appearing only on first tap:
TextField(focusNode: FirstDisabledFocusNode(),)
class FirstDisabledFocusNode extends FocusNode {
#override
bool consumeKeyboardToken() {
return false;
}
}
This prevents always:
TextField(focusNode: AlwaysDisabledFocusNode())
class AlwaysDisabledFocusNode extends FocusNode {
#override
bool get hasFocus => false;
}
Insert NoKeyboardEditableText instead your TextField
class NoKeyboardEditableText extends EditableText {
NoKeyboardEditableText({
#required TextEditingController controller,
TextStyle style = const TextStyle(),
Color cursorColor = Colors.black,
bool autofocus = false,
Color selectionColor
}):super(
controller: controller,
focusNode: NoKeyboardEditableTextFocusNode(),
style: style,
cursorColor: cursorColor,
autofocus: autofocus,
selectionColor: selectionColor,
backgroundCursorColor: Colors.black
);
#override
EditableTextState createState() {
return NoKeyboardEditableTextState();
}
}
class NoKeyboardEditableTextState extends EditableTextState {
#override
Widget build(BuildContext context) {
Widget widget = super.build(context);
return Container(
decoration: UnderlineTabIndicator(borderSide: BorderSide(color: Colors.blueGrey)),
child: widget,
);
}
#override
void requestKeyboard() {
super.requestKeyboard();
//hide keyboard
SystemChannels.textInput.invokeMethod('TextInput.hide');
}
}
class NoKeyboardEditableTextFocusNode extends FocusNode {
#override
bool consumeKeyboardToken() {
// prevents keyboard from showing on first focus
return false;
}
}
try use input_with_keyboard_control package
It helped me solve my problem, of receiving the text from a barcode scanner, without showing the keyboard