Skip to content

Commit 7810f71

Browse files
feat: expose more properties in Controls (#4105)
* nav bar/drawer/rail * TooltipDirection enum has wrong values * Text and TextSpan * GestureDetector * deprecate MapPointerDeviceType in favor of PointerDeviceType * create BoxConstraints dataclass * update utils * fix navbar appearance when disabled * PopupMenu new props * move AnimationValue from types.py to animation.py * update * Tab: icon_content, height, icon_margin * New props: TextField, Dropdown, CupertinoTextField * FormField: make suffix_icon and prefix_icon support both string and Control types * refactor ControlState props * properly disable navbar * fix failing CI: NoneType is not available in py3.8 * fix failing CI: update expected test value * fix failing CI: import ControlState * FormField: add `collapsed` prop which fits field's content (#4106) * remove _set_control_state_attr_json; add wrap_attr_dict boolean prop to _set_attr_json * FormField: fit_parent_size prop * Define __post_init__ method for Dataclasses making use of ControlState * remove Optional in AnimationValue * `FormField.label` as a Control * Fix wrapping ListTile to Material widget (do not wrap if it's cupertino) --------- Co-authored-by: Feodor Fitsner <[email protected]>
1 parent 0310503 commit 7810f71

File tree

135 files changed

+2605
-1550
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

135 files changed

+2605
-1550
lines changed

packages/flet/lib/src/controls/create_control.dart

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ Widget createWidget(
257257
children: controlView.children,
258258
control: controlView.control,
259259
parentDisabled: parentDisabled,
260+
parentAdaptive: parentAdaptive,
260261
backend: backend);
261262
case "divider":
262263
return DividerControl(
@@ -1163,11 +1164,11 @@ Widget _scaledControl(
11631164

11641165
Widget _offsetControl(
11651166
BuildContext context, Widget widget, Control? parent, Control control) {
1166-
var offsetDetails = parseOffset(control, "offset");
1167+
var offset = parseOffset(control, "offset");
11671168
var animation = parseAnimation(control, "animateOffset");
1168-
if (offsetDetails != null && animation != null) {
1169+
if (offset != null && animation != null) {
11691170
return AnimatedSlide(
1170-
offset: Offset(offsetDetails.x, offsetDetails.y),
1171+
offset: offset,
11711172
duration: animation.duration,
11721173
curve: animation.curve,
11731174
onEnd: control.attrBool("onAnimationEnd", false)!
@@ -1178,9 +1179,8 @@ Widget _offsetControl(
11781179
}
11791180
: null,
11801181
child: widget);
1181-
} else if (offsetDetails != null) {
1182-
return FractionalTranslation(
1183-
translation: Offset(offsetDetails.x, offsetDetails.y), child: widget);
1182+
} else if (offset != null) {
1183+
return FractionalTranslation(translation: offset, child: widget);
11841184
}
11851185
return widget;
11861186
}

packages/flet/lib/src/controls/cupertino_textfield.dart

Lines changed: 40 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import '../utils/edge_insets.dart';
1111
import '../utils/form_field.dart';
1212
import '../utils/gradient.dart';
1313
import '../utils/images.dart';
14+
import '../utils/others.dart';
15+
import '../utils/overlay_style.dart';
1416
import '../utils/text.dart';
1517
import '../utils/textfield.dart';
1618
import 'create_control.dart';
@@ -159,22 +161,14 @@ class _CupertinoTextFieldControlState extends State<CupertinoTextFieldControl>
159161
inputFormatters.add(TextCapitalizationFormatter(textCapitalization));
160162
}
161163

162-
TextInputType keyboardType = multiline
163-
? TextInputType.multiline
164-
: parseTextInputType(
165-
widget.control.attrString("keyboardType"), TextInputType.text)!;
166-
167164
TextAlign textAlign = parseTextAlign(
168165
widget.control.attrString("textAlign"), TextAlign.start)!;
169166

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

172169
bool rtl = widget.control.attrBool("rtl", false)!;
173170
bool autocorrect = widget.control.attrBool("autocorrect", true)!;
174-
bool enableSuggestions =
175-
widget.control.attrBool("enableSuggestions", true)!;
176-
bool smartDashesType = widget.control.attrBool("smartDashesType", true)!;
177-
bool smartQuotesType = widget.control.attrBool("smartQuotesType", true)!;
171+
;
178172

179173
FocusNode focusNode = shiftEnter ? _shiftEnterfocusNode : _focusNode;
180174

@@ -228,29 +222,25 @@ class _CupertinoTextFieldControlState extends State<CupertinoTextFieldControl>
228222
}),
229223
);
230224
}
231-
225+
var fitParentSize = widget.control.attrBool("fitParentSize", false)!;
232226
BoxDecoration? defaultDecoration = const CupertinoTextField().decoration;
233227
var gradient = parseGradient(Theme.of(context), widget.control, "gradient");
234228
var blendMode = parseBlendMode(widget.control.attrString("blendMode"));
235229

236230
var bgColor = widget.control.attrColor("bgColor", context);
237-
// for adaptive TextField use label for placeholder
238-
var placeholder = widget.control.attrString("placeholderText") ??
239-
widget.control.attrString("label");
240-
// for adaptive TextField use labelStyle for placeholderStyle
241-
var placeholderStyle =
242-
parseTextStyle(Theme.of(context), widget.control, "placeholderStyle") ??
243-
parseTextStyle(Theme.of(context), widget.control, "labelStyle");
231+
244232
return withPageArgs((context, pageArgs) {
245-
var decorationImage = parseDecorationImage(
246-
Theme.of(context), widget.control, "image", pageArgs);
247233
Widget textField = CupertinoTextField(
248234
style: textStyle,
249235
textAlignVertical: textVerticalAlign != null
250236
? TextAlignVertical(y: textVerticalAlign)
251237
: null,
252-
placeholder: placeholder,
253-
placeholderStyle: placeholderStyle,
238+
placeholder: widget.control.attrString("placeholderText") ??
239+
widget.control.attrString("label"),
240+
// use label for adaptive TextField
241+
placeholderStyle: parseTextStyle(Theme.of(context), widget.control, "placeholderStyle") ??
242+
parseTextStyle(Theme.of(context), widget.control, "labelStyle"),
243+
// labelStyle for adaptive TextField
254244
autofocus: autofocus,
255245
enabled: !disabled,
256246
onSubmitted: !multiline
@@ -262,7 +252,8 @@ class _CupertinoTextFieldControlState extends State<CupertinoTextFieldControl>
262252
decoration: defaultDecoration?.copyWith(
263253
color: bgColor,
264254
gradient: gradient,
265-
image: decorationImage,
255+
image: parseDecorationImage(
256+
Theme.of(context), widget.control, "image", pageArgs),
266257
backgroundBlendMode:
267258
bgColor != null || gradient != null ? blendMode : null,
268259
border: border,
@@ -274,47 +265,54 @@ class _CupertinoTextFieldControlState extends State<CupertinoTextFieldControl>
274265
cursorWidth: widget.control.attrDouble("cursorWidth", 2.0)!,
275266
cursorRadius: parseRadius(
276267
widget.control, "cursorRadius", const Radius.circular(2.0))!,
277-
keyboardType: keyboardType,
268+
keyboardType: multiline
269+
? TextInputType.multiline
270+
: parseTextInputType(widget.control.attrString("keyboardType"),
271+
TextInputType.text)!,
278272
clearButtonSemanticLabel:
279273
widget.control.attrString("clearButtonSemanticsLabel"),
280274
autocorrect: autocorrect,
281-
enableSuggestions: enableSuggestions,
282-
smartDashesType: smartDashesType
275+
enableSuggestions:
276+
widget.control.attrBool("enableSuggestions", true)!,
277+
smartDashesType: widget.control.attrBool("smartDashesType", true)!
283278
? SmartDashesType.enabled
284279
: SmartDashesType.disabled,
285-
smartQuotesType: smartQuotesType
280+
smartQuotesType: widget.control.attrBool("smartQuotesType", true)!
286281
? SmartQuotesType.enabled
287282
: SmartQuotesType.disabled,
288283
suffixMode: parseVisibilityMode(
289-
widget.control.attrString("suffixVisibilityMode", "")!),
284+
widget.control.attrString("suffixVisibilityMode"),
285+
OverlayVisibilityMode.always)!,
290286
prefixMode: parseVisibilityMode(
291-
widget.control.attrString("prefixVisibilityMode", "")!),
287+
widget.control.attrString("prefixVisibilityMode"),
288+
OverlayVisibilityMode.always)!,
292289
textAlign: textAlign,
293-
minLines: minLines,
294-
maxLines: maxLines,
290+
minLines: fitParentSize ? null : minLines,
291+
maxLines: fitParentSize ? null : maxLines,
295292
maxLength: maxLength,
296293
prefix: prefixControls.isNotEmpty
297294
? createControl(widget.control, prefixControls.first.id, disabled,
298295
parentAdaptive: widget.parentAdaptive)
299296
: null,
300-
suffix: revealPasswordIcon ??
301-
(suffixControls.isNotEmpty
302-
? createControl(
303-
widget.control, suffixControls.first.id, disabled,
304-
parentAdaptive: widget.parentAdaptive)
305-
: null),
297+
suffix: revealPasswordIcon ?? (suffixControls.isNotEmpty ? createControl(widget.control, suffixControls.first.id, disabled, parentAdaptive: widget.parentAdaptive) : null),
306298
readOnly: readOnly,
307299
textDirection: rtl ? TextDirection.rtl : null,
308300
inputFormatters: inputFormatters.isNotEmpty ? inputFormatters : null,
309301
obscureText: password && !_revealPassword,
310-
padding: parseEdgeInsets(
311-
widget.control, "padding", const EdgeInsets.all(7.0))!,
302+
padding: parseEdgeInsets(widget.control, "padding", const EdgeInsets.all(7.0))!,
312303
scribbleEnabled: widget.control.attrBool("enableScribble", true)!,
304+
scrollPadding: parseEdgeInsets(widget.control, "scrollPadding", const EdgeInsets.all(20.0))!,
305+
obscuringCharacter: widget.control.attrString("obscuringCharacter", '•')!,
306+
cursorOpacityAnimates: widget.control.attrBool("animateCursorOpacity", Theme.of(context).platform == TargetPlatform.iOS)!,
307+
expands: fitParentSize,
308+
enableIMEPersonalizedLearning: widget.control.attrBool("enableIMEPersonalizedLearning", true)!,
309+
clipBehavior: parseClip(widget.control.attrString("clipBehavior"), Clip.hardEdge)!,
310+
cursorColor: cursorColor,
313311
autofillHints: parseAutofillHints(widget.control, "autofillHints"),
314-
scrollPadding: parseEdgeInsets(
315-
widget.control, "scrollPadding", const EdgeInsets.all(20.0))!,
316-
obscuringCharacter:
317-
widget.control.attrString("obscuringCharacter", '•')!,
312+
keyboardAppearance: parseBrightness(widget.control.attrString("keyboardBrightness")),
313+
enableInteractiveSelection: widget.control.attrBool("enableInteractiveSelection"),
314+
clearButtonMode: parseVisibilityMode(widget.control.attrString("clearButtonVisibilityMode"), OverlayVisibilityMode.never)!,
315+
strutStyle: parseStrutStyle(widget.control, "strutStyle"),
318316
onTap: () {
319317
widget.backend.triggerControlEvent(widget.control.id, "click");
320318
},

packages/flet/lib/src/controls/cupertino_timer_picker.dart

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,23 +33,16 @@ class _CupertinoTimerPickerControlState
3333

3434
int value = widget.control.attrInt("value", 0)!;
3535
Duration initialTimerDuration = Duration(seconds: value);
36-
int minuteInterval =
37-
widget.control.attrDouble("minuteInterval", 1)!.toInt();
38-
int secondInterval =
39-
widget.control.attrDouble("secondInterval", 1)!.toInt();
40-
CupertinoTimerPickerMode mode = parseCupertinoTimerPickerMode(
41-
widget.control.attrString("mode"), CupertinoTimerPickerMode.hms)!;
42-
43-
Color? backgroundColor = widget.control.attrColor("bgColor", context);
4436

4537
Widget picker = CupertinoTimerPicker(
46-
mode: mode,
38+
mode: parseCupertinoTimerPickerMode(
39+
widget.control.attrString("mode"), CupertinoTimerPickerMode.hms)!,
4740
initialTimerDuration: initialTimerDuration,
48-
minuteInterval: minuteInterval,
49-
secondInterval: secondInterval,
41+
minuteInterval: widget.control.attrInt("minuteInterval", 1)!,
42+
secondInterval: widget.control.attrInt("secondInterval", 1)!,
5043
itemExtent: widget.control.attrDouble("itemExtent", 32.0)!,
5144
alignment: parseAlignment(widget.control, "alignment", Alignment.center)!,
52-
backgroundColor: backgroundColor,
45+
backgroundColor: widget.control.attrColor("bgColor", context),
5346
onTimerDurationChanged: (Duration d) {
5447
widget.backend.updateControlState(
5548
widget.control.id, {"value": d.inSeconds.toString()});

packages/flet/lib/src/controls/date_picker.dart

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -32,23 +32,7 @@ class _DatePickerControlState extends State<DatePickerControl> {
3232

3333
var open = widget.control.attrBool("open", false)!;
3434
DateTime? value = widget.control.attrDateTime("value");
35-
DateTime? firstDate = widget.control.attrDateTime("firstDate");
36-
DateTime? lastDate = widget.control.attrDateTime("lastDate");
3735
DateTime? currentDate = widget.control.attrDateTime("currentDate");
38-
String? helpText = widget.control.attrString("helpText");
39-
String? cancelText = widget.control.attrString("cancelText");
40-
String? confirmText = widget.control.attrString("confirmText");
41-
String? errorFormatText = widget.control.attrString("errorFormatText");
42-
String? errorInvalidText = widget.control.attrString("errorInvalidText");
43-
TextInputType keyboardType = parseTextInputType(
44-
widget.control.attrString("keyboardType"), TextInputType.text)!;
45-
DatePickerMode datePickerMode = parseDatePickerMode(
46-
widget.control.attrString("datePickerMode"), DatePickerMode.day)!;
47-
DatePickerEntryMode datePickerEntryMode = parseDatePickerEntryMode(
48-
widget.control.attrString("datePickerEntryMode"),
49-
DatePickerEntryMode.calendar)!;
50-
String? fieldHintText = widget.control.attrString("fieldHintText");
51-
String? fieldLabelText = widget.control.attrString("fieldLabelText");
5236
IconData? switchToCalendarEntryModeIcon = parseIcon(
5337
widget.control.attrString("switchToCalendarEntryModeIcon", "")!);
5438
IconData? switchToInputEntryModeIcon =
@@ -75,19 +59,23 @@ class _DatePickerControlState extends State<DatePickerControl> {
7559
Widget createSelectDateDialog() {
7660
Widget dialog = DatePickerDialog(
7761
initialDate: value ?? currentDate ?? DateTime.now(),
78-
firstDate: firstDate ?? DateTime(1900),
79-
lastDate: lastDate ?? DateTime(2050),
62+
firstDate: widget.control.attrDateTime("firstDate", DateTime(1900))!,
63+
lastDate: widget.control.attrDateTime("lastDate", DateTime(2050))!,
8064
currentDate: currentDate ?? DateTime.now(),
81-
helpText: helpText,
82-
cancelText: cancelText,
83-
confirmText: confirmText,
84-
errorFormatText: errorFormatText,
85-
errorInvalidText: errorInvalidText,
86-
keyboardType: keyboardType,
87-
initialCalendarMode: datePickerMode,
88-
initialEntryMode: datePickerEntryMode,
89-
fieldHintText: fieldHintText,
90-
fieldLabelText: fieldLabelText,
65+
helpText: widget.control.attrString("helpText"),
66+
cancelText: widget.control.attrString("cancelText"),
67+
confirmText: widget.control.attrString("confirmText"),
68+
errorFormatText: widget.control.attrString("errorFormatText"),
69+
errorInvalidText: widget.control.attrString("errorInvalidText"),
70+
keyboardType: parseTextInputType(
71+
widget.control.attrString("keyboardType"), TextInputType.text)!,
72+
initialCalendarMode: parseDatePickerMode(
73+
widget.control.attrString("datePickerMode"), DatePickerMode.day)!,
74+
initialEntryMode: parseDatePickerEntryMode(
75+
widget.control.attrString("datePickerEntryMode"),
76+
DatePickerEntryMode.calendar)!,
77+
fieldHintText: widget.control.attrString("fieldHintText"),
78+
fieldLabelText: widget.control.attrString("fieldLabelText"),
9179
onDatePickerModeChange: (DatePickerEntryMode mode) {
9280
widget.backend.triggerControlEvent(
9381
widget.control.id, "entryModeChange", mode.name);

packages/flet/lib/src/controls/draggable.dart

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
44

55
import '../flet_control_backend.dart';
66
import '../models/control.dart';
7+
import '../utils/others.dart';
78
import 'create_control.dart';
89
import 'error.dart';
910

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

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

4042
Widget? child = contentCtrls.isNotEmpty
4143
? createControl(control, contentCtrls.first.id, disabled,
42-
parentAdaptive: parentAdaptive)
43-
: null;
44-
45-
Widget? childWhenDragging = contentWhenDraggingCtrls.isNotEmpty
46-
? createControl(control, contentWhenDraggingCtrls.first.id, disabled,
47-
parentAdaptive: parentAdaptive)
44+
parentAdaptive: adaptive)
4845
: null;
4946

5047
Widget? childFeedback = contentFeedbackCtrls.isNotEmpty
5148
? createControl(control, contentFeedbackCtrls.first.id, disabled,
52-
parentAdaptive: parentAdaptive)
53-
: null;
49+
parentAdaptive: adaptive)
50+
: Opacity(opacity: 0.5, child: child);
5451

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

6259
return Draggable<String>(
6360
data: data,
64-
childWhenDragging: childWhenDragging,
61+
axis: parseAxis(control.attrString("axis")),
62+
affinity: parseAxis(control.attrString("affinity")),
63+
maxSimultaneousDrags: control.attrInt("maxSimultaneousDrags"),
64+
childWhenDragging: contentWhenDraggingCtrls.isNotEmpty
65+
? createControl(control, contentWhenDraggingCtrls.first.id, disabled,
66+
parentAdaptive: adaptive)
67+
: null,
6568
feedback: MouseRegion(
6669
cursor: SystemMouseCursors.grabbing,
67-
child: childFeedback ?? Opacity(opacity: 0.5, child: child),
70+
child: childFeedback,
6871
),
6972
onDragStarted: () {
7073
debugPrint("Draggable.onDragStarted ${control.id}");

packages/flet/lib/src/controls/dropdown.dart

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -147,16 +147,22 @@ class _DropdownControlState extends State<DropdownControl> with FletStoreMixin {
147147

148148
var prefixControls = itemsView.controlViews
149149
.where((c) => c.control.name == "prefix" && c.control.isVisible);
150-
var prefixIconControls =
151-
widget.children.where((c) => c.name == "prefixIcon" && c.isVisible);
150+
var prefixIconControls = itemsView.controlViews
151+
.where((c) => c.control.name == "prefix_icon" && c.control.isVisible);
152152
var suffixControls = itemsView.controlViews
153153
.where((c) => c.control.name == "suffix" && c.control.isVisible);
154-
var suffixIconControls =
155-
widget.children.where((c) => c.name == "suffixIcon" && c.isVisible);
156-
var iconControls =
157-
widget.children.where((c) => c.name == "icon" && c.isVisible);
154+
var suffixIconControls = itemsView.controlViews
155+
.where((c) => c.control.name == "suffix_icon" && c.control.isVisible);
158156
var counterControls = itemsView.controlViews
159157
.where((c) => c.control.name == "counter" && c.control.isVisible);
158+
var iconControls = itemsView.controlViews
159+
.where((c) => c.control.name == "icon" && c.control.isVisible);
160+
var errorCtrl = itemsView.controlViews
161+
.where((c) => c.control.name == "error" && c.control.isVisible);
162+
var helperCtrl = itemsView.controlViews
163+
.where((c) => c.control.name == "helper" && c.control.isVisible);
164+
var labelCtrl = itemsView.controlViews
165+
.where((c) => c.control.name == "label" && c.control.isVisible);
160166

161167
var focusValue = widget.control.attrString("focus");
162168
if (focusValue != null && focusValue != _lastFocusValue) {
@@ -195,14 +201,21 @@ class _DropdownControlState extends State<DropdownControl> with FletStoreMixin {
195201
decoration: buildInputDecoration(context, widget.control,
196202
prefix:
197203
prefixControls.isNotEmpty ? prefixControls.first.control : null,
198-
prefixIcon: prefixIconControls.isNotEmpty ? prefixIconControls.first : null,
204+
prefixIcon: prefixIconControls.isNotEmpty
205+
? prefixIconControls.first.control
206+
: null,
199207
suffix:
200208
suffixControls.isNotEmpty ? suffixControls.first.control : null,
201-
suffixIcon: suffixIconControls.isNotEmpty ? suffixIconControls.first : null,
202-
icon: iconControls.isNotEmpty ? iconControls.first : null,
209+
suffixIcon: suffixIconControls.isNotEmpty
210+
? suffixIconControls.first.control
211+
: null,
203212
counter: counterControls.isNotEmpty
204213
? counterControls.first.control
205214
: null,
215+
icon: iconControls.isNotEmpty ? iconControls.first.control : null,
216+
error: errorCtrl.isNotEmpty ? errorCtrl.first.control : null,
217+
helper: helperCtrl.isNotEmpty ? helperCtrl.first.control : null,
218+
label: labelCtrl.isNotEmpty ? labelCtrl.first.control : null,
206219
customSuffix: null,
207220
focused: _focused,
208221
disabled: disabled,

0 commit comments

Comments
 (0)