Skip to content

Commit

Permalink
Feature: TextField Input validation (flet-dev#2101)
Browse files Browse the repository at this point in the history
* add ´InputFilter´

* create textfield utils

* update textfield.dart

* update return type

* remove `regex_string` default

* add predefined InputFilters
  • Loading branch information
ndonkoHenri authored and zrr1999 committed Jul 17, 2024
1 parent 5194ba2 commit cebdb22
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 6 deletions.
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

0 comments on commit cebdb22

Please sign in to comment.