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
21 changes: 16 additions & 5 deletions package/lib/src/controls/textfield.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import '../protocol/update_control_props_payload.dart';
import '../utils/borders.dart';
import '../utils/colors.dart';
import '../utils/text.dart';
import '../utils/textfield.dart';
import 'create_control.dart';
import 'form_field.dart';

Expand Down Expand Up @@ -162,6 +163,19 @@ class _TextFieldControlState extends State<TextFieldControl> {
.toLowerCase(),
orElse: () => TextCapitalization.none);

FilteringTextInputFormatter? inputFilter =
parseInputFilter(widget.control, "inputFilter");

List<TextInputFormatter>? inputFormatters = [];
// add non-null input formatters
if (inputFilter != null) {
inputFormatters.add(inputFilter);
}
if (textCapitalization != TextCapitalization.none) {
inputFormatters
.add(TextCapitalizationFormatter(textCapitalization));
}

Widget? revealPasswordIcon;
if (password && canRevealPassword) {
revealPasswordIcon = GestureDetector(
Expand Down Expand Up @@ -241,11 +255,8 @@ class _TextFieldControlState extends State<TextFieldControl> {
maxLines: maxLines,
maxLength: maxLength,
readOnly: readOnly,
inputFormatters: textCapitalization != TextCapitalization.none
? [
TextCapitalizationFormatter(textCapitalization),
]
: null,
inputFormatters:
inputFormatters.isNotEmpty ? inputFormatters : null,
obscureText: password && !_revealPassword,
controller: _controller,
focusNode: focusNode,
Expand Down
36 changes: 36 additions & 0 deletions package/lib/src/utils/textfield.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'dart:convert';

import 'package:flet/src/utils/numbers.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import '../models/control.dart';

FilteringTextInputFormatter? parseInputFilter(
Control control, String propName) {
var v = control.attrString(propName, null);
if (v == null) {
return null;
}

final j1 = json.decode(v);
return inputFilterFromJSON(j1);
}

FilteringTextInputFormatter inputFilterFromJSON(dynamic json) {
bool allow = true;
String? regexString = "";
String? replacementString = "";

if (json != null) {
allow = parseBool(json["allow"], true);
regexString = json["regex_string"]?.toString();
replacementString = json["replacement_string"]?.toString();
}

debugPrint(
"Textfield inputFilter - allow: $allow | regexString: $regexString | replacementString: $replacementString");

return FilteringTextInputFormatter(RegExp(regexString ?? ""),
allow: allow, replacementString: replacementString ?? "");
}
9 changes: 8 additions & 1 deletion sdk/python/packages/flet-core/src/flet_core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,14 @@
from flet_core.text_button import TextButton
from flet_core.text_span import TextSpan
from flet_core.text_style import TextDecoration, TextDecorationStyle, TextStyle
from flet_core.textfield import KeyboardType, TextCapitalization, TextField
from flet_core.textfield import (
KeyboardType,
InputFilter,
NumbersOnlyInputFilter,
TextCapitalization,
TextField,
TextOnlyInputFilter,
)
from flet_core.theme import (
ColorScheme,
PageTransitionsTheme,
Expand Down
29 changes: 29 additions & 0 deletions sdk/python/packages/flet-core/src/flet_core/textfield.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import dataclasses
from dataclasses import field
import time
from enum import Enum
from typing import Any, Optional, Union
Expand Down Expand Up @@ -63,6 +65,21 @@ class TextCapitalization(Enum):
SENTENCES = "sentences"


@dataclasses.dataclass
class InputFilter:
regex_string: str
allow: bool = field(default=True)
replacement_string: str = field(default="")

class NumbersOnlyInputFilter(InputFilter):
def __init__(self):
super().__init__(r"[0-9]")

class TextOnlyInputFilter(InputFilter):
def __init__(self):
super().__init__(r"[a-zA-Z]")


class TextField(FormFieldControl):
"""
A text field lets the user enter text, either with hardware keyboard or with an onscreen keyboard.
Expand Down Expand Up @@ -179,6 +196,7 @@ def __init__(
cursor_height: OptionalNumber = None,
cursor_radius: OptionalNumber = None,
selection_color: Optional[str] = None,
input_filter: Optional[InputFilter] = None,
on_change=None,
on_submit=None,
on_focus=None,
Expand Down Expand Up @@ -269,6 +287,7 @@ def __init__(
self.cursor_width = cursor_width
self.cursor_radius = cursor_radius
self.selection_color = selection_color
self.input_filter = input_filter
self.on_change = on_change
self.on_submit = on_submit
self.on_focus = on_focus
Expand All @@ -279,6 +298,7 @@ def _get_control_name(self):

def _before_build_command(self):
super()._before_build_command()
self._set_attr_json("inputFilter", self.__input_filter)
if self.bgcolor is not None and self.filled is None:
self.filled = True # Flutter requires filled = True to display a bgcolor

Expand Down Expand Up @@ -509,6 +529,15 @@ def selection_color(self):
def selection_color(self, value):
self._set_attr("selectionColor", value)

# input_filter
@property
def input_filter(self) -> Optional[InputFilter]:
return self.__input_filter

@input_filter.setter
def input_filter(self, value: Optional[InputFilter]):
self.__input_filter = value

# on_change
@property
def on_change(self):
Expand Down