Skip to content

WKeyboardActions

View raw .md

A wrapper that renders a Done button and field-navigation toolbar above the keyboard for any group of input fields, with optional platform targeting and custom close widget.

On this page
Preview widgets/w_keyboard_actions_basic The live demo is not available for this example. w_keyboard_actions_basic.dart View source
dart
WKeyboardActions(
  focusNodes: [_nameFocus, _amountFocus],
  toolbarClassName: 'bg-gray-100 dark:bg-gray-800',
  child: Column(
    children: [
      WInput(focusNode: _nameFocus, placeholder: 'Jane Doe'),
      WInput(
        focusNode: _amountFocus,
        placeholder: '0.00',
        type: InputType.number,
      ),
    ],
  ),
)

Basic Usage#

Create a FocusNode for each input field and pass them all in the focusNodes list. WKeyboardActions attaches listeners to every node and shows the toolbar as long as any one of them is focused.

dart
class _MyFormState extends State<MyForm> {
  final _nameFocus = FocusNode();
  final _amountFocus = FocusNode();

  @override
  void dispose() {
    _nameFocus.dispose();
    _amountFocus.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return WKeyboardActions(
      focusNodes: [_nameFocus, _amountFocus],
      child: Column(
        children: [
          WInput(focusNode: _nameFocus, placeholder: 'Full Name'),
          WInput(
            focusNode: _amountFocus,
            placeholder: '0.00',
            type: InputType.number,
          ),
        ],
      ),
    );
  }
}

The toolbar is hosted in the ROOT Overlay, not the nearest one. It positions itself in screen terms (bottom: viewInsets.bottom, so it sits on top of the keyboard), and a nested Overlay is measured in its own box rather than the screen's. An app whose page host owns an Overlay inside a scroll view gives that overlay the height of the scrolled content, so a nearest-overlay toolbar would render hundreds of points below the viewport: present in the widget tree, absent from the screen. You need no Overlay of your own for this to work; MaterialApp and CupertinoApp both provide the root one.

Constructor#

dart
const WKeyboardActions({
  Key? key,
  required Widget child,
  required List<FocusNode> focusNodes,
  String platform = 'all',
  bool nextFocus = true,
  String? toolbarClassName,
  Widget Function(FocusNode)? closeWidgetBuilder,
})

Props#

Prop Type Default Description
child Widget Required The form or column of inputs the toolbar is attached to.
focusNodes List<FocusNode> Required FocusNodes for the inputs that need keyboard actions. Order determines navigation order.
platform String 'all' Platform gate: 'all', 'ios', or 'android'.
nextFocus bool true When true, Previous/Next arrow buttons appear in the toolbar for field navigation.
toolbarClassName String? null Wind utility classes applied to the toolbar background (typically bg-* classes).
closeWidgetBuilder Widget Function(FocusNode)? null Builder that replaces the default "Done" button. Receives the currently-focused FocusNode.

Platform Targeting#

The platform prop gates the toolbar to a specific OS. The most common value is 'ios', because iOS numeric keyboards have no built-in Done button.

Value Toolbar visible on
'all' iOS and Android (default)
'ios' iOS only
'android' Android only
dart
WKeyboardActions(
  platform: 'ios',
  focusNodes: [_amountFocus],
  child: WInput(
    focusNode: _amountFocus,
    placeholder: '0.00',
    type: InputType.number,
  ),
)

When nextFocus: true (the default), the toolbar shows Previous and Next arrow buttons. They move focus to the previous or next node in the focusNodes list, in the order they were provided. The Previous button is disabled at the first field; the Next button is disabled at the last.

Set nextFocus: false to show only the Done button, which is useful for single-field forms.

dart
WKeyboardActions(
  focusNodes: [_nameFocus, _emailFocus, _amountFocus],
  nextFocus: true,
  child: Column(
    children: [
      WInput(focusNode: _nameFocus, placeholder: 'Jane Doe'),
      WInput(focusNode: _emailFocus, placeholder: '[email protected]'),
      WInput(focusNode: _amountFocus, placeholder: '0.00'),
    ],
  ),
)

Toolbar Styling#

Pass Wind bg-* utility classes via toolbarClassName to set the toolbar background. The dark: pair is required.

dart
WKeyboardActions(
  toolbarClassName: 'bg-gray-100 dark:bg-gray-800',
  focusNodes: [_focusNode],
  child: myInputField,
)

When toolbarClassName is null, the toolbar uses Theme.of(context).colorScheme.surfaceContainerHighest.

Custom Close Button#

Supply closeWidgetBuilder to replace the default "Done" label with any widget. The builder receives the currently-focused FocusNode, which you can call unfocus() on.

dart
WKeyboardActions(
  focusNodes: [_focusNode],
  closeWidgetBuilder: (node) => WButton(
    onTap: node.unfocus,
    className: 'bg-blue-600 text-white px-4 py-1.5 rounded-md text-sm font-medium',
    child: const WText('Save & Close', className: 'text-white text-sm font-medium'),
  ),
  child: myInputField,
)

Styling Examples#

Minimal (single field, iOS only)#

dart
WKeyboardActions(
  platform: 'ios',
  nextFocus: false,
  focusNodes: [_amountFocus],
  toolbarClassName: 'bg-white dark:bg-gray-900',
  child: WInput(
    focusNode: _amountFocus,
    placeholder: '0.00',
    type: InputType.number,
  ),
)

Multi-field form with custom toolbar color#

dart
WKeyboardActions(
  focusNodes: [_nameFocus, _emailFocus, _amountFocus],
  toolbarClassName: 'bg-blue-50 dark:bg-blue-950',
  child: Column(
    children: [
      WInput(focusNode: _nameFocus, placeholder: 'Jane Doe'),
      WInput(focusNode: _emailFocus, placeholder: '[email protected]'),
      WInput(focusNode: _amountFocus, placeholder: '0.00'),
    ],
  ),
)

Custom close widget#

dart
WKeyboardActions(
  focusNodes: [_focusNode],
  toolbarClassName: 'bg-emerald-50 dark:bg-emerald-900',
  closeWidgetBuilder: (node) => TextButton.icon(
    onPressed: node.unfocus,
    icon: const Icon(Icons.check_circle_outlined),
    label: const Text('Done'),
  ),
  child: myInputField,
)
esc

Search index did not load.

No match for "".

Try a class like rounded-lg or a widget like WButton.