Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
* `flet build`: extensions can declare build settings, such as Android compile options and Gradle dependencies, in a `flet-extension.toml` file, applied to every app that installs them under the app's own settings. See [Build settings](/docs/extend/user-extensions#build-settings) ([#6999](https://github.com/flet-dev/flet/pull/6999)) by @ndonkoHenri.
* `flet build`: add `[tool.flet.ios.entitlement]` and `--ios-entitlements` to sign iOS apps with entitlements, such as push notifications, universal links, App Groups or critical alerts. See [Entitlements](/docs/publish/ios#entitlements) ([#6999](https://github.com/flet-dev/flet/pull/6999)) by @ndonkoHenri.
* `flet build`: add `[tool.flet.android]` settings for the app's [Java compile options](/docs/publish/android#java-compile-options) (`compile_options`), [Gradle dependencies](/docs/publish/android#gradle-dependencies) (`gradle_dependencies`), and manifest [receivers](/docs/publish/android#receivers) (`receiver`) and [services](/docs/publish/android#services) (`service`) ([#6999](https://github.com/flet-dev/flet/pull/6999)) by @ndonkoHenri.
* Add `text_input_action` to `TextField` and `CupertinoTextField`, with the `TextInputAction` enum, to choose the action button of the onscreen keyboard: `TextInputAction.NEXT` moves focus to the next focusable control, keeping the keyboard open when that is another text field ([#6962](https://github.com/flet-dev/flet/issues/6962), [#7021](https://github.com/flet-dev/flet/pull/7021)) by @ndonkoHenri.

### Improvements

Expand Down
1 change: 1 addition & 0 deletions packages/flet/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
* **Breaking:** `parseTheme()` now builds themes from a seed with neutral surfaces by default (`#ffffff` / `#121212` and a neutral grey ramp for surface containers, `onSurface`, `outline` and inverse surfaces); the seed still drives accent colors. A theme's `surfaces: "tonal"` restores the seed-tinted Material 3 surfaces. Adds the `ThemeSurfaces` enum, `parseThemeSurfaces()` and `neutralizeSurfaces()` ([#6921](https://github.com/flet-dev/flet/issues/6921)) by @FeodorFitsner.
* Import Material and Cupertino from the `material_ui` and `cupertino_ui` packages instead of `package:flutter/material.dart` and `package:flutter/cupertino.dart`. Helpers that take or return Material or Cupertino types, such as `parseColor()`, `parseTextStyle()` and `parseButtonStyle()`, now use those packages' types, and `flet.dart` no longer re-exports `Icons` and `CupertinoIcons`. Requires Flutter 3.47 or later.
* Add `markdownStyleSheetFromTheme()`, which builds the default `MarkdownStyleSheet` from a `material_ui` theme. `MarkdownStyleSheet.fromTheme()` from `flutter_markdown_plus` only accepts the Flutter SDK's `ThemeData`.
* Add `text_input_action` to `TextField` and `CupertinoTextField`, with `parseTextInputAction()` and `Control.getTextInputAction()` to parse Flutter's `TextInputAction` ([#6962](https://github.com/flet-dev/flet/issues/6962)) by @ndonkoHenri.
* Fix `FletBackend` running two competing reconnects when a connection reports its disconnect twice, such as a socket reset after a `flet run` hot reload ([#6913](https://github.com/flet-dev/flet/issues/6913), [#6945](https://github.com/flet-dev/flet/pull/6945)) by @ndonkoHenri.

## 1.0.4
Expand Down
1 change: 1 addition & 0 deletions packages/flet/lib/src/controls/cupertino_textfield.dart
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ class _CupertinoTextFieldControlState extends State<CupertinoTextFieldControl> {
? TextInputType.multiline
: widget.control
.getTextInputType("keyboard_type", TextInputType.text)!,
textInputAction: widget.control.getTextInputAction("text_input_action"),
clearButtonSemanticLabel:
widget.control.getString("clear_button_semantics_label"),
autocorrect: autocorrect,
Expand Down
1 change: 1 addition & 0 deletions packages/flet/lib/src/controls/textfield.dart
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ class _TextFieldControlState extends State<TextFieldControl> {
? TextInputType.multiline
: widget.control
.getTextInputType("keyboard_type", TextInputType.text)!,
textInputAction: widget.control.getTextInputAction("text_input_action"),
autocorrect: widget.control.getBool("autocorrect", true)!,
enableSuggestions: widget.control.getBool("enable_suggestions", true)!,
smartDashesType: widget.control.getBool("smart_dashes_type", true)!
Expand Down
10 changes: 10 additions & 0 deletions packages/flet/lib/src/utils/form_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,11 @@ TextInputType? parseTextInputType(String? value,
return typeMap[value?.toLowerCase()] ?? defaultValue;
}

TextInputAction? parseTextInputAction(String? value,
[TextInputAction? defaultValue]) {
return parseEnum(TextInputAction.values, value, defaultValue);
}

InputDecoration buildInputDecoration(
BuildContext context,
Control control, {
Expand Down Expand Up @@ -481,6 +486,11 @@ extension FormFieldParsers on Control {
return parseTextInputType(get(propertyName), defaultValue);
}

TextInputAction? getTextInputAction(String propertyName,
[TextInputAction? defaultValue]) {
return parseTextInputAction(get(propertyName), defaultValue);
}

OverlayVisibilityMode? getOverlayVisibilityMode(String propertyName,
[OverlayVisibilityMode? defaultValue]) {
return parseOverlayVisibilityMode(get(propertyName), defaultValue);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import flet as ft


def main(page: ft.Page):
def send(e: ft.Event[ft.TextField]):
status.value = f"Sent from {name.value} <{email.value}>: {message.value}"

page.add(
ft.SafeArea(
content=ft.Column(
controls=[
name := ft.TextField(
key="name",
label="Name",
text_input_action=ft.TextInputAction.NEXT,
),
email := ft.TextField(
key="email",
label="Email",
keyboard_type=ft.KeyboardType.EMAIL,
text_input_action=ft.TextInputAction.NEXT,
),
message := ft.TextField(
key="message",
label="Message",
text_input_action=ft.TextInputAction.SEND,
on_submit=send,
),
status := ft.Text(),
],
),
),
)


if __name__ == "__main__":
ft.run(main)
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[project]
name = "text-field-keyboard-actions"
version = "1.0.0"
description = "Chains form fields with the keyboard's Next button and sends the form with its Send button."
requires-python = ">=3.10"
keywords = ["text field", "keyboard", "text input action", "next", "form", "focus"]
authors = [{ name = "Flet team", email = "hello@flet.dev" }]
dependencies = ["flet"]

[dependency-groups]
dev = ["flet-cli", "flet-desktop", "flet-web"]

[tool.flet.gallery]
categories = ["Input/TextField"]

[tool.flet.metadata]
title = "Keyboard actions"
docs_intro = """
Set [`text_input_action`][flet.TextField.text_input_action] to choose the action button of the onscreen keyboard.

Here, the keyboard's Next button moves from one field to the next while the keyboard stays open, and its Send button on the last field calls [`on_submit`][flet.TextField.on_submit]. On desktop and web, `Enter` does the same."""
controls = ["SafeArea", "Column", "TextField", "Text"]
layout_pattern = "form"
complexity = "basic"
features = ["keyboard action button", "focus chaining", "form submission"]

[tool.flet]
org = "dev.flet"
company = "Flet"
copyright = "Copyright (C) 2023-2026 by Flet"
3 changes: 3 additions & 0 deletions sdk/python/packages/flet/src/flet/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,7 @@
TextCapitalization,
TextField,
TextFieldPasteFilesEvent,
TextInputAction,
TextOnlyInputFilter,
)
from flet.controls.material.time_picker import (
Expand Down Expand Up @@ -1205,6 +1206,7 @@
"TextField",
"TextFieldPasteFilesEvent",
"TextFieldTheme",
"TextInputAction",
"TextOnlyInputFilter",
"TextOverflow",
"TextSelection",
Expand Down Expand Up @@ -1754,6 +1756,7 @@
"TextDecorationStyle": "flet.controls.text_style",
"TextField": "flet.controls.material.textfield",
"TextFieldPasteFilesEvent": "flet.controls.material.textfield",
"TextInputAction": "flet.controls.material.textfield",
"TextOnlyInputFilter": "flet.controls.material.textfield",
"TextOverflow": "flet.controls.text_style",
"TextSelection": "flet.controls.core.text",
Expand Down
180 changes: 173 additions & 7 deletions sdk/python/packages/flet/src/flet/controls/material/textfield.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from dataclasses import dataclass
from dataclasses import dataclass, field
from enum import Enum
from typing import Annotated, Optional, Union

Expand Down Expand Up @@ -28,6 +28,7 @@
"TextCapitalization",
"TextField",
"TextFieldPasteFilesEvent",
"TextInputAction",
"TextOnlyInputFilter",
]

Expand Down Expand Up @@ -184,7 +185,7 @@ class KeyboardType(Enum):

class TextCapitalization(Enum):
"""
Configures how the platform keyboard will select an uppercase or lowercase \
Configures how the platform keyboard will select an uppercase or lowercase
keyboard.

Only supports text keyboards, other keyboard types will ignore this
Expand Down Expand Up @@ -224,10 +225,143 @@ class TextCapitalization(Enum):
"""


class TextInputAction(Enum):
"""
The action button of the onscreen keyboard, and what pressing it or `Enter` does.

Each action lists the mobile platforms whose keyboards support it. In debug
builds on Android and iOS, such as an app running on the iOS Simulator, a field
with an action that the platform doesn't support gets focus without showing the
keyboard, and no error is reported. On Android, release builds show the
keyboard with its default action button instead.
"""

NONE = "none"
"""
No action specific to the field; the platform decides how the keyboard looks.

Keeps focus in the field, so the keyboard stays open.

Note:
Supported on Android only.
"""

UNSPECIFIED = "unspecified"
"""
Lets the platform choose the most appropriate action.

Keeps focus in the field, so the keyboard stays open.

Supported on Android and iOS.
"""

DONE = "done"
"""
The user is done entering input, such as at the end of a form.

Removes focus from the field, which closes the keyboard.

Supported on Android and iOS.
"""

GO = "go"
"""
Takes the user to the destination they typed, such as a URL.

Removes focus from the field, which closes the keyboard.

Supported on Android and iOS.
"""

SEARCH = "search"
"""
Runs a search for the typed text.

Removes focus from the field, which closes the keyboard.

Supported on Android and iOS.
"""

SEND = "send"
"""
Sends what the user typed, such as a message.

Removes focus from the field, which closes the keyboard.

Supported on Android and iOS.
"""

NEXT = "next"
"""
Moves focus to the next focusable control, keeping the keyboard open when it's
another text field.

Supported on Android and iOS.
"""

PREVIOUS = "previous"
"""
Moves focus to the previous focusable control, keeping the keyboard open when
it's another text field.

Note:
Supported on Android only.
"""

CONTINUE_ACTION = "continueAction"
"""
Continues to the next step, such as the next page of a sign-up flow.

Doesn't move focus: the field keeps it, so the keyboard stays open.

Note:
Supported on iOS only.
"""

JOIN = "join"
"""
Joins something, such as a wireless network.

Keeps focus in the field, so the keyboard stays open.

Note:
Supported on iOS only.
"""

ROUTE = "route"
"""
Shows routing options, such as driving directions.

Keeps focus in the field, so the keyboard stays open.

Note:
Supported on iOS only.
"""

EMERGENCY_CALL = "emergencyCall"
"""
Calls emergency services.

Keeps focus in the field, so the keyboard stays open.

Note:
Supported on iOS only.
"""

NEWLINE = "newline"
"""
Inserts a new line into a multiline field.

On a single-line field, it removes focus from the field like :attr:`DONE`.

Supported on Android and iOS.
"""


@value
class InputFilter:
"""
An input filter that uses a regular expression to allow or deny/block certain \
An input filter that uses a regular expression to allow or deny/block certain
patterns in the input.
"""

Expand Down Expand Up @@ -308,7 +442,7 @@ def __init__(self):
)
class TextField(FormFieldControl, AdaptiveControl):
"""
A text field lets the user enter text, either with hardware keyboard or with an \
A text field lets the user enter text, either with hardware keyboard or with an
onscreen keyboard.

Example:
Expand Down Expand Up @@ -407,7 +541,7 @@ class TextField(FormFieldControl, AdaptiveControl):

can_reveal_password: bool = False
"""
Displays a toggle icon button that allows revealing the entered password. Is shown \
Displays a toggle icon button that allows revealing the entered password. Is shown
if both :attr:`password` and `can_reveal_password` are `True`.

The icon is displayed in the same location as :attr:`flet.FormFieldControl.suffix`
Expand All @@ -425,8 +559,8 @@ class TextField(FormFieldControl, AdaptiveControl):

shift_enter: bool = False
"""
Changes the behavior of `Enter` button in :attr:`multiline` textfield to be \
chat-like, i.e. new line can be added with `Shift`+`Enter` and pressing just \
Changes the behavior of `Enter` button in :attr:`multiline` textfield to be
chat-like, i.e. new line can be added with `Shift`+`Enter` and pressing just
`Enter` fires :attr:`on_submit` event.
"""

Expand Down Expand Up @@ -704,6 +838,38 @@ class TextField(FormFieldControl, AdaptiveControl):
details.
"""

text_input_action: Optional[TextInputAction] = field(default=None, kw_only=True)
"""
The action button of the onscreen keyboard, and what pressing it or `Enter` does.

:attr:`~flet.TextInputAction.NEXT` and :attr:`~flet.TextInputAction.PREVIOUS`
move focus to the next or previous focusable control in reading order, which
can be a button in this field's :attr:`~flet.FormFieldControl.suffix`. When
that control is another text field, the keyboard stays open.
See :class:`~flet.TextInputAction` for what the other actions do and the
platforms that support them.

On a single-line field, every action also calls :attr:`on_submit`.
On a :attr:`multiline` field, the action doesn't call :attr:`on_submit`, and
with an action other than :attr:`~flet.TextInputAction.NEWLINE`, `Enter` and
the keyboard's action key usually perform the action instead of inserting a
new line. On the web, that includes `Shift+Enter` on a :attr:`shift_enter`
field.

If not set, a single-line field uses :attr:`~flet.TextInputAction.DONE` and a
:attr:`multiline` field uses :attr:`~flet.TextInputAction.NEWLINE`.

A change made while this field has focus applies the next time it gets focus.

Example:
```python
page.add(
ft.TextField(label="Name", text_input_action=ft.TextInputAction.NEXT),
ft.TextField(label="Email", text_input_action=ft.TextInputAction.DONE),
)
```
"""

def _migrate_state(self, other: BaseControl):
super()._migrate_state(other)
if (
Expand Down
2 changes: 2 additions & 0 deletions website/docs/controls/textfield.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import {ClassMembers, ClassSummary, CodeExample, Image} from '@site/src/componen

<Image src={frontMatter.example_images + '/multiline.gif'} alt="multiline" width="55%" />

<CodeExample path={frontMatter.examples + '/keyboard_actions/main.py'} language="python" />

<CodeExample path={frontMatter.examples + '/underlined_and_borderless/main.py'} language="python" />

<Image src={frontMatter.example_images + '/underlined_and_borderless.gif'} alt="underlined-and-borderless" width="55%" />
Expand Down
Loading
Loading