How can I let my DropdownButton affect my TextFormField - flutter

I think this is probably not too complicated, but I am new to coding and don't understand this, nor could I find an explanation online that I could understand.
In my app my AppBar includes both a title and a bottom, the title has been just text, and the bottom was a TextFormField users used to search through my database.
Recently I updated the title to a DropdownButton, and I wanted to use what the user selected there to change the the search parameters in my TextFormField (so that it would search different columns in my database).
I built the DropdownButton in a separate document, and then inserted it into title. Finding how to use it has been more tricky.
I built some sort of callback, which worked to print in my console, but I couldn't figure out how to pass the information into my TextFormField.
class HomePage extends StatelessWidget {
HomePage({Key key, this.title}) : super(key: key);
_handleValueReturned(value) {
if (value == 1) {print('This works');} if (value == 2) {print('This works not');} else {return null;}
}
...
appBar: AppBar(
title: DropDownButton(
valueReturned: _handleValueReturned,
),
bottom:...
...
Do I have to use some sort of provider or "of" or packages like Eventifier or ChangeNotifier? I couldn't figure out how all these things worked.

According to my understanding, you are trying to set the selected value from the DropDownButton in the TextFormField, if that is what you are trying to do then create a TextEditingController and add it to the TextFormField then set its text property inside your callback which is called _handleValueReturned, but you have to rebuild the widget so the TextFormField is rebuilt with the new value, of course, you have to convert your widget into StatefulWidget to do that.
Creating and initializing the TextEditingController:
final _controller = TextEditingController();
Attatching the controller to the TextFromField:
TextFormField(
controller: _controller,
...
);
Changing the value of the controller inside _handleValueReturned callback:
_handleValueReturned(value) {
...
setState((){
_controller.text = value; // It can be any String according to your scenario.
});
}
Side Note: When you are dealing with a simple text input such as one input field then you do not have to use TextFormField and you can simply replace it with TextField.
I hope I got your problem correctly and I hope it got fixed.

Related

How do I disable KeyboardListener when the focus is in TextField?

I have a requirement of using arrow keys to move between PageView pages, which I achieved with KeyboardListener. However some pages may include a TextInput and if that TextInput is focused, I want to prevent the KeyboardListener to handle the arrow keys.
The only solution I can think of is to set some flag when TextInput gains focus and condition the arrow logic, but this does not look elegant at all (since the KeyboardListener is way up the Widget tree and I would need to wire that logic to every TextInput).
Is there a better way?
I ended up doing this:
Extended FocusNode to have class indicating that for given Widget global keyboard events should be ignored:
class IgnoreShortcutKeysFocusNode extends FocusNode {}
In Widgets that contain a TextInput, I first create the extended IgnoreShortcutKeysFocusNode and assign it to the TextInput:
final _focusNode = IgnoreShortcutKeysFocusNode();
#override
Widget build(BuildContext context) {
return TextField(
focusNode: _focusNode,
// ...
);
}
In KeyboardListener handler, I check what is the currently focused node and act accordingly:
void _handleKeyEvent(KeyEvent value) {
if (FocusManager.instance.primaryFocus is IgnoreShortcutKeysFocusNode) return;
// arrow key handling
}
This is not ideal since the IgnoreShortcutKeysFocusNode must be added to each TextInput, but it works.

In Flutter can I make the children of a DropdownButton a widget other than DropdownMenuItem

I have a Flutter web app, which includes a web view on some pages.
I'm using the PointerInterceptor to prevent my web view from absorbing click events.
This works well,
but I have a situation now where I've got a DropdownButton and clicking it creates a bunch of DropdownMenuItems - I want to wrap those items in the same PointerInterceptor, like this :
DropdownButton<dynamic>(
items: myItems.map((e) => PointerInterceptor( child: DropdownMenuItem(
value: e,
child: Text(e.name),
))).toList(),
The problem is that this results in the following error :
The argument type 'List' can't be assigned to the
parameter type 'List<DropdownMenuItem>?'.
But I have put my DropdownButton in the app bar, and the DropdownMenuItems are injected into the Widget tree directly under the MaterialApp widget, so there isn't a higher level widget I can wrap.
How can I use the PointerInterceptor widget when the DropdownButton expects items to be DropdownMenuItems?
I was able to achieve this by making another class that wrapped the DropdownMenuItems , and then use that as the items in the DropdownButton in its place.
(ie. replace DropdownMenuItems with PointerInterceptedDropdownMenuItem)
Here is the definition of my wrapper class :
class PointerInterceptedDropdownMenuItem<T> extends DropdownMenuItem {
final VoidCallback? onTap;
final T? value;
final bool enabled;
const PointerInterceptedDropdownMenuItem({
Key? key,
this.onTap,
this.value,
this.enabled = true,
AlignmentGeometry alignment = AlignmentDirectional.centerStart,
required Widget child,
}) : super(key: key, alignment:alignment, child: child);
#override Widget build(BuildContext context) {
return PointerInterceptor( child: super.build(context) );
}
}
NB: This is a good, tidy solution for the part of the question asking about having the children of a DropdownButton be other than a DropdownMenuItem,
but it is not a good solution for the specific part of wrapping the items in the PointerInterceptor class, and that is because having that many PointerInceptors (I have a long list) has a bad impact on performance, therefore the solution for that part will be to move the PointerInterceptor to the top level of the scaffold and then making it conditional, and updating some state (reflecting the dropdown list being open)to say if the PointerInterceptor should cover the whole screen or not.

How to clear the TextFormField externally in an Autocomplete widget?

As you know, it is a basic requirement that; after the user makes an entry in an Autocomplete field (either by entering a new value or selecting an existing value from the dropdown list) and then press a 'Submit' button or 'Delete' button (say, to update the database); the old entry in the TextFormField should be cleared automatically for the next entry.
How can this be programmatically done in a simple way (for example, like Autocomplete.TextFormField.clear ) in Flutter?
I have tried several ways, but am unable to access/ modify the TextEditingController from an outside function.
Thank you in advance for any advice, please!
I am posting this for the benefit of whoever who reads this post, having a similar requirement.
A solution lies with flutter_typeahead widget, found here:
https://pub.dev/packages/flutter_typeahead
It could be used instead of RawAutocomplete or Autocomplete widgets.
You can use a RawAutocomplete to access the text editing controller from outside the Autocomplete. The RawAutocomplete is similar to Autocomplete, but it also provides the textEditingController and focusNode properties.
Note that if you pass in a textEditingController, you must also provide a non-null focusNode and fieldViewBuilder.
An example of modifying the textEditingController from outside the Autocomplete can be seen below.
final _controller = TextEditingController();
final _focusNode = FocusNode();
Widget build(BuildContext context) {
return RawAutocomplete<String>(
textEditingController: _controller,
focusNode: _focusNode,
fieldViewBuilder: (context, textEditingController, focusNode, onFieldSubmitted) =>
TextFormField(controller: _controller),
...
)};
//Then in your outside function
setState(() {
_controller.clear();
};

Flutter: How to add bullets points in a TextField?

How to add bullets points in a TextField widget similar to the Notes app in IOS?
You can use Unicode code to achieve this. Add the following Unicode ( \u25EF ) to your TextField using TextEditingController. You may also need to change the position of the cursor when adding text to your TextEditingController. This depends on your requirement and you will understand when you test this.
TextEditingController _textController = new TextEditingController();
_textController.text = " \u25EF ";
Yes. of course you need a custom button or something to trigger this event like in the note of IOS.
You can create a custom keyboard with an extra button to add bullets or a floating deck with a button like the Note app. Or simply give a button on the screen.
You need to create a checkbox (or similar button) manually when user select it and add to left of textfield. You can define variable on a Row and initialize to Container. Something like that:
Widget widget = Container();
[...]
Row(
children: [
widget,
Expanded(child: TextField(...))
],
);
[...]
//this activate bullet point
void functionCheckBox(){
setState({
widget = Checkbox(value: value, onChanged: onChanged);
});
}
I dont know if it's the best solution or if exists a plugin that does something like that.

Overwrite Paste Event for TextFormField

I have a TextFormField. Usually you can use the selection toolbar to copy/paste/select all and so on using long tap/double tap.
I want to overwrite the Paste Event. It shouldn't simple insert the current clipboard data but open a popup with several options to insert.
Is it possible to catch and overwrite the Paste event in any way? I saw something like handlePaste() for SelectionControls, but I don't know how to add this to my TextFormField.
Thanks in advance!
AFAIK, you can't exactly 'intercept' the standard toolbar. However, what you can do is to prevent the standard toolbar and make your own.
You can use wrap the textfield/textformfield under IgnorePointer. It will hide any tap gestures on the text field. Below is the code snippet.
IgnorePointer(
child: TextField(
focusNode: _textfieldFocusNode,
controller: _controller,
),
)
Now,you can wrap this IgnorePointer under GestureDetector and show your own menu. Like this :
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
FocusScope.of(context).requestFocus(_textfieldFocusNode);
},
onLongPress: () {
showMenu(____
}
)
This produces the result below and the sample implementation code is here
Found a way to override paste event. I'm not sure, that it is a right way, but it works.
In every TextField you have selectionControls, that provides a way to show and handle toolbar controls.
So, to catch paste event first:
create your own version of selection controls, for example
class AppCupertinoTextSelectionControls extends CupertinoTextSelectionControls {
AppCupertinoTextSelectionControls({
required this.onPaste,
});
ValueChanged<TextSelectionDelegate> onPaste;
#override
Future<void> handlePaste(final TextSelectionDelegate delegate) {
onPaste(delegate);
return super.handlePaste(delegate);
}
}
class AppMaterialTextSelectionControls extends MaterialTextSelectionControls {
AppMaterialTextSelectionControls({
required this.onPaste,
});
ValueChanged<TextSelectionDelegate> onPaste;
#override
Future<void> handlePaste(final TextSelectionDelegate delegate) {
onPaste(delegate);
return super.handlePaste(delegate);
}
}
then, initialise it in your state (for example in StatefulWidget it can looks like that, see below). To study how it used in TextField please see source here
TextSelectionControls? _selectionControls;
#override
void initState() {
if (widget.onPaste != null) {
if (Platform.isIOS) {
_selectionControls = AppCupertinoTextSelectionControls(
onPaste: widget.onPaste!,
);
} else {
_selectionControls = AppMaterialTextSelectionControls(
onPaste: widget.onPaste!,
);
}
}
super.initState();
}
Use callback for onPaste with a type ValueChanged<TextSelectionDelegate> and you can use the same code the Flutter team used to get Clipboard data:
Future<void> onPastePhone(final TextSelectionDelegate? delegate) async {
final TextSelection selection = phoneController.selection;
if (!selection.isValid) {
return;
}
// Snapshot the input before using `await`.
// See https://github.com/flutter/flutter/issues/11427
final ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain);
final text = data?.text ?? '';
if (text.isEmpty) {
return;
}
}
Then use selection controls in your TextField.
TextFormField(
selectionControls: _selectionControls,
)
Hope it helps.
I search for this problem. I think there is no proper way to solve this problem. I read about the Textfield class and found two solutions for it.
if you check TextField widget you can find that it will use EditableText to show its simple Text input. EditableText has a selectionControls property. this property is used to render the selection toolbar. also, I found that material and Cupertino have different implementation of it.
1st Solution: you can create your own custom TextField that will use EditableText and pass your custom selectionControl to your widget. I think this gonna be a very hard job to do. create your own implementation of the widget, handling animations, and...
2nd Solution: You can simply copy all related files of TextField in a new file and update it as you want. for this solution, I create a repo in GitHub. you can checkout source code to understand how you can show a dialog in the paste option. and this is how the code should work.
note: I just simply update paste function of the Material implementation of selectionControls. if you want you can also update the Cupertino selectionControls too.
note: also I added documents in everywhere I change the code.