| name | flet-validation |
| description | Use whenever editing validation for Python controls in sdk/python/packages/: adding/changing constrained properties, `Raises: ValueError` docstrings, `before_update()` checks, `raise ValueError`, Annotated/V rules, or __validation_rules__. |
When To Use
Use this skill when you:
- add validation to a control property,
- migrate manual
before_update() checks to V rules,
- update
Raises sections tied to validation,
- align Python validation with Dart-side control behavior.
Do not use this skill for unrelated doc-only edits outside control validation.
Do not use this skill for deprecation authoring conventions; use
flet-deprecation.
Source Of Truth
- Validation runtime API lives in:
sdk/python/packages/flet/src/flet/utils/validation.py
- The same runtime can validate regular dataclass models via
validate(instance);
this skill focuses on the control-specific authoring conventions.
- Import from public path only:
from flet.utils.validation import V
and when needed
from flet.utils.validation import ValidationRules
Validation Decision Order
Use this order and stop at the first option that keeps logic clear:
Annotated[..., V.*] field rules (default).
__validation_rules__: ValidationRules for cross-field invariants that do
not map cleanly to one field.
before_update() only for normalization/mutation, or for truly non-ruleable
invariants.
Never duplicate the same invariant in more than one layer.
Validation Authoring Rules
-
Prefer field-level validation with Annotated[...] metadata.
- Example (close to current slider controls):
@dataclass
class Sample:
opacity: Annotated[
Optional[Number],
V.between(0.0, 1.0),
] = None
value: Annotated[
Optional[Number],
V.ge_field("min"),
V.le_field("max"),
] = None
min: Annotated[
Number,
V.le_field("max"),
V.le_field("value"),
] = 0.0
max: Annotated[
Number,
V.ge_field("min"),
V.ge_field("value"),
] = 1.0
-
Use class-level __validation_rules__ only for invariants that cannot be
expressed cleanly with field rules.
- Type:
__validation_rules__: ValidationRules = (...)
- Use
V.ensure(...) with an explicit message only when the predicate is
short and readable.
- If
V.ensure(lambda ...) becomes hard to read, prefer either:
- a named predicate function passed to
V.ensure(...), or
- a clear
before_update() check when it cannot be represented well with
existing V.* rules.
-
Keep the before_update() override for normalization/mutation first.
- Remove validation checks duplicated by
V rules.
- Prefer raising validation errors from rule evaluation (
validate()), not
from ad-hoc before_update() checks, unless the invariant is genuinely
non-ruleable with current validation primitives.
-
Cross-field comparisons use field rules only.
- Use:
V.gt_field, V.ge_field, V.lt_field, V.le_field
-
None handling is inferred from type hints.
- If a field is annotated as optional (for example
Optional[T]), None is allowed.
- If a field is not optional,
None fails validation.
- For
*_field comparisons, if either side is optional and currently None, the
comparison is skipped; if a non-optional side is None, validation fails.
-
Match Dart effective behavior.
- Review both:
- Flet Dart wrapper (
packages/flet/lib/src/controls/<control>.dart or
extension wrapper),
- wrapped Flutter widget source assertions/invariants.
- Mirror those constraints in Python so invalid payloads fail before crossing
to Dart.
- Validate against effective defaults applied on Dart side (for example
min/max).
- Include wrapper-imposed constraints when relevant (for example
bounds/rounding/division logic in wrapper formatting code).
-
For new properties, validate against base widget assertions before wiring.
- When adding a Python property to a Flet control, confirm whether the mapped
Dart wrapper and underlying widget both enforce constraints for that property.
- Add equivalent Python-side validation (
Annotated[...], __validation_rules__,
or readable before_update()) to prevent Dart assertions from being the first
failure point.
-
Keep error ownership clear.
- Runtime outbound value failures should raise
ValueError.
- Validation declaration/build errors (invalid
V.* arguments) should raise
ValidationDeclarationError.
Typing Style
- Follow the codebase typing style:
- prefer
Optional[T] over T | None,
- use
Union[...] when needed.
Property Docstring Style
When a property has validation, document it in that property’s docstring (google style).
-
Add a Raises: block under the property docstring.
-
Use one ValueError entry per logical rule, except between(...).
- For
V.between(a, b), use one entry:
ValueError: If it is not between \a` and `b`, inclusive.`
-
Start each entry with If it ..., where 'it' refers to the property name.
-
Use canonical wording from validation helper docstrings.
- The source of truth is
sdk/python/packages/flet/src/flet/utils/validation.py.
- Each
V.* helper includes Property docstring Raises wording.
- Keep property
Raises entries as negations of the annotation rule.
- For strict inequalities, say
"strictly".
For example: V.gt(x) -> ValueError: If it is not strictly greater than \x`.`
- For sign-neutral divisibility helpers (
factor_of, multiple_of), add
explicit sign rules (V.gt(0) or V.lt(0)) when direction matters, and
include separate Raises entries for those sign rules.
-
Mention conditional applicability when needed.
-
Keep examples and wording aligned with real control files.
- Prefer concrete property names such as
min, max, value,
start_value, end_value, min_lines, max_lines.
- For cross-field rules, use same-class links: [
min][(c).], [max][(c).].
Cross-Referencing Conventions
Follow the cross-reference guidance in:
docs-conventions.
Most common pattern to use in control property docstrings:
- same-class properties:
[\prop`][(c).]`
Keep symbol labels wrapped in backticks.
Required Test Matrix
When adding/changing validation, include tests that cover:
- one valid case and one invalid case per new logical rule;
- boundary values (
==, min/max edges) where applicable;
- cross-field set/unset combinations when optional values are involved;
- effective-default behavior when Dart applies defaults but Python value is
omitted.
Prefer placing tests in:
sdk/python/packages/flet/tests/test_validation.py for validation runtime;
- control-specific tests when behavior is tied to one control.
Common Pitfalls
- Keeping stale manual
before_update() validations after migration.
- Missing
Raises entries on validated properties.
- Combining multiple non-between rules into one ambiguous
ValueError sentence.
- Forgetting Dart defaults that still apply when Python property is omitted.
- Forcing complex
V.ensure(lambda ...) expressions when before_update() would be cleaner.