Skip to content

feat: expose more properties in Controls #4105

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 31 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
aa03e37
nav bar/drawer/rail
ndonkoHenri Sep 22, 2024
69e63ce
TooltipDirection enum has wrong values
ndonkoHenri Sep 22, 2024
f26c80f
Text and TextSpan
ndonkoHenri Sep 23, 2024
30f4721
GestureDetector
ndonkoHenri Sep 23, 2024
d370c00
deprecate MapPointerDeviceType in favor of PointerDeviceType
ndonkoHenri Sep 23, 2024
7874d9e
create BoxConstraints dataclass
ndonkoHenri Sep 22, 2024
e99fe89
update utils
ndonkoHenri Sep 24, 2024
7901af7
fix navbar appearance when disabled
ndonkoHenri Sep 24, 2024
ca7aee8
PopupMenu new props
ndonkoHenri Sep 24, 2024
84daa44
move AnimationValue from types.py to animation.py
ndonkoHenri Sep 24, 2024
f31c377
update
ndonkoHenri Sep 30, 2024
bd12d20
Tab: icon_content, height, icon_margin
ndonkoHenri Oct 4, 2024
c8c587e
New props: TextField, Dropdown, CupertinoTextField
ndonkoHenri Oct 5, 2024
de5e4cb
FormField: make suffix_icon and prefix_icon support both string and C…
ndonkoHenri Oct 5, 2024
bb283a4
refactor ControlState props
ndonkoHenri Oct 5, 2024
36ab786
properly disable navbar
ndonkoHenri Oct 5, 2024
a65be58
fix failing CI: NoneType is not available in py3.8
ndonkoHenri Oct 5, 2024
d8ffc0d
fix failing CI: update expected test value
ndonkoHenri Oct 5, 2024
a4ccf64
fix failing CI: import ControlState
ndonkoHenri Oct 5, 2024
810126c
FormField: add `collapsed` prop which fits field's content (#4106)
ndonkoHenri Oct 6, 2024
f2d94b9
remove _set_control_state_attr_json; add wrap_attr_dict boolean prop …
ndonkoHenri Oct 10, 2024
26ef254
FormField: fit_parent_size prop
ndonkoHenri Oct 10, 2024
fb71143
Define __post_init__ method for Dataclasses making use of ControlState
ndonkoHenri Oct 11, 2024
2d673ed
Merge branch 'main' into new-props
ndonkoHenri Oct 18, 2024
e3cf7e8
merge 'main' branch
ndonkoHenri Oct 19, 2024
a28398f
remove Optional in AnimationValue
ndonkoHenri Oct 25, 2024
ce8fdee
Merge 'main' branch
ndonkoHenri Oct 25, 2024
83042c7
`FormField.label` as a Control
ndonkoHenri Oct 26, 2024
612c8ae
Merge branch 'main' into new-props
FeodorFitsner Oct 29, 2024
0a3afdd
Fix wrapping ListTile to Material widget
FeodorFitsner Oct 29, 2024
a44bcde
Merge branch 'main' into new-props
FeodorFitsner Oct 29, 2024
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
12 changes: 6 additions & 6 deletions packages/flet/lib/src/controls/create_control.dart
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ Widget createWidget(
children: controlView.children,
control: controlView.control,
parentDisabled: parentDisabled,
parentAdaptive: parentAdaptive,
backend: backend);
case "divider":
return DividerControl(
Expand Down Expand Up @@ -1163,11 +1164,11 @@ Widget _scaledControl(

Widget _offsetControl(
BuildContext context, Widget widget, Control? parent, Control control) {
var offsetDetails = parseOffset(control, "offset");
var offset = parseOffset(control, "offset");
var animation = parseAnimation(control, "animateOffset");
if (offsetDetails != null && animation != null) {
if (offset != null && animation != null) {
return AnimatedSlide(
offset: Offset(offsetDetails.x, offsetDetails.y),
offset: offset,
duration: animation.duration,
curve: animation.curve,
onEnd: control.attrBool("onAnimationEnd", false)!
Expand All @@ -1178,9 +1179,8 @@ Widget _offsetControl(
}
: null,
child: widget);
} else if (offsetDetails != null) {
return FractionalTranslation(
translation: Offset(offsetDetails.x, offsetDetails.y), child: widget);
} else if (offset != null) {
return FractionalTranslation(translation: offset, child: widget);
}
return widget;
}
Expand Down
82 changes: 40 additions & 42 deletions packages/flet/lib/src/controls/cupertino_textfield.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import '../utils/edge_insets.dart';
import '../utils/form_field.dart';
import '../utils/gradient.dart';
import '../utils/images.dart';
import '../utils/others.dart';
import '../utils/overlay_style.dart';
import '../utils/text.dart';
import '../utils/textfield.dart';
import 'create_control.dart';
Expand Down Expand Up @@ -159,22 +161,14 @@ class _CupertinoTextFieldControlState extends State<CupertinoTextFieldControl>
inputFormatters.add(TextCapitalizationFormatter(textCapitalization));
}

TextInputType keyboardType = multiline
? TextInputType.multiline
: parseTextInputType(
widget.control.attrString("keyboardType"), TextInputType.text)!;

TextAlign textAlign = parseTextAlign(
widget.control.attrString("textAlign"), TextAlign.start)!;

double? textVerticalAlign = widget.control.attrDouble("textVerticalAlign");

bool rtl = widget.control.attrBool("rtl", false)!;
bool autocorrect = widget.control.attrBool("autocorrect", true)!;
bool enableSuggestions =
widget.control.attrBool("enableSuggestions", true)!;
bool smartDashesType = widget.control.attrBool("smartDashesType", true)!;
bool smartQuotesType = widget.control.attrBool("smartQuotesType", true)!;
;

FocusNode focusNode = shiftEnter ? _shiftEnterfocusNode : _focusNode;

Expand Down Expand Up @@ -228,29 +222,25 @@ class _CupertinoTextFieldControlState extends State<CupertinoTextFieldControl>
}),
);
}

var fitParentSize = widget.control.attrBool("fitParentSize", false)!;
BoxDecoration? defaultDecoration = const CupertinoTextField().decoration;
var gradient = parseGradient(Theme.of(context), widget.control, "gradient");
var blendMode = parseBlendMode(widget.control.attrString("blendMode"));

var bgColor = widget.control.attrColor("bgColor", context);
// for adaptive TextField use label for placeholder
var placeholder = widget.control.attrString("placeholderText") ??
widget.control.attrString("label");
// for adaptive TextField use labelStyle for placeholderStyle
var placeholderStyle =
parseTextStyle(Theme.of(context), widget.control, "placeholderStyle") ??
parseTextStyle(Theme.of(context), widget.control, "labelStyle");

return withPageArgs((context, pageArgs) {
var decorationImage = parseDecorationImage(
Theme.of(context), widget.control, "image", pageArgs);
Widget textField = CupertinoTextField(
style: textStyle,
textAlignVertical: textVerticalAlign != null
? TextAlignVertical(y: textVerticalAlign)
: null,
placeholder: placeholder,
placeholderStyle: placeholderStyle,
placeholder: widget.control.attrString("placeholderText") ??
widget.control.attrString("label"),
// use label for adaptive TextField
placeholderStyle: parseTextStyle(Theme.of(context), widget.control, "placeholderStyle") ??
parseTextStyle(Theme.of(context), widget.control, "labelStyle"),
// labelStyle for adaptive TextField
autofocus: autofocus,
enabled: !disabled,
onSubmitted: !multiline
Expand All @@ -262,7 +252,8 @@ class _CupertinoTextFieldControlState extends State<CupertinoTextFieldControl>
decoration: defaultDecoration?.copyWith(
color: bgColor,
gradient: gradient,
image: decorationImage,
image: parseDecorationImage(
Theme.of(context), widget.control, "image", pageArgs),
backgroundBlendMode:
bgColor != null || gradient != null ? blendMode : null,
border: border,
Expand All @@ -274,47 +265,54 @@ class _CupertinoTextFieldControlState extends State<CupertinoTextFieldControl>
cursorWidth: widget.control.attrDouble("cursorWidth", 2.0)!,
cursorRadius: parseRadius(
widget.control, "cursorRadius", const Radius.circular(2.0))!,
keyboardType: keyboardType,
keyboardType: multiline
? TextInputType.multiline
: parseTextInputType(widget.control.attrString("keyboardType"),
TextInputType.text)!,
clearButtonSemanticLabel:
widget.control.attrString("clearButtonSemanticsLabel"),
autocorrect: autocorrect,
enableSuggestions: enableSuggestions,
smartDashesType: smartDashesType
enableSuggestions:
widget.control.attrBool("enableSuggestions", true)!,
smartDashesType: widget.control.attrBool("smartDashesType", true)!
? SmartDashesType.enabled
: SmartDashesType.disabled,
smartQuotesType: smartQuotesType
smartQuotesType: widget.control.attrBool("smartQuotesType", true)!
? SmartQuotesType.enabled
: SmartQuotesType.disabled,
suffixMode: parseVisibilityMode(
widget.control.attrString("suffixVisibilityMode", "")!),
widget.control.attrString("suffixVisibilityMode"),
OverlayVisibilityMode.always)!,
prefixMode: parseVisibilityMode(
widget.control.attrString("prefixVisibilityMode", "")!),
widget.control.attrString("prefixVisibilityMode"),
OverlayVisibilityMode.always)!,
textAlign: textAlign,
minLines: minLines,
maxLines: maxLines,
minLines: fitParentSize ? null : minLines,
maxLines: fitParentSize ? null : maxLines,
maxLength: maxLength,
prefix: prefixControls.isNotEmpty
? createControl(widget.control, prefixControls.first.id, disabled,
parentAdaptive: widget.parentAdaptive)
: null,
suffix: revealPasswordIcon ??
(suffixControls.isNotEmpty
? createControl(
widget.control, suffixControls.first.id, disabled,
parentAdaptive: widget.parentAdaptive)
: null),
suffix: revealPasswordIcon ?? (suffixControls.isNotEmpty ? createControl(widget.control, suffixControls.first.id, disabled, parentAdaptive: widget.parentAdaptive) : null),
readOnly: readOnly,
textDirection: rtl ? TextDirection.rtl : null,
inputFormatters: inputFormatters.isNotEmpty ? inputFormatters : null,
obscureText: password && !_revealPassword,
padding: parseEdgeInsets(
widget.control, "padding", const EdgeInsets.all(7.0))!,
padding: parseEdgeInsets(widget.control, "padding", const EdgeInsets.all(7.0))!,
scribbleEnabled: widget.control.attrBool("enableScribble", true)!,
scrollPadding: parseEdgeInsets(widget.control, "scrollPadding", const EdgeInsets.all(20.0))!,
obscuringCharacter: widget.control.attrString("obscuringCharacter", '•')!,
cursorOpacityAnimates: widget.control.attrBool("animateCursorOpacity", Theme.of(context).platform == TargetPlatform.iOS)!,
expands: fitParentSize,
enableIMEPersonalizedLearning: widget.control.attrBool("enableIMEPersonalizedLearning", true)!,
clipBehavior: parseClip(widget.control.attrString("clipBehavior"), Clip.hardEdge)!,
cursorColor: cursorColor,
autofillHints: parseAutofillHints(widget.control, "autofillHints"),
scrollPadding: parseEdgeInsets(
widget.control, "scrollPadding", const EdgeInsets.all(20.0))!,
obscuringCharacter:
widget.control.attrString("obscuringCharacter", '•')!,
keyboardAppearance: parseBrightness(widget.control.attrString("keyboardBrightness")),
enableInteractiveSelection: widget.control.attrBool("enableInteractiveSelection"),
clearButtonMode: parseVisibilityMode(widget.control.attrString("clearButtonVisibilityMode"), OverlayVisibilityMode.never)!,
strutStyle: parseStrutStyle(widget.control, "strutStyle"),
onTap: () {
widget.backend.triggerControlEvent(widget.control.id, "click");
},
Expand Down
17 changes: 5 additions & 12 deletions packages/flet/lib/src/controls/cupertino_timer_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,16 @@ class _CupertinoTimerPickerControlState

int value = widget.control.attrInt("value", 0)!;
Duration initialTimerDuration = Duration(seconds: value);
int minuteInterval =
widget.control.attrDouble("minuteInterval", 1)!.toInt();
int secondInterval =
widget.control.attrDouble("secondInterval", 1)!.toInt();
CupertinoTimerPickerMode mode = parseCupertinoTimerPickerMode(
widget.control.attrString("mode"), CupertinoTimerPickerMode.hms)!;

Color? backgroundColor = widget.control.attrColor("bgColor", context);

Widget picker = CupertinoTimerPicker(
mode: mode,
mode: parseCupertinoTimerPickerMode(
widget.control.attrString("mode"), CupertinoTimerPickerMode.hms)!,
initialTimerDuration: initialTimerDuration,
minuteInterval: minuteInterval,
secondInterval: secondInterval,
minuteInterval: widget.control.attrInt("minuteInterval", 1)!,
secondInterval: widget.control.attrInt("secondInterval", 1)!,
itemExtent: widget.control.attrDouble("itemExtent", 32.0)!,
alignment: parseAlignment(widget.control, "alignment", Alignment.center)!,
backgroundColor: backgroundColor,
backgroundColor: widget.control.attrColor("bgColor", context),
onTimerDurationChanged: (Duration d) {
widget.backend.updateControlState(
widget.control.id, {"value": d.inSeconds.toString()});
Expand Down
44 changes: 16 additions & 28 deletions packages/flet/lib/src/controls/date_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,7 @@ class _DatePickerControlState extends State<DatePickerControl> {

var open = widget.control.attrBool("open", false)!;
DateTime? value = widget.control.attrDateTime("value");
DateTime? firstDate = widget.control.attrDateTime("firstDate");
DateTime? lastDate = widget.control.attrDateTime("lastDate");
DateTime? currentDate = widget.control.attrDateTime("currentDate");
String? helpText = widget.control.attrString("helpText");
String? cancelText = widget.control.attrString("cancelText");
String? confirmText = widget.control.attrString("confirmText");
String? errorFormatText = widget.control.attrString("errorFormatText");
String? errorInvalidText = widget.control.attrString("errorInvalidText");
TextInputType keyboardType = parseTextInputType(
widget.control.attrString("keyboardType"), TextInputType.text)!;
DatePickerMode datePickerMode = parseDatePickerMode(
widget.control.attrString("datePickerMode"), DatePickerMode.day)!;
DatePickerEntryMode datePickerEntryMode = parseDatePickerEntryMode(
widget.control.attrString("datePickerEntryMode"),
DatePickerEntryMode.calendar)!;
String? fieldHintText = widget.control.attrString("fieldHintText");
String? fieldLabelText = widget.control.attrString("fieldLabelText");
IconData? switchToCalendarEntryModeIcon = parseIcon(
widget.control.attrString("switchToCalendarEntryModeIcon", "")!);
IconData? switchToInputEntryModeIcon =
Expand All @@ -75,19 +59,23 @@ class _DatePickerControlState extends State<DatePickerControl> {
Widget createSelectDateDialog() {
Widget dialog = DatePickerDialog(
initialDate: value ?? currentDate ?? DateTime.now(),
firstDate: firstDate ?? DateTime(1900),
lastDate: lastDate ?? DateTime(2050),
firstDate: widget.control.attrDateTime("firstDate", DateTime(1900))!,
lastDate: widget.control.attrDateTime("lastDate", DateTime(2050))!,
currentDate: currentDate ?? DateTime.now(),
helpText: helpText,
cancelText: cancelText,
confirmText: confirmText,
errorFormatText: errorFormatText,
errorInvalidText: errorInvalidText,
keyboardType: keyboardType,
initialCalendarMode: datePickerMode,
initialEntryMode: datePickerEntryMode,
fieldHintText: fieldHintText,
fieldLabelText: fieldLabelText,
helpText: widget.control.attrString("helpText"),
cancelText: widget.control.attrString("cancelText"),
confirmText: widget.control.attrString("confirmText"),
errorFormatText: widget.control.attrString("errorFormatText"),
errorInvalidText: widget.control.attrString("errorInvalidText"),
keyboardType: parseTextInputType(
widget.control.attrString("keyboardType"), TextInputType.text)!,
initialCalendarMode: parseDatePickerMode(
widget.control.attrString("datePickerMode"), DatePickerMode.day)!,
initialEntryMode: parseDatePickerEntryMode(
widget.control.attrString("datePickerEntryMode"),
DatePickerEntryMode.calendar)!,
fieldHintText: widget.control.attrString("fieldHintText"),
fieldLabelText: widget.control.attrString("fieldLabelText"),
onDatePickerModeChange: (DatePickerEntryMode mode) {
widget.backend.triggerControlEvent(
widget.control.id, "entryModeChange", mode.name);
Expand Down
23 changes: 13 additions & 10 deletions packages/flet/lib/src/controls/draggable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flutter/material.dart';

import '../flet_control_backend.dart';
import '../models/control.dart';
import '../utils/others.dart';
import 'create_control.dart';
import 'error.dart';

Expand All @@ -27,6 +28,7 @@ class DraggableControl extends StatelessWidget {
@override
Widget build(BuildContext context) {
debugPrint("DragTarget build: ${control.id}");
var adaptive = control.isAdaptive ?? parentAdaptive;

var group = control.attrString("group", "");
var contentCtrls =
Expand All @@ -39,18 +41,13 @@ class DraggableControl extends StatelessWidget {

Widget? child = contentCtrls.isNotEmpty
? createControl(control, contentCtrls.first.id, disabled,
parentAdaptive: parentAdaptive)
: null;

Widget? childWhenDragging = contentWhenDraggingCtrls.isNotEmpty
? createControl(control, contentWhenDraggingCtrls.first.id, disabled,
parentAdaptive: parentAdaptive)
parentAdaptive: adaptive)
: null;

Widget? childFeedback = contentFeedbackCtrls.isNotEmpty
? createControl(control, contentFeedbackCtrls.first.id, disabled,
parentAdaptive: parentAdaptive)
: null;
parentAdaptive: adaptive)
: Opacity(opacity: 0.5, child: child);

if (child == null) {
return const ErrorControl(
Expand All @@ -61,10 +58,16 @@ class DraggableControl extends StatelessWidget {

return Draggable<String>(
data: data,
childWhenDragging: childWhenDragging,
axis: parseAxis(control.attrString("axis")),
affinity: parseAxis(control.attrString("affinity")),
maxSimultaneousDrags: control.attrInt("maxSimultaneousDrags"),
childWhenDragging: contentWhenDraggingCtrls.isNotEmpty
? createControl(control, contentWhenDraggingCtrls.first.id, disabled,
parentAdaptive: adaptive)
: null,
feedback: MouseRegion(
cursor: SystemMouseCursors.grabbing,
child: childFeedback ?? Opacity(opacity: 0.5, child: child),
child: childFeedback,
),
onDragStarted: () {
debugPrint("Draggable.onDragStarted ${control.id}");
Expand Down
31 changes: 22 additions & 9 deletions packages/flet/lib/src/controls/dropdown.dart
Original file line number Diff line number Diff line change
Expand Up @@ -147,16 +147,22 @@ class _DropdownControlState extends State<DropdownControl> with FletStoreMixin {

var prefixControls = itemsView.controlViews
.where((c) => c.control.name == "prefix" && c.control.isVisible);
var prefixIconControls =
widget.children.where((c) => c.name == "prefixIcon" && c.isVisible);
var prefixIconControls = itemsView.controlViews
.where((c) => c.control.name == "prefix_icon" && c.control.isVisible);
var suffixControls = itemsView.controlViews
.where((c) => c.control.name == "suffix" && c.control.isVisible);
var suffixIconControls =
widget.children.where((c) => c.name == "suffixIcon" && c.isVisible);
var iconControls =
widget.children.where((c) => c.name == "icon" && c.isVisible);
var suffixIconControls = itemsView.controlViews
.where((c) => c.control.name == "suffix_icon" && c.control.isVisible);
var counterControls = itemsView.controlViews
.where((c) => c.control.name == "counter" && c.control.isVisible);
var iconControls = itemsView.controlViews
.where((c) => c.control.name == "icon" && c.control.isVisible);
var errorCtrl = itemsView.controlViews
.where((c) => c.control.name == "error" && c.control.isVisible);
var helperCtrl = itemsView.controlViews
.where((c) => c.control.name == "helper" && c.control.isVisible);
var labelCtrl = itemsView.controlViews
.where((c) => c.control.name == "label" && c.control.isVisible);

var focusValue = widget.control.attrString("focus");
if (focusValue != null && focusValue != _lastFocusValue) {
Expand Down Expand Up @@ -195,14 +201,21 @@ class _DropdownControlState extends State<DropdownControl> with FletStoreMixin {
decoration: buildInputDecoration(context, widget.control,
prefix:
prefixControls.isNotEmpty ? prefixControls.first.control : null,
prefixIcon: prefixIconControls.isNotEmpty ? prefixIconControls.first : null,
prefixIcon: prefixIconControls.isNotEmpty
? prefixIconControls.first.control
: null,
suffix:
suffixControls.isNotEmpty ? suffixControls.first.control : null,
suffixIcon: suffixIconControls.isNotEmpty ? suffixIconControls.first : null,
icon: iconControls.isNotEmpty ? iconControls.first : null,
suffixIcon: suffixIconControls.isNotEmpty
? suffixIconControls.first.control
: null,
counter: counterControls.isNotEmpty
? counterControls.first.control
: null,
icon: iconControls.isNotEmpty ? iconControls.first.control : null,
error: errorCtrl.isNotEmpty ? errorCtrl.first.control : null,
helper: helperCtrl.isNotEmpty ? helperCtrl.first.control : null,
label: labelCtrl.isNotEmpty ? labelCtrl.first.control : null,
customSuffix: null,
focused: _focused,
disabled: disabled,
Expand Down
Loading