Flutter how to hide the autofill options in a TextFormField on flutter-web/iOS - iphone

I have a simple Widget, containing a single TextFormField
class TestLayout extends StatelessWidget {
TestLayout({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(children: [
TextFormField(),
]);
}
}
But when I deploy this on a webserver and access the site from my iPhone, I get the following (see screenshot below):
Note the additional line with autofill related items above the keyboard (password, creditcard and address items and the corresponding 'done' button). How can I get rid of this line?

Have you tried to set the keyboardType property to visiblePassword along with enableSuggestions propertie setted to false?
Something like this should fix:
TextFormField(
enableSuggestions: false,
keyboardType: TextInputType.visiblePassword
),

Related

Flutter - Handle key event only if there is no text entry event

I'm trying to implement some shortcuts on a desktop app. I've been looking into these links:
Understanding Flutter's focus system
Focus and text fields
Using Actions and Shortcuts
I would like to do an action when the user presses on the key a (for example).
class MyWidget extends StatefulWidget {
const MyWidget({Key? key}) : super(key: key);
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
int count = 0;
KeyEventResult onKey(FocusNode node, RawKeyEvent event) {
if (event is RawKeyDownEvent && event.logicalKey == LogicalKeyboardKey.keyA) {
setState(() {
count++;
});
return KeyEventResult.handled;
}
return KeyEventResult.ignored;
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Focus(
onKey: onKey,
child: Column(
children: [
Text('count: $count'),
const TextField(),
TextButton(
child: Text('button'),
onPressed: () {},
),
],
),
),
),
);
}
}
When the user clicks on a, the count increments.
The issue with this code is the user being unable to type a in the text field anymore because it is being handled by the focus node.
The first link states:
Key events start at the focus node with primary focus. If that node doesn’t return KeyEventResult.handled from its onKey handler, then its parent focus node is given the event. If the parent doesn’t handle it, it goes to its parent, and so on, until it reaches the root of the focus tree.
and
Focus key events are processed before text entry events, so handling a key event when the focus widget surrounds a text field prevents that key from being entered into the text field.
I would like my Focus widget to handle the key event only if the subtree didn't handle it itself including TextFields (and therefore text entry events).
I tried to always return KeyEventResult.ignored in the onKey method, but the OS triggers a sound meaning there is no action available every time the user clicks on a.
Is there a way to implement what I am trying to do? If yes, how?
You could wrap your text field in a focus that returns KeyEventResult.skipRemainingHandlers in onKeyEvent. This will prevent it affecting the other handlers while the text field is focused. Although this is inelegant and I'd love a correction with a better solution.
const unhandledKeys = [
LogicalKeyboardKey.delete,
LogicalKeyboardKey.backspace,
LogicalKeyboardKey.arrowUp,
LogicalKeyboardKey.arrowDown,
LogicalKeyboardKey.arrowLeft,
LogicalKeyboardKey.arrowRight
];
final focusNode = useFocusNode(onKeyEvent: (_a, _b) {
if (unhandledKeys.contains(event.logicalKey)) {
return KeyEventResult.ignored;
}
return KeyEventResult.skipRemainingHandlers;
});
return Focus(
focusNode: focusNode,
child: TextField()
);

Pass parent state to generic child widget

I'm building a flutter app, and I have built a customized AppBar widget for it. This appbar has a SearchBar widget, which calls whatever callback is passed to it onChange. Now, there are multiple screens that use this SearchBar, and each of them will do something different with the user input. But I've noticed that on each of the screens that use the appbar, I'd have to use a state to control the SearchBar inputted text. So, I'm trying to not have to create the state for every screen, and have a Widget that wraps my screens, and controls the input in it's state, and passes it's state down to the child I provide to this apps. This would be similar to React's Higher Order Components, which wrap another component and can pass props to it.
This seems to me like a good design pattern, but I don't know how to implement it. Since the child widget that I would pass to this second order component that would wrap my screens, won't be getting any info from it, the child is simply passed as a widget (note: the code is also doing other stuff, working as a general wraper to replace similar repetitive code in all of my screens):
class CustomScaffold extends StatefulWidget {
final ScreenInfo appBarInfo;
final Builder child;
final Widget bottomBarApp;
final Widget appBar;
final EdgeInsetsGeometry padding;
const CustomScaffold({
#required this.child,
Key key,
this.bottomBarApp,
this.appBar,
this.padding,
this.appBarInfo,
}) : super(key: key);
#override
_CustomScaffoldState createState() => _CustomScaffoldState();
}
class _CustomScaffoldState extends State<CustomScaffold> {
String searchTerm = '';
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: [
widget.appBar ??
GradientAppBar(
title: widget.appBarInfo.label,
searchBar: SearchBar(
callback: (String input) {
setState(() {
searchTerm = input;
});
},
placeholder: widget.appBarInfo.searchPlaceholder,
),
),
Padding(
padding: widget.padding,
child: widget.child,
),
],
),
),
bottomNavigationBar: widget.bottomBarApp ?? CustomBottomBarNavigator(),
);
}
}
I'm thinking that I could passa builder instead of a widget as the child, but I'm not sure how this would work. Also, I'm still learning bloc in general, so I'm not sure if it would be a good idea to use bloc for this. I'm guessing bloc's purpose is a little different, and would complicate this specific pattern.
Does this idea make sense? What would be the best way to implement it?
Thanks in advance.

Flutter: How to hide TextField text pointer (cursor) when use initial value text (Android)

Use case: Messaging app you edit your message: keyboard, blinking cursor and initial text appears but pointer (cursor) is not
But on Flutter when you use initial text (or via textController) there are always pointer(cursor) which is not wanted
Example
Steps to reproduce:
run flutter create bug
edit main.dart to replace center text (line 100) to MyStatefulPage(),
class MyStatefulPage extends StatefulWidget {
#override
State<MyStatefulPage> createState() {
return _MyStatefulPageState();
}
}
class _MyStatefulPageState extends State<MyStatefulPage> {
TextEditingController controller;
#override
void initState() {
super.initState();
controller = new TextEditingController();
controller.text = 'My Initial Text';
}
#override
Widget build(BuildContext context) {
return TextField(
decoration: InputDecoration(
border: InputBorder.none
),
// showCursor: false,
controller: controller,
autofocus: true,
maxLines: 8,
);
}
}
With that code when you open app keyboard will appear but so will pointer(cursor) I want to hide this cursor(pointer).
Note: it's only for Android.
TextField set enableInteractiveSelection property to false can resolve this issue
TextFormField cursorHeight: 0 and cursorWidth: 0 can hide the cursor.
in textformfield use showCursor: false

Flutter Widget Focus issue

I've create a custom widget for time picking.
The widget contains an icon which opens a TimePicker, and a TextFormField so the user can type the time value manually.
This is how it looks like:
When the user types a value it immediately validated and also when the focus is off, it validate the value and update the field with correct time format.
For example, if the user types 8 and then clicks next widget, it will update to 8:00
Checkout the form image with 2 TimePickers:
What I want is that when user types StartTime, and then clicks the keyboard Next button, the focus will move to the EndTime picker. Then when the user clicks Next button on the EndTime Picker the focus will move to the next widget in the form
The problem is that the FocusNode is inside the TimePicker custom widget (which is StateFull) and I couldn't figure out how it can be exposed outside of it.
You can see the code for it here:
https://github.com/moti0375/tikal_time_tracker/blob/develop/lib/ui/time_picker.dart
Any idea will be appreciated.
Finally I've figured it out,
Instead of creating the FocusNode inside the picker widget (a child widget), I've created the FocusNode in the parent widget (the form) and provide it to the child widget in its constructor, by this the focus node created in the parent widget context.
Then, I've added a request focus method to the child widgets so the parent can call them and FocusScope.of(context).requestFocus(focusNode); is called inside the child widgets but on the focusNode that provided by the parent widget.
Here is a portion of the code:
Child widget:
class TimeTrackerTimePicker extends StatefulWidget {
final FocusNode focusNode;
TimeTrackerTimePicker({ this.focusNode});
//This can be called from the parent widget with the parent context
void requestFocus(BuildContext context){
print("${this.pickerName} requestFocus...");
FocusScope.of(context).requestFocus(focusNode);
}
....
....
#override
State<StatefulWidget> createState() {
return TimePickerState();
}
}
State class:
class TimePickerState extends State<TimeTrackerTimePicker> {
#override
Widget build(BuildContext context) {
return Container(
....
child: new Flexible(
child: new TextFormField(
textInputAction: TextInputAction.next,
focusNode: widget.focusNode, //linking to the focusNode
onFieldSubmitted: onSubmitButtonClicked,
decoration: InputDecoration(
hintText: widget.hint != null ? widget.hint : "",
contentPadding:
EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(20.0))),
maxLines: 1,
controller: pickerController))
)
}
}
Then in the parent widget when you need to set focus:
FocusNode focusNode;
TimeTrackerTimePicker timePicker;
#override
void initState() {
super.initState();
focusNode = new FocusNode();
timePicker = new TimeTrackerTimePicker(focusNode: focusNode);
}
.....
//request focus when required..
void requestPickerFocus(){
timePicker.requestFocus(context);
}
FocusScope.of(context).unfocus()

How to listen to keyboard on screen Flutter?

I am building a mobile app, I want to remove a widget when the keyboard appears on the screen, i.e when the input text field is on focus.
I have tried to use RawKeyboardListener but that doesn't seem to work, my code is as below:
new Container(
child: new RawKeyboardListener(
focusNode: new FocusNode(),
onKey: (input) => debugPrint("*****KEY PRESSED"),
child: new TextField(
controller: new TextEditingController(),
),
),
);
You can use this simple check:
MediaQuery.of(context).viewInsets.bottom == 0
The keyboard is closed when this returns true, otherwise it's open.
Be aware to take the context of the whole screen (Scaffold for example) and not only from one widget.
This is how you integrate that check to your code:
Visibility(
child: Icon(Icons.add),
visible: MediaQuery.of(context).viewInsets.bottom == 0,
)
The keyboard will automatically appear when the text field is focused. So you can add a listner to the focusnode to listen the focus change and hide respective widget.
Example:
void _listener(){
if(_myNode.hasFocus){
// keyboard appeared
}else{
// keyboard dismissed
}
}
FocusNode _myNode = new FocusNode()..addListener(_listner);
TextField _myTextField = new TextField(
focusNode: _mynNode,
...
...
);
new Container(
child: _myTextField
);
I used the package keyboard_visibility
Then I wrapped my TextField with a KeyboardListener implemented as follows:
class KeyboardListener extends StatefulWidget {
final Widget child;
final void Function(bool) onChange;
KeyboardListener({#required this.child, #required this.onChange});
#override
_KeyboardListenerState createState() => _KeyboardListenerState();
}
class _KeyboardListenerState extends State<KeyboardListener> {
int _sId;
KeyboardVisibilityNotification _kvn;
#override
void initState() {
super.initState();
_kvn = KeyboardVisibilityNotification();
_sId = _kvn.addNewListener(
onChange: widget.onChange,
);
}
#override
Widget build(BuildContext context) {
return widget.child;
}
#override
void dispose() {
_kvn.removeListener(_sId);
super.dispose();
}
}
You can use this library keyboard_visibility: ^0.5.6 at :
https://pub.dev/packages/keyboard_visibility
For execute your code, insert this in the initState()
KeyboardVisibilityNotification.addNewListener(
onChange: (bool visible) {
print(visible);
this.setState(() {
keyboardIsOpen = visible;
});
},
);
Whenever keyboard is open or closed, the library calls onChange method with the visibility boolean.
A widget that calls a callback whenever the user presses or releases a key on a keyboard.
A RawKeyboardListener is useful for listening to raw key events and hardware buttons that are represented as keys. Typically used by games and other apps that use keyboards for purposes other than text entry.
For text entry, consider using a EditableText, which integrates with on-screen keyboards and input method editors (IMEs).
const RawKeyboardListener({
Key key,
#required FocusNode focusNode,
#required ValueChanged<RawKeyEvent> onKey,
#required Widget child
})
Creates a widget that receives raw keyboard events.
For text entry, consider using a EditableText, which integrates with on-screen keyboards and input method editors (IMEs).
Implementation
const RawKeyboardListener({
Key key,
#required this.focusNode,
#required this.onKey,
#required this.child,
}) : assert(focusNode != null),
assert(child != null),
super(key: key);