Only static members can be accessed in initializers on TextEditingController - flutter

GOAL: when editing information it goes to a Form that has an initial value equal to the information at present.
The code below works because it has the property 'initialValue'
TextFormField(
maxLines: 1,
autofocus: true,
initialValue: _currentProditem.producttitle,
style: TextStyle(
color: Colors.black87,
fontWeight: FontWeight.w400,
fontSize: 18.0,
fontFamily:'Cabin',
),
but the code below does not work because the properties 'initialValue' and 'controller' are conflicting. That's why I commented the property 'initialValue' in the code below. But I want to put the initialValue somewhere so I can achieve the above goal.
TextFormField(
controller: _controller,
maxLines: 1,
autofocus: true,
// initialValue: _currentProditem.price,
keyboardType: TextInputType.number,
onChanged: (string) {
string = '${_formatNumber(string.replaceAll(',', ''))}';
_controller.text = string;
Timer(Duration(milliseconds: 1), () {
_controller.selection = TextSelection.fromPosition(TextPosition(offset: string.length));
});
},
so I researched how to put IntialValue and found that you need to add the code below
final _controller = TextEditingController(text: 'some text');
I tried changing the the word 'some text' to '_currentProditem.price' but there was a problem with the word '_currentProditem' it says 'error: Only static members can be accessed in initializers.'
For reference the word '_currentProditem' can be found on the code below
class ItemNotifier with ChangeNotifier {
List<Proditem> _itemList = [];
Proditem _currentProditem;
UnmodifiableListView<Proditem> get itemList => UnmodifiableListView(_itemList);
Proditem get currentProditem => _currentProditem;
set itemList(List<Proditem> itemList) {
_itemList = itemList;
notifyListeners();
}
set currentProditem(Proditem proditem) {
_currentProditem = proditem;
notifyListeners();
}
}

only static fields can be accessed outside any method
so, just initialise the _controller empty
final _controller = TextEditingController();
while building or in initState set the value
_controller.text = currentPrice;
as the controller has a setter named text, its easy to update the value dynamically like when a button is pressed

Related

How to use a controller in a RadioListTile?

I made a code that displays a String Array using RadioListTile, but I don't know how to exactly get the user input when he/she selects one option from the list. I'm trying to use controller, but I'm not pretty sure how to use it in this ocation. Any advice? or comments? Here is my code:
///Here is the variable I use to get the user's answer.
TextEditingController userAns = TextEditingController();
///Here I'm using RadioListTile to display all the options and let the user to select one of them.
String selectedOption = '';
List<Widget> checkWhatOptionsDisplay(var question){
///Get question options and check what type of options display.
List<Widget> displayOptions = [];
var options = question.options;
if(options==""){
displayOptions.add(
TextField(
controller: userAns,
keyboardType: TextInputType.text,
decoration: InputDecoration(
hintText: 'Answer Fill in Blank',
labelText: 'Answer Fill in Blank',
),
)
);
}else{
for(String option in options){
displayOptions.add(
RadioListTile(
value: option,
groupValue: selectedOption,
title: Text(option),
onChanged:(currentOption){
setSelectedOption(currentOption);
} ,
selected: selectedOption == option,
)
);
}
}
return displayOptions;
}
setSelectedOption(var option) {
setState(() {
selectedOption = option;
});
}

How to make provider listener listen only once

I am geting input(text) using textfield and displaying in a list, I am using onchanged property of textfield and provider to update the text of new element in list but, all elements in the list update to onChanged's new value, once the element is added to list I want it to stop listening to changes of onChanged. So, that I can display list with different elements. How do I achieve that.
TextField(
autofocus: true,
decoration: kTextFieldDecocation.copyWith(
hintText: 'B Name'),
onChanged: (newbName) {
Provider.of<BNameControllerClass>(context,
listen: false)
.newBorrowerName(newbName);
},
),
List element's text
Text(
Provider.of<BNameControllerClass>(context,
listen: true)
.bName,
style: const TextStyle(fontSize: 20),
);
provider class
class BNameControllerClass extends ChangeNotifier {
String bName = 'Ganesh';
newBorrowerName(String newName) {
bName = newName;
notifyListeners();
}
}
Create List<String> textsFromInput = [];
Generate text widgets with ListView.generate().
Then in TextField you can use onSubmitted: (value) => textsFromInput.add(value)
Depending on what state management you have you can then call setState() or handle list rebuilding with bloc builder buildWhen: previous.textsFromInput.length != current.textsFromInput.length or with provider.

Error when supplying an initial value to TextField

I am facing a problem with TextField, since I am working hand in hand with TextEditingController()..text and onChanged, but when entering a new data, it is not reflected in the TextField. I made a print inside the onChanged this same one recognizes a new entry but the value to initiate continues without being updated. Inside the onChanged, I have a function which is in charge of validating what is entered and returning an error if necessary. When I comment the instruction before mentioned the TextField already allows to enter and to update what the user enters.
I hope you can help me, a feedback, tutorial, etc.
I would appreciate it.
TextField Code:
CustomTextField(
controller: TextEditingController()..text = datumAdministrative.name,
placeholder: Constants.selectDate,
helperText: Constants.requiredData,
keyboardType: TextInputType.text,
enable: true,
errorText: validationForm.name.error,
textInputAction: TextInputAction.next,
textCapitalization: TextCapitalization.sentences,
onChanged: (String value) {
validationForm.changeName(value);
},
);
ValidationForm Code:
void changeName(String value) {
String pattern = r'(^[a-zA-Z ]*$)';
RegExp regExp = new RegExp(pattern);
regExp.hasMatch(value)
? _name = ValidationItem(value, null)
: _name = ValidationItem(null, Constants.nameAdministrativeMessage);
notifyListeners();
}
Try Using : TextController(text: "<Required Text>")
Also does the validation have to be every single time the user enter any word ?
If not, you can try validation everything at the end.
If you are trying to use reactive validation, make sure your CustomTextField is wrapped with the widget which is responsible for rebuilding the UI.. something like Consumer() when using provider package
The problem is probably caused because the TextEditingController object is getting discarded by the rebuilds made by Flutter because you are instantiating the TextEditingController inside a build method. You should save the instance of your controller elsewhere, like in a state object as shown by the official docs or in your case, you can create it and get it from your validationForm.
This is the example in the docs:
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
/// save the [TextEditingController] instance
final TextEditingController _controller = TextEditingController();
...
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(6),
child: TextFormField(
controller: _controller, // User your instance
decoration: const InputDecoration(border: OutlineInputBorder()),
),
),
);
}
}
Possible solution in your case:
CustomTextField(
controller: validationForm.myTextController,
placeholder: Constants.selectDate,
...

How to change color of particular text in a text field dynamically?

Consider bellow image, I want to dynamically change the text color of part of the text based on the user input text (not the whole text) in a text field. How can i do that in flutter?
For this example we actually don't need a full blown rich-text editor.
I had a similar goal in my app to highlight tags (#flutter) or date references (next week, on Friday, etc) and I was able to implement this by extending built-in EditableText widget and posted my example as a Gist here: https://gist.github.com/pulyaevskiy/d7af7217c2e71f31dfb78699f91dfbb5
Below is full implementation of this widget which I called AnnotatedEditableText.
There is new property annotations which describes ranges of text that need to be highlighted and their style.
import 'package:flutter/widgets.dart';
class Annotation extends Comparable<Annotation> {
Annotation({#required this.range, this.style});
final TextRange range;
final TextStyle style;
#override
int compareTo(Annotation other) {
return range.start.compareTo(other.range.start);
}
#override
String toString() {
return 'Annotation(range:$range, style:$style)';
}
}
class AnnotatedEditableText extends EditableText {
AnnotatedEditableText({
Key key,
FocusNode focusNode,
TextEditingController controller,
TextStyle style,
ValueChanged<String> onChanged,
ValueChanged<String> onSubmitted,
Color cursorColor,
Color selectionColor,
TextSelectionControls selectionControls,
this.annotations,
}) : super(
key: key,
focusNode: focusNode,
controller: controller,
cursorColor: cursorColor,
style: style,
keyboardType: TextInputType.text,
autocorrect: true,
autofocus: true,
selectionColor: selectionColor,
selectionControls: selectionControls,
onChanged: onChanged,
onSubmitted: onSubmitted,
);
final List<Annotation> annotations;
#override
AnnotatedEditableTextState createState() => new AnnotatedEditableTextState();
}
class AnnotatedEditableTextState extends EditableTextState {
#override
AnnotatedEditableText get widget => super.widget;
List<Annotation> getRanges() {
var source = widget.annotations;
source.sort();
var result = new List<Annotation>();
Annotation prev;
for (var item in source) {
if (prev == null) {
// First item, check if we need one before it.
if (item.range.start > 0) {
result.add(new Annotation(
range: TextRange(start: 0, end: item.range.start),
));
}
result.add(item);
prev = item;
continue;
} else {
// Consequent item, check if there is a gap between.
if (prev.range.end > item.range.start) {
// Invalid ranges
throw new StateError(
'Invalid (intersecting) ranges for annotated field');
} else if (prev.range.end < item.range.start) {
result.add(Annotation(
range: TextRange(start: prev.range.end, end: item.range.start),
));
}
// Also add current annotation
result.add(item);
prev = item;
}
}
// Also check for trailing range
final String text = textEditingValue.text;
if (result.last.range.end < text.length) {
result.add(Annotation(
range: TextRange(start: result.last.range.end, end: text.length),
));
}
return result;
}
#override
TextSpan buildTextSpan() {
final String text = textEditingValue.text;
if (widget.annotations != null) {
var items = getRanges();
var children = <TextSpan>[];
for (var item in items) {
children.add(
TextSpan(style: item.style, text: item.range.textInside(text)),
);
}
return new TextSpan(style: widget.style, children: children);
}
return new TextSpan(style: widget.style, text: text);
}
}
Rich text controller works fine!
See more on https://pub.dev/packages/rich_text_controller
First you choose your RegExp
RichTextController _controller;
Map<RegExp, TextStyle> patternUser = {
RegExp(r"\B#[a-zA-Z0-9]+\b"):
TextStyle(color: Colors.amber, fontWeight: FontWeight.bold)
};
on initState()
_controller = RichTextController(
patternMap: patternUser,
);
Add controller on your TextFormField
TextFormField(
controller: _controller,
style: TextStyle(color: Colors.white),
)

Flutter- TextEditingController listener get called multiple time for textfield

TextField controlled controller.addListener(() gets called multiple time after pressing the clear button, this will only happen if we are clearing it.
Snippet:
TextEditingController controller = new TextEditingController();
TextField field = new TextField(
controller: controller,
autofocus: true,
);
controller.addListener(() {
print("Pressed cancel button");
});
Video Link
Note: While adding characters in TextField listener method gets called only ones.
I guess that would be a defect on flutter, a possible solution would be to use onChanged()
TextField field = new TextField(
autofocus: true,
onChanged: (String value) {
print("Pressed clear button");
},
);
I have the same problem with Nexus 6p when used with API level 23 and Pixel with API 25.
but this problem did not occurs with Pixel with API28 and it does not occurs with Nexus6P with API26.
exact code from https://flutter.dev/docs/cookbook/forms/text-field-changes was used.
1. We need to create our own .clear() method
void clearField() {
print("c: clearField");
var newValue = textController.value.copyWith(
text: '',
selection: TextSelection.collapsed(offset: 0),
);
textController.value = newValue;
callApi('');
}
// and call it by :
child: TextField(
controller: textController,
autofocus: true,
decoration: InputDecoration(
suffixIcon: IconButton(
icon: Icon(Icons.close),
onPressed: clearField, // call
),
),
),
2. We need to carefully handle changes
void changesOnField() {
print("c: changesOnField");
String text = textController.text;
if (text.isNotEmpty) { // set this
callApi(text);
}
}
Full Code
You may look into this repo and build it locally Github
Result