Skip to content

Commit 7f89312

Browse files
authored
Feature: TextField Input validation (#2101)
* add ´InputFilter´ * create textfield utils * update textfield.dart * update return type * remove `regex_string` default * add predefined InputFilters
1 parent 4815439 commit 7f89312

File tree

4 files changed

+89
-6
lines changed

4 files changed

+89
-6
lines changed

package/lib/src/controls/textfield.dart

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import '../protocol/update_control_props_payload.dart';
1010
import '../utils/borders.dart';
1111
import '../utils/colors.dart';
1212
import '../utils/text.dart';
13+
import '../utils/textfield.dart';
1314
import 'create_control.dart';
1415
import 'form_field.dart';
1516

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

166+
FilteringTextInputFormatter? inputFilter =
167+
parseInputFilter(widget.control, "inputFilter");
168+
169+
List<TextInputFormatter>? inputFormatters = [];
170+
// add non-null input formatters
171+
if (inputFilter != null) {
172+
inputFormatters.add(inputFilter);
173+
}
174+
if (textCapitalization != TextCapitalization.none) {
175+
inputFormatters
176+
.add(TextCapitalizationFormatter(textCapitalization));
177+
}
178+
165179
Widget? revealPasswordIcon;
166180
if (password && canRevealPassword) {
167181
revealPasswordIcon = GestureDetector(
@@ -241,11 +255,8 @@ class _TextFieldControlState extends State<TextFieldControl> {
241255
maxLines: maxLines,
242256
maxLength: maxLength,
243257
readOnly: readOnly,
244-
inputFormatters: textCapitalization != TextCapitalization.none
245-
? [
246-
TextCapitalizationFormatter(textCapitalization),
247-
]
248-
: null,
258+
inputFormatters:
259+
inputFormatters.isNotEmpty ? inputFormatters : null,
249260
obscureText: password && !_revealPassword,
250261
controller: _controller,
251262
focusNode: focusNode,
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import 'dart:convert';
2+
3+
import 'package:flet/src/utils/numbers.dart';
4+
import 'package:flutter/material.dart';
5+
import 'package:flutter/services.dart';
6+
7+
import '../models/control.dart';
8+
9+
FilteringTextInputFormatter? parseInputFilter(
10+
Control control, String propName) {
11+
var v = control.attrString(propName, null);
12+
if (v == null) {
13+
return null;
14+
}
15+
16+
final j1 = json.decode(v);
17+
return inputFilterFromJSON(j1);
18+
}
19+
20+
FilteringTextInputFormatter inputFilterFromJSON(dynamic json) {
21+
bool allow = true;
22+
String? regexString = "";
23+
String? replacementString = "";
24+
25+
if (json != null) {
26+
allow = parseBool(json["allow"], true);
27+
regexString = json["regex_string"]?.toString();
28+
replacementString = json["replacement_string"]?.toString();
29+
}
30+
31+
debugPrint(
32+
"Textfield inputFilter - allow: $allow | regexString: $regexString | replacementString: $replacementString");
33+
34+
return FilteringTextInputFormatter(RegExp(regexString ?? ""),
35+
allow: allow, replacementString: replacementString ?? "");
36+
}

sdk/python/packages/flet-core/src/flet_core/__init__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,14 @@
169169
from flet_core.text_button import TextButton
170170
from flet_core.text_span import TextSpan
171171
from flet_core.text_style import TextDecoration, TextDecorationStyle, TextStyle
172-
from flet_core.textfield import KeyboardType, TextCapitalization, TextField
172+
from flet_core.textfield import (
173+
KeyboardType,
174+
InputFilter,
175+
NumbersOnlyInputFilter,
176+
TextCapitalization,
177+
TextField,
178+
TextOnlyInputFilter,
179+
)
173180
from flet_core.theme import (
174181
ColorScheme,
175182
PageTransitionsTheme,

sdk/python/packages/flet-core/src/flet_core/textfield.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import dataclasses
2+
from dataclasses import field
13
import time
24
from enum import Enum
35
from typing import Any, Optional, Union
@@ -63,6 +65,21 @@ class TextCapitalization(Enum):
6365
SENTENCES = "sentences"
6466

6567

68+
@dataclasses.dataclass
69+
class InputFilter:
70+
regex_string: str
71+
allow: bool = field(default=True)
72+
replacement_string: str = field(default="")
73+
74+
class NumbersOnlyInputFilter(InputFilter):
75+
def __init__(self):
76+
super().__init__(r"[0-9]")
77+
78+
class TextOnlyInputFilter(InputFilter):
79+
def __init__(self):
80+
super().__init__(r"[a-zA-Z]")
81+
82+
6683
class TextField(FormFieldControl):
6784
"""
6885
A text field lets the user enter text, either with hardware keyboard or with an onscreen keyboard.
@@ -179,6 +196,7 @@ def __init__(
179196
cursor_height: OptionalNumber = None,
180197
cursor_radius: OptionalNumber = None,
181198
selection_color: Optional[str] = None,
199+
input_filter: Optional[InputFilter] = None,
182200
on_change=None,
183201
on_submit=None,
184202
on_focus=None,
@@ -269,6 +287,7 @@ def __init__(
269287
self.cursor_width = cursor_width
270288
self.cursor_radius = cursor_radius
271289
self.selection_color = selection_color
290+
self.input_filter = input_filter
272291
self.on_change = on_change
273292
self.on_submit = on_submit
274293
self.on_focus = on_focus
@@ -279,6 +298,7 @@ def _get_control_name(self):
279298

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

@@ -509,6 +529,15 @@ def selection_color(self):
509529
def selection_color(self, value):
510530
self._set_attr("selectionColor", value)
511531

532+
# input_filter
533+
@property
534+
def input_filter(self) -> Optional[InputFilter]:
535+
return self.__input_filter
536+
537+
@input_filter.setter
538+
def input_filter(self, value: Optional[InputFilter]):
539+
self.__input_filter = value
540+
512541
# on_change
513542
@property
514543
def on_change(self):

0 commit comments

Comments
 (0)