Skip to content

Commit cf980fc

Browse files
committed
Add layout utils and improve expand handling
Introduces a new layout utility for parsing 'expand' properties and refactors controls to use it for consistent expand logic. Adds error handling for unbounded horizontal GridView and ListView, updates related integration tests, and includes new golden images for horizontal views. Fix #5555
1 parent 826cc5f commit cf980fc

File tree

15 files changed

+159
-42
lines changed

15 files changed

+159
-42
lines changed

packages/flet/lib/flet.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export 'src/utils/gradient.dart';
3939
export 'src/utils/icons.dart';
4040
export 'src/utils/images.dart';
4141
export 'src/utils/launch_url.dart';
42+
export 'src/utils/layout.dart';
4243
export 'src/utils/locale.dart';
4344
export 'src/utils/lock.dart';
4445
export 'src/utils/markdown.dart';

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import '../extensions/control.dart';
66
import '../models/control.dart';
77
import '../utils/animations.dart';
88
import '../utils/badge.dart';
9+
import '../utils/layout.dart';
910
import '../utils/numbers.dart';
1011
import '../utils/tooltip.dart';
1112
import '../utils/transforms.dart';
@@ -81,11 +82,7 @@ Widget _directionality(Widget widget, Control control) {
8182
}
8283

8384
Widget _expandable(Widget widget, Control control) {
84-
int? expand = control.get("expand") == true
85-
? 1
86-
: control.get("expand") == false
87-
? 0
88-
: control.getInt("expand");
85+
int? expand = control.getExpand("expand");
8986
if (expand != null && control.parent?.internals?["host_expanded"] == true) {
9087
return (control.getBool("expand_loose") == true)
9188
? Flexible(flex: expand, child: widget)

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

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,26 @@
1-
import 'package:flet/flet.dart';
21
import 'package:flutter/cupertino.dart';
32
import 'package:flutter/material.dart';
43
import 'package:flutter/services.dart';
54

5+
import '../extensions/control.dart';
6+
import '../models/control.dart';
7+
import '../utils/autofill.dart';
8+
import '../utils/borders.dart';
9+
import '../utils/box.dart';
10+
import '../utils/colors.dart';
11+
import '../utils/edge_insets.dart';
12+
import '../utils/form_field.dart';
13+
import '../utils/gradient.dart';
14+
import '../utils/images.dart';
15+
import '../utils/layout.dart';
16+
import '../utils/misc.dart';
17+
import '../utils/numbers.dart';
18+
import '../utils/platform.dart';
19+
import '../utils/text.dart';
20+
import '../utils/textfield.dart';
21+
import '../utils/theme.dart';
22+
import 'base_controls.dart';
23+
624
class CupertinoTextFieldControl extends StatefulWidget {
725
final Control control;
826

@@ -295,9 +313,7 @@ class _CupertinoTextFieldControlState extends State<CupertinoTextFieldControl> {
295313
child: textField);
296314
}
297315

298-
if (widget.control.get("expand") == true ||
299-
(widget.control.get("expand") is int &&
300-
widget.control.getInt("expand", 0)! > 0)) {
316+
if (widget.control.getExpand("expand", 0)! > 0) {
301317
return ConstrainedControl(control: widget.control, child: textField);
302318
} else {
303319
double? width = widget.control.getDouble("width");

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import '../utils/borders.dart';
77
import '../utils/colors.dart';
88
import '../utils/edge_insets.dart';
99
import '../utils/form_field.dart';
10+
import '../utils/layout.dart';
1011
import '../utils/numbers.dart';
1112
import '../utils/text.dart';
1213
import 'base_controls.dart';
@@ -146,7 +147,7 @@ class _DropdownM2ControlState extends State<DropdownM2Control> {
146147
items: items,
147148
);
148149

149-
if (widget.control.getInt("expand", 0)! > 0) {
150+
if (widget.control.getExpand("expand", 0)! > 0) {
150151
return ConstrainedControl(control: widget.control, child: dropDown);
151152
} else {
152153
return LayoutBuilder(

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import '../controls/control_widget.dart';
44
import '../extensions/control.dart';
55
import '../models/control.dart';
66
import '../utils/edge_insets.dart';
7+
import '../utils/layout.dart';
78
import '../utils/misc.dart';
89
import '../utils/numbers.dart';
10+
import '../widgets/error.dart';
911
import 'base_controls.dart';
1012
import 'scroll_notification_control.dart';
1113
import 'scrollable_control.dart';
@@ -106,6 +108,17 @@ class _GridViewControlState extends State<GridViewControl> {
106108
},
107109
);
108110

111+
if (horizontal &&
112+
constraints.maxHeight == double.infinity &&
113+
widget.control.getDouble("height") == null &&
114+
widget.control.getExpand("expand", 0)! <= 0) {
115+
return const ErrorControl(
116+
"Error displaying GridViewControl: height is unbounded.",
117+
description:
118+
"Set a fixed height, a non-zero expand, or place inside "
119+
"a control with bounded height.");
120+
}
121+
109122
child = ScrollableControl(
110123
control: widget.control,
111124
scrollDirection: horizontal ? Axis.horizontal : Axis.vertical,

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import 'package:flet/src/utils/layout.dart';
12
import 'package:flutter/material.dart';
23

34
import '../extensions/control.dart';
45
import '../models/control.dart';
56
import '../utils/edge_insets.dart';
67
import '../utils/misc.dart';
78
import '../utils/numbers.dart';
9+
import '../widgets/error.dart';
810
import 'base_controls.dart';
911
import 'scroll_notification_control.dart';
1012
import 'scrollable_control.dart';
@@ -121,6 +123,17 @@ class _ListViewControlState extends State<ListViewControl> {
121123
prototypeItem: prototypeItem,
122124
);
123125

126+
if (horizontal &&
127+
constraints.maxHeight == double.infinity &&
128+
widget.control.getDouble("height") == null &&
129+
widget.control.getExpand("expand", 0)! <= 0) {
130+
return const ErrorControl(
131+
"Error displaying ListViewControl: height is unbounded.",
132+
description:
133+
"Set a fixed height, a non-zero expand, or place inside "
134+
"a control with bounded height.");
135+
}
136+
124137
child = ScrollableControl(
125138
control: widget.control,
126139
scrollDirection: horizontal ? Axis.horizontal : Axis.vertical,

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,10 @@ class _NavigationRailControlState extends State<NavigationRailControl>
6060

6161
if (constraints.maxHeight == double.infinity &&
6262
widget.control.getDouble("height") == null) {
63-
return const ErrorControl("Error displaying NavigationRail",
63+
return const ErrorControl(
64+
"Error displaying NavigationRail: height is unbounded.",
6465
description:
65-
"Control's height is unbounded. Either set a fixed \"height\" or nest NavigationRail inside expanded control or control with a fixed height.");
66+
"Either set a fixed \"height\" or nest NavigationRail inside expanded control or control with a fixed height.");
6667
}
6768

6869
return NavigationRail(

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

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
1-
import 'package:flet/flet.dart';
21
import 'package:flutter/material.dart';
32
import 'package:flutter/services.dart';
43

4+
import '../models/control.dart';
5+
import '../utils/autofill.dart';
6+
import '../utils/borders.dart';
7+
import '../utils/colors.dart';
8+
import '../utils/edge_insets.dart';
9+
import '../utils/form_field.dart';
10+
import '../utils/layout.dart';
11+
import '../utils/misc.dart';
12+
import '../utils/mouse.dart';
13+
import '../utils/numbers.dart';
14+
import '../utils/platform.dart';
15+
import '../utils/text.dart';
16+
import '../utils/textfield.dart';
17+
import '../utils/theme.dart';
18+
import 'base_controls.dart';
19+
520
class TextFieldControl extends StatefulWidget {
621
final Control control;
722

@@ -255,9 +270,7 @@ class _TextFieldControlState extends State<TextFieldControl> {
255270
textField =
256271
isLinuxDesktop() ? ExcludeSemantics(child: textField) : textField;
257272

258-
if (widget.control.get("expand") == true ||
259-
(widget.control.get("expand") is int &&
260-
widget.control.getInt("expand", 0)! > 0)) {
273+
if (widget.control.getExpand("expand", 0)! > 0) {
261274
return ConstrainedControl(control: widget.control, child: textField);
262275
} else {
263276
double? width = widget.control.getDouble("width");
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import '../models/control.dart';
2+
import 'numbers.dart';
3+
4+
int? parseExpand(dynamic value, [int? defaultValue]) {
5+
return value == true
6+
? 1
7+
: value == false
8+
? 0
9+
: parseInt(value) ?? defaultValue;
10+
}
11+
12+
extension LayoutParsers on Control {
13+
int? getExpand(String propertyName, [int? defaultValue]) {
14+
return parseExpand(get(propertyName), defaultValue);
15+
}
16+
}
6.66 KB
Loading

0 commit comments

Comments
 (0)