Skip to content

Commit 9f88865

Browse files
committed
FAB, scrollable page
1 parent 04554f0 commit 9f88865

File tree

9 files changed

+246
-14
lines changed

9 files changed

+246
-14
lines changed

client/lib/controls/create_control.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import 'column.dart';
1212
import 'container.dart';
1313
import 'dropdown.dart';
1414
import 'elevated_button.dart';
15+
import 'floating_action_button.dart';
1516
import 'grid_view.dart';
1617
import 'icon.dart';
1718
import 'icon_button.dart';
@@ -93,6 +94,12 @@ Widget createControl(Control? parent, String id, bool parentDisabled) {
9394
control: controlView.control,
9495
children: controlView.children,
9596
parentDisabled: parentDisabled);
97+
case ControlType.floatingActionButton:
98+
return FloatingActionButtonControl(
99+
parent: parent,
100+
control: controlView.control,
101+
children: controlView.children,
102+
parentDisabled: parentDisabled);
96103
case ControlType.column:
97104
return ColumnControl(
98105
parent: parent,
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import 'package:flet_view/controls/error.dart';
2+
import 'package:flutter/material.dart';
3+
4+
import '../models/control.dart';
5+
import '../utils/colors.dart';
6+
import '../utils/icons.dart';
7+
import '../web_socket_client.dart';
8+
import 'create_control.dart';
9+
10+
class FloatingActionButtonControl extends StatelessWidget {
11+
final Control? parent;
12+
final Control control;
13+
final List<Control> children;
14+
final bool parentDisabled;
15+
16+
const FloatingActionButtonControl(
17+
{Key? key,
18+
this.parent,
19+
required this.control,
20+
required this.children,
21+
required this.parentDisabled})
22+
: super(key: key);
23+
24+
@override
25+
Widget build(BuildContext context) {
26+
debugPrint("FloatingActionButtonControl build: ${control.id}");
27+
28+
String? text = control.attrString("text");
29+
IconData? icon = getMaterialIcon(control.attrString("icon", "")!);
30+
Color? bgColor =
31+
HexColor.fromString(context, control.attrString("bgColor", "")!);
32+
var contentCtrls = children.where((c) => c.name == "content");
33+
bool disabled = control.isDisabled || parentDisabled;
34+
35+
Function()? onPressed = disabled
36+
? null
37+
: () {
38+
debugPrint("FloatingActionButtonControl ${control.id} clicked!");
39+
ws.pageEventFromWeb(
40+
eventTarget: control.id,
41+
eventName: "click",
42+
eventData: control.attrs["data"] ?? "");
43+
};
44+
45+
if (text == null && icon == null) {
46+
return const ErrorControl("FAB doesn't have a text, nor icon.");
47+
}
48+
49+
Widget button;
50+
if (contentCtrls.isNotEmpty) {
51+
button = FloatingActionButton(
52+
onPressed: onPressed,
53+
child: createControl(control, contentCtrls.first.id, disabled));
54+
} else if (icon != null && text == null) {
55+
button = FloatingActionButton(
56+
onPressed: onPressed,
57+
child: Icon(icon),
58+
backgroundColor: bgColor,
59+
);
60+
} else if (icon != null && text != null) {
61+
button = FloatingActionButton.extended(
62+
onPressed: onPressed,
63+
label: Text(text),
64+
icon: Icon(icon),
65+
backgroundColor: bgColor);
66+
} else {
67+
return const ErrorControl("FAB doesn't have a text, nor icon.");
68+
}
69+
70+
return constrainedControl(button, parent, control);
71+
}
72+
}

client/lib/controls/page.dart

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import '../utils/edge_insets.dart';
1212
import '../utils/theme.dart';
1313
import '../widgets/screen_size.dart';
1414
import 'create_control.dart';
15+
import 'scrollable_control.dart';
1516

1617
class PageControl extends StatelessWidget {
1718
final Control? parent;
@@ -34,6 +35,14 @@ class PageControl extends StatelessWidget {
3435
final crossAlignment = parseCrossAxisAlignment(
3536
control, "horizontalAlignment", CrossAxisAlignment.start);
3637

38+
ScrollMode scrollMode = ScrollMode.values.firstWhere(
39+
(m) =>
40+
m.name.toLowerCase() ==
41+
control.attrString("scroll", "")!.toLowerCase(),
42+
orElse: () => ScrollMode.none);
43+
44+
debugPrint("scrollMode: $scrollMode");
45+
3746
Control? offstage;
3847
List<Widget> controls = [];
3948
bool firstControl = true;
@@ -100,6 +109,11 @@ class PageControl extends StatelessWidget {
100109
.toList()
101110
: [];
102111

112+
var column = Column(
113+
mainAxisAlignment: mainAlignment,
114+
crossAxisAlignment: crossAlignment,
115+
children: controls);
116+
103117
return MaterialApp(
104118
title: title,
105119
theme: theme,
@@ -109,16 +123,18 @@ class PageControl extends StatelessWidget {
109123
body: Stack(children: [
110124
SizedBox.expand(
111125
child: Container(
112-
padding: parseEdgeInsets(control, "padding") ??
113-
const EdgeInsets.all(10),
114-
decoration: BoxDecoration(
115-
color: HexColor.fromString(
116-
context, control.attrString("bgcolor", "")!)),
117-
child: Column(
118-
mainAxisAlignment: mainAlignment,
119-
crossAxisAlignment: crossAlignment,
120-
children: controls),
121-
)),
126+
padding: parseEdgeInsets(control, "padding") ??
127+
const EdgeInsets.all(10),
128+
decoration: BoxDecoration(
129+
color: HexColor.fromString(
130+
context, control.attrString("bgcolor", "")!)),
131+
child: scrollMode != ScrollMode.none
132+
? ScrollableControl(
133+
child: column,
134+
scrollDirection: Axis.vertical,
135+
scrollMode: scrollMode,
136+
)
137+
: column)),
122138
...offstageWidgets,
123139
const ScreenSize()
124140
]),

client/lib/models/control_type.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ enum ControlType {
77
dropdown,
88
dropdownOption,
99
elevatedButton,
10+
floatingActionButton,
1011
gridView,
1112
icon,
1213
iconButton,

docs/roadmap.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
* [x] OutlinedButton
2727
* [x] TextButton
2828
* [x] IconButton
29+
* [x] FloatingActionButton
2930
* Input and selections
3031
* [x] TextField
3132
* [x] Dropdown
@@ -224,10 +225,10 @@
224225
<td></td>
225226
</tr>
226227
<tr>
227-
<td></td>
228+
<td></td>
228229
<td>FloatingActionButton</td>
229230
<td>-</td>
230-
<td></td>
231+
<td>S1</td>
231232
</tr>
232233
<tr><th colspan="4">Input and selections</th></tr>
233234
<tr>
@@ -714,6 +715,21 @@ Events:
714715

715716
- onPressed
716717

718+
## FloatingActionButton (FAB)
719+
720+
Docs: https://api.flutter.dev/flutter/material/FloatingActionButton-class.html
721+
722+
Properties:
723+
724+
- text
725+
- icon
726+
- bgColor
727+
- content - a Control representing custom button content
728+
729+
Events:
730+
731+
- click
732+
717733
## RadioGroup
718734

719735
Properties:

sdk/python/flet/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from flet.dropdown import Dropdown
88
from flet.elevated_button import ElevatedButton
99
from flet.flet import *
10+
from flet.floating_action_button import FloatingActionButton
1011
from flet.grid_view import GridView
1112
from flet.icon import Icon
1213
from flet.icon_button import IconButton
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
from typing import Optional
2+
3+
from beartype import beartype
4+
5+
from flet.constrained_control import ConstrainedControl
6+
from flet.control import Control, OptionalNumber
7+
from flet.ref import Ref
8+
9+
10+
class FloatingActionButton(ConstrainedControl):
11+
def __init__(
12+
self,
13+
text: str = None,
14+
ref: Ref = None,
15+
width: OptionalNumber = None,
16+
height: OptionalNumber = None,
17+
expand: int = None,
18+
opacity: OptionalNumber = None,
19+
visible: bool = None,
20+
disabled: bool = None,
21+
data: any = None,
22+
#
23+
# Specific
24+
#
25+
icon: str = None,
26+
bgcolor: str = None,
27+
content: Control = None,
28+
on_click=None,
29+
):
30+
ConstrainedControl.__init__(
31+
self,
32+
ref=ref,
33+
width=width,
34+
height=height,
35+
expand=expand,
36+
opacity=opacity,
37+
visible=visible,
38+
disabled=disabled,
39+
data=data,
40+
)
41+
42+
self.text = text
43+
self.icon = icon
44+
self.bgcolor = bgcolor
45+
self.content = content
46+
self.on_click = on_click
47+
48+
def _get_control_name(self):
49+
return "floatingactionbutton"
50+
51+
def _get_children(self):
52+
if self.__content == None:
53+
return []
54+
self.__content._set_attr_internal("n", "content")
55+
return [self.__content]
56+
57+
# text
58+
@property
59+
def text(self):
60+
return self._get_attr("text")
61+
62+
@text.setter
63+
def text(self, value):
64+
self._set_attr("text", value)
65+
66+
# icon
67+
@property
68+
def icon(self):
69+
return self._get_attr("icon")
70+
71+
@icon.setter
72+
def icon(self, value):
73+
self._set_attr("icon", value)
74+
75+
# bgcolor
76+
@property
77+
def bgcolor(self):
78+
return self._get_attr("bgcolor")
79+
80+
@bgcolor.setter
81+
def bgcolor(self, value):
82+
self._set_attr("bgcolor", value)
83+
84+
# on_click
85+
@property
86+
def on_click(self):
87+
return self._get_event_handler("click")
88+
89+
@on_click.setter
90+
def on_click(self, handler):
91+
self._add_event_handler("click", handler)
92+
93+
# content
94+
@property
95+
def content(self):
96+
return self.__content
97+
98+
@content.setter
99+
@beartype
100+
def content(self, value: Optional[Control]):
101+
self.__content = value

sdk/python/flet/page.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
MainAxisAlignment,
1616
OptionalNumber,
1717
PaddingValue,
18+
ScrollMode,
1819
)
1920
from flet.control_event import ControlEvent
2021
from flet.embed_json_encoder import EmbedJsonEncoder
@@ -395,6 +396,21 @@ def dark_theme(self, value: Optional[Theme]):
395396
self.__dark_theme.brightness = "dark"
396397
self._set_attr("darkTheme", json.dumps(value, default=vars) if value else None)
397398

399+
# scroll
400+
@property
401+
def scroll(self):
402+
return self.__scroll
403+
404+
@scroll.setter
405+
@beartype
406+
def scroll(self, value: ScrollMode):
407+
self.__scroll = value
408+
if value == True:
409+
value = "auto"
410+
elif value == False:
411+
value = "none"
412+
self._set_attr("scroll", value)
413+
398414
# window_width
399415
@property
400416
def window_width(self):

sdk/python/playground/todo.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
Checkbox,
66
Column,
77
ElevatedButton,
8+
FloatingActionButton,
89
IconButton,
910
OutlinedButton,
1011
Page,
@@ -92,12 +93,12 @@ def __init__(self):
9293
self.view = Column(
9394
width=600,
9495
controls=[
95-
Row([Text(value="Todos", size=30)], alignment="center"),
96+
Row([Text(value="Todos", style="headlineMedium")], alignment="center"),
9697
Row(
9798
# on_submit=self.add_clicked,
9899
controls=[
99100
self.new_task,
100-
ElevatedButton(text="Add", on_click=self.add_clicked),
101+
FloatingActionButton(icon=icons.ADD, on_click=self.add_clicked),
101102
],
102103
),
103104
Column(
@@ -159,6 +160,7 @@ def clear_clicked(self, e):
159160
def main(page: Page):
160161
page.title = "ToDo App"
161162
page.horizontal_alignment = "center"
163+
page.scroll = "adaptive"
162164
page.update()
163165
app = TodoApp()
164166
page.add(app.view)

0 commit comments

Comments
 (0)