Flutter - change TextFormField background when in active mode (Typing) - flutter

I want to achieve this.
While a text form field is inactive, its background, fill color will be grey. But when I am typing or it is in active mode, its background color will be white.
How to achieve this behavior?

try this:
class CustomTextFiled extends StatefulWidget {
const CustomTextFiled({
Key? key,
this.focusNode,
required this.fillColor,
required this.focusColor,
// add whaterver properties that your textfield needs. like controller and ..
}) : super(key: key);
final FocusNode? focusNode;
final Color focusColor;
final Color fillColor;
#override
_CustomTextFiledState createState() => _CustomTextFiledState();
}
class _CustomTextFiledState extends State<CustomTextFiled> {
late FocusNode focusNode;
#override
void initState() {
focusNode = widget.focusNode ?? FocusNode();
focusNode.addListener(() {
setState(() {});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return TextField(
focusNode: focusNode,
decoration: InputDecoration(
filled: true,
fillColor: focusNode.hasFocus ? widget.focusColor : widget.fillColor,
),
);
}
}

You can use FocusNode with listener.
late final FocusNode focusNode = FocusNode()
..addListener(() {
setState(() {});
});
....
TextField(
focusNode: focusNode,
decoration: InputDecoration(
fillColor: focusNode.hasFocus ? Colors.white : null,
filled: focusNode.hasFocus ? true : null,
),
)

After going through some tests, I have finalized the correct answer. The above answer is good. The first one has a problem. Focus Node variable must be inside the state class so that it can preserve its state.
class _GlobalTextFormFieldState extends State<GlobalTextFormField> {
late FocusNode focusNode;
#override
void initState() {
focusNode = FocusNode();
focusNode.addListener(() {
setState(() {});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return TextFormField(
focusNode: focusNode,
);
}
}

Related

How do i make a TextEditingController Required in a stateful widget

I created a widget for a textfield that accepts password and I made use of stateful widget. Now I want to get the value of what is written in the text field in two different files but I can't make the texteditingcontroller requiredenter image description here
Login Page should be something like this:
declare the controller in the login page then you can pass the controller to other Widget including Passwordfield, the Login page now is the owner of the controller it initialize it and dispose it.
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
late TextEditingController _passwordController;
#override
void initState() {
_passwordController = TextEditingController();
}
#override
void dispose() {
_passwordController.dispose();
}
Widget build(BuildContext context) {
return Column(
children: <Widget>[
// Emailfield(),
Passwordfield(
controller: _passwordController,
),
],
);
}
}
in the Passwordfield edit the constructor to use the controller in this Widget:
class Passwordfield extends StatefulWidget {
final TextEditingController controller;
Passwordfield({Key? key, required this.controller,}) : super(key: key);
#override
_PasswordfieldState createState() => _PasswordfieldState();
}
class _PasswordfieldState extends State<Passwordfield> {
ValueChanged<String> onChanged = (value) {};
String hintText = "password";
bool hidepassword = true;
Widget build(BuildContext context) {
return TextField(
controller: widget.controller,
onChanged: onChanged,
obscureText: hidepassword,
// ...
);
}
}
You can make it like this:
First you have to create controller :
var _controller = TextEditingController();
Second add this controller to your textfield
TextField(
autofocus: true,
controller: _controller, // add controller here
decoration: InputDecoration(
hintText: 'Test',
focusColor: Colors.white,
),
),
and finally in your button check if controller is empty or not
CustomButton(
onTap: () async {
if (_controller.text.trim().isEmpty) {
showCustomSnackBar(
'Password field is empty',
context);
}
}
)
just it

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,
),
),
);
}
}

Why my TextFormField value doesn't change here?

Well it's probably a very simple question but I'm starting and I'm not able to find too much documentation about flutter...
import 'package:flutter/material.dart';
class AutoChangeField extends StatefulWidget {
#override
_AutoChangeFieldState createState() => _AutoChangeFieldState();
}
class _AutoChangeFieldState extends State<AutoChangeField> {
Color _color = Colors.black;
String _newValue = 'write something';
#override
Widget build(BuildContext context) {
return TextFormField(
initialValue: _newValue,
cursorColor: _color,
onChanged: (val){
setState(() {
_newValue = "It changed!";
_color = Colors.red;
});
},
);
}
}
Just the cursorColor swaps to red properly when you write something at the TextFormField, but the value of it does not. Value keeps being "write something + (what u writed)" instead of "It changed!". :(
Thanks you so much.
You should use a TextEditingController and set the initialValue of the TextEditingController to "Write something"
Like this
class AutoChangeField extends StatefulWidget {
#override
_AutoChangeFieldState createState() => _AutoChangeFieldState();
}
class _AutoChangeFieldState extends State<AutoChangeField> {
Color _color = Colors.black;
TextEditingController controller = TextEditingController(text: "Write something");
#override
Widget build(BuildContext context) {
return TextFormField(
controller: controller,
cursorColor: _color,
onChanged: (val){
print(val);
controller.text = "It changed!";
setState(() {
_color = Colors.red;
});
},
);
}
}
NOTE: You cannot use the controller and initialValue at the same time.
You need to use 'controller' proeprty.
Define a controller like below :
TextEditingController myController = new TextEditingController();
Assign it to the TextFormField as :
TextFormField(
controller: myController,
Now, assign a new value as :
myController.text = "its changed";

Detect TextFormField stop typing in flutter

I have TextFormField, and want do same actions when user stop typing in textfield. Now I am using onchange function, but I want detect when user stop typing.
If you want to achieve debounce on textfield for searching, then here you go.
final _searchQueryController = new TextEditingController();
Timer _debounce;
String query = "";
int _debouncetime = 500;
#override
void initState() {
_searchQueryController.addListener(_onSearchChanged);
super.initState();
}
#override
void dispose() {
_searchQueryController.removeListener(_onSearchChanged);
_searchQueryController.dispose();
super.dispose();
}
_onSearchChanged() {
if (_debounce?.isActive ?? false) _debounce.cancel();
_debounce = Timer(Duration(milliseconds: _debouncetime), () {
if (_searchQueryController.text != "") {
///here you perform your search
performSearch(_searchQueryController.text);
}
});
}
//your textfield
TextField(controller: _searchQueryController,
autofocus: true,
decoration: InputDecoration(
hintText: " Search...",
border: InputBorder.none,
),
style: TextStyle(fontSize: 14.0),
)
You can do it with flutter_hooks as follows:
class DebounceTextField extends HookWidget {
///
const DebounceTextField({
Key? key,
required this.padding,
required this.onAnswer,
required this.child,
this.initialText,
this.debounceTime,
}) : super(key: key);
///
final EdgeInsets padding;
///
final String? initialText;
///
final OnAnswer onAnswer;
///
final TextFormField child;
///
final int? debounceTime;
#override
Widget build(BuildContext context) {
final TextEditingController textController =
useTextEditingController(text: initialText);
useEffect(
() {
Timer? timer;
void listener() {
timer?.cancel();
timer = Timer(
Duration(milliseconds: debounceTime ?? 500),
() => onAnswer(textController.text),
);
}
textController.addListener(listener);
return () {
timer?.cancel();
textController.removeListener(listener);
};
},
<TextEditingController>[textController],
);
// child.controller = textController;
return Padding(
padding: padding,
child: TextFormField(
controller: textController,
validator: _shortAnswerValidator,
decoration: const InputDecoration(
hintText: "Cevabı buraya yazınız...",
),
),
);
}
}
We got the inspiration for this one here.

Flutter onChanged and onSaved together for Text Inputs

I've been trying to implement a small form in Flutter and found that the onChanged and onSaved events are not available together on either of the 2 TextInput widgets.
onChanged is defined in TextField widget and onSaved is defined in TextFormField widget. One workaround is to use the TextEditingController to watch for changes but that adds a bunch of additional lines of code to add listeners, remove listeners and dispose. Is there a better solution to address this issue?
You can create your own widget to support that method, like this :
import 'package:flutter/material.dart';
class MyTextField extends StatefulWidget {
final Key key;
final String initialValue;
final FocusNode focusNode;
final InputDecoration decoration;
final TextInputType keyboardType;
final TextInputAction textInputAction;
final TextStyle style;
final TextAlign textAlign;
final bool autofocus;
final bool obscureText;
final bool autocorrect;
final bool autovalidate;
final bool maxLengthEnforced;
final int maxLines;
final int maxLength;
final VoidCallback onEditingComplete;
final ValueChanged<String> onFieldSubmitted;
final FormFieldSetter<String> onSaved;
final FormFieldValidator<String> validator;
final bool enabled;
final Brightness keyboardAppearance;
final EdgeInsets scrollPadding;
final ValueChanged<String> onChanged;
MyTextField(
{this.key,
this.initialValue,
this.focusNode,
this.decoration = const InputDecoration(),
this.keyboardType = TextInputType.text,
this.textInputAction = TextInputAction.done,
this.style,
this.textAlign = TextAlign.start,
this.autofocus = false,
this.obscureText = false,
this.autocorrect = true,
this.autovalidate = false,
this.maxLengthEnforced = true,
this.maxLines = 1,
this.maxLength,
this.onEditingComplete,
this.onFieldSubmitted,
this.onSaved,
this.validator,
this.enabled,
this.keyboardAppearance,
this.scrollPadding = const EdgeInsets.all(20.0),
this.onChanged});
#override
_MyTextFieldState createState() => _MyTextFieldState();
}
class _MyTextFieldState extends State<MyTextField> {
final TextEditingController _controller = new TextEditingController();
_onChangedValue() {
if (widget.onChanged != null) {
widget.onChanged(_controller.text);
}
}
#override
void initState() {
_controller.addListener(_onChangedValue);
super.initState();
}
#override
void dispose() {
_controller.removeListener(_onChangedValue);
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return TextFormField(
key: widget.key,
controller: _controller,
initialValue: widget.initialValue,
focusNode: widget.focusNode,
decoration: widget.decoration,
keyboardType: widget.keyboardType,
textInputAction: widget.textInputAction,
style: widget.style,
textAlign: widget.textAlign,
autofocus: widget.autofocus,
obscureText: widget.obscureText,
autocorrect: widget.autocorrect,
autovalidate: widget.autovalidate,
maxLengthEnforced: widget.maxLengthEnforced,
maxLines: widget.maxLines,
onEditingComplete: widget.onEditingComplete,
onFieldSubmitted: widget.onFieldSubmitted,
onSaved: widget.onSaved,
validator: widget.validator,
enabled: widget.enabled,
keyboardAppearance: widget.keyboardAppearance,
scrollPadding: widget.scrollPadding,
);
}
}
And include it in your page:
Padding(
padding: EdgeInsets.all(20.0),
child: Center(child: MyTextField(
onChanged: (value) {
print("testing onchanged $value");
},
)),
)