Skip to content

Commit 2e970dd

Browse files
CupertinoDatePicker Control (#2795)
* CupertinoDatePicker: initial commit * DatePicker: reformat * CupertinoTimerPicker: cleanup * CupertinoDatePicker can be embedded in CupertinoBottomSheet * fix DateOrder * remove current_date + catch assertion errors * fix try catch * uniform action buttons * remove Container.on_tap * Remove unused imports --------- Co-authored-by: Feodor Fitsner <feodor@appveyor.com>
1 parent a777b41 commit 2e970dd

14 files changed

+464
-134
lines changed

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

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ class ContainerControl extends StatelessWidget with FletStoreMixin {
6666
children.where((c) => c.name == "content" && c.isVisible);
6767
bool ink = control.attrBool("ink", false)!;
6868
bool onClick = control.attrBool("onclick", false)!;
69-
bool onTap = control.attrBool("ontap", false)!;
7069
String url = control.attrString("url", "")!;
7170
String? urlTarget = control.attrString("urlTarget");
7271
bool onLongPress = control.attrBool("onLongPress", false)!;
@@ -146,7 +145,7 @@ class ContainerControl extends StatelessWidget with FletStoreMixin {
146145

147146
Widget? result;
148147

149-
if ((onTap || onClick || url != "" || onLongPress || onHover) &&
148+
if ((onClick || url != "" || onLongPress || onHover) &&
150149
ink &&
151150
!disabled) {
152151
var ink = Material(
@@ -156,12 +155,7 @@ class ContainerControl extends StatelessWidget with FletStoreMixin {
156155
// Dummy callback to enable widget
157156
// see https://github.com/flutter/flutter/issues/50116#issuecomment-582047374
158157
// and https://github.com/flutter/flutter/blob/eed80afe2c641fb14b82a22279d2d78c19661787/packages/flutter/lib/src/material/ink_well.dart#L1125-L1129
159-
onTap: onTap
160-
? () {
161-
debugPrint("Container ${control.id} Tap!");
162-
backend.triggerControlEvent(control.id, "tap", "");
163-
}
164-
: null,
158+
onTap: onHover ? () {} : null,
165159
onTapDown: onClick || url != ""
166160
? (details) {
167161
debugPrint("Container ${control.id} clicked!");
@@ -257,10 +251,10 @@ class ContainerControl extends StatelessWidget with FletStoreMixin {
257251
: null,
258252
child: child);
259253

260-
if ((onTap || onClick || onLongPress || onHover || url != "") &&
254+
if ((onClick || onLongPress || onHover || url != "") &&
261255
!disabled) {
262256
result = MouseRegion(
263-
cursor: onTap || onClick || url != ""
257+
cursor: onClick || url != ""
264258
? SystemMouseCursors.click
265259
: MouseCursor.defer,
266260
onEnter: onHover
@@ -278,12 +272,6 @@ class ContainerControl extends StatelessWidget with FletStoreMixin {
278272
}
279273
: null,
280274
child: GestureDetector(
281-
onTap: onTap
282-
? () {
283-
debugPrint("Container ${control.id} onTap!");
284-
backend.triggerControlEvent(control.id, "ontap", "");
285-
}
286-
: null,
287275
onTapDown: onClick || url != ""
288276
? (details) {
289277
debugPrint("Container ${control.id} clicked!");

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import 'cupertino_button.dart';
3838
import 'cupertino_checkbox.dart';
3939
import 'cupertino_context_menu.dart';
4040
import 'cupertino_context_menu_action.dart';
41+
import 'cupertino_date_picker.dart';
4142
import 'cupertino_dialog_action.dart';
4243
import 'cupertino_list_tile.dart';
4344
import 'cupertino_navigation_bar.dart';
@@ -494,7 +495,12 @@ Widget createWidget(
494495
return DatePickerControl(
495496
parent: parent,
496497
control: controlView.control,
497-
children: controlView.children,
498+
parentDisabled: parentDisabled,
499+
backend: backend);
500+
case "cupertinodatepicker":
501+
return CupertinoDatePickerControl(
502+
parent: parent,
503+
control: controlView.control,
498504
parentDisabled: parentDisabled,
499505
backend: backend);
500506
case "timepicker":

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class CupertinoActionSheetActionControl extends StatelessWidget {
2727
debugPrint("CupertinoActionSheetActionControl build: ${control.id}");
2828
bool disabled = control.isDisabled || parentDisabled;
2929

30+
String text = control.attrString("text", "")!;
3031
var contentCtrls =
3132
children.where((c) => c.name == "content" && c.isVisible);
3233
if (contentCtrls.isEmpty) {
@@ -37,13 +38,17 @@ class CupertinoActionSheetActionControl extends StatelessWidget {
3738
return constrainedControl(
3839
context,
3940
CupertinoActionSheetAction(
40-
isDefaultAction: control.attrBool("default", false)!,
41-
isDestructiveAction: control.attrBool("destructive", false)!,
41+
isDefaultAction: control.attrBool("isDefaultAction", false)!,
42+
isDestructiveAction: control.attrBool("isDestructiveAction", false)!,
4243
onPressed: () {
43-
backend.triggerControlEvent(control.id, "click", "");
44+
if (!disabled) {
45+
backend.triggerControlEvent(control.id, "click");
46+
}
4447
},
45-
child: createControl(control, contentCtrls.first.id, disabled,
46-
parentAdaptive: parentAdaptive),
48+
child: contentCtrls.isNotEmpty
49+
? createControl(control, contentCtrls.first.id, disabled,
50+
parentAdaptive: parentAdaptive)
51+
: Text(text),
4752
),
4853
parent,
4954
control);

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,13 @@ class _CupertinoContextMenuActionControlState
5454
: Text(text, overflow: TextOverflow.ellipsis));
5555

5656
return CupertinoContextMenuAction(
57-
isDefaultAction: widget.control.attrBool("default", false)!,
58-
isDestructiveAction: widget.control.attrBool("destructive", false)!,
57+
isDefaultAction: widget.control.attrBool("isDefaultAction", false)!,
58+
isDestructiveAction:
59+
widget.control.attrBool("isDestructiveAction", false)!,
5960
onPressed: () {
60-
widget.backend.triggerControlEvent(widget.control.id, "click", "");
61+
if (!disabled) {
62+
widget.backend.triggerControlEvent(widget.control.id, "click");
63+
}
6164
},
6265
trailingIcon: trailingIcon,
6366
child: child,
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import 'package:collection/collection.dart';
2+
import 'package:flutter/cupertino.dart';
3+
import 'package:flutter/material.dart';
4+
5+
import '../flet_control_backend.dart';
6+
import '../models/control.dart';
7+
import '../utils/colors.dart';
8+
import 'create_control.dart';
9+
import 'error.dart';
10+
11+
class CupertinoDatePickerControl extends StatefulWidget {
12+
final Control? parent;
13+
final Control control;
14+
final bool parentDisabled;
15+
final FletControlBackend backend;
16+
17+
const CupertinoDatePickerControl(
18+
{super.key,
19+
this.parent,
20+
required this.control,
21+
required this.parentDisabled,
22+
required this.backend});
23+
24+
@override
25+
State<CupertinoDatePickerControl> createState() =>
26+
_CupertinoDatePickerControlState();
27+
}
28+
29+
class _CupertinoDatePickerControlState
30+
extends State<CupertinoDatePickerControl> {
31+
static const double _kItemExtent = 32.0;
32+
33+
@override
34+
Widget build(BuildContext context) {
35+
debugPrint("CupertinoDatePicker build: ${widget.control.id}");
36+
37+
bool showDayOfWeek = widget.control.attrBool("showDayOfWeek", false)!;
38+
Color? bgcolor = HexColor.fromString(
39+
Theme.of(context), widget.control.attrString("bgcolor", "")!);
40+
DateTime? value = widget.control.attrDateTime("value");
41+
DateTime? firstDate = widget.control.attrDateTime("firstDate");
42+
DateTime? lastDate = widget.control.attrDateTime("lastDate");
43+
int minimumYear = widget.control.attrInt("minimumYear", 1)!;
44+
int? maximumYear = widget.control.attrInt("maximumYear");
45+
double itemExtent = widget.control.attrDouble("itemExtent", _kItemExtent)!;
46+
int minuteInterval = widget.control.attrInt("minuteInterval", 1)!;
47+
bool use24hFormat = widget.control.attrBool("use24hFormat", false)!;
48+
49+
DatePickerDateOrder? dateOrder = DatePickerDateOrder.values
50+
.firstWhereOrNull((a) =>
51+
a.name.toLowerCase() ==
52+
widget.control.attrString("dateOrder", "")!.toLowerCase());
53+
CupertinoDatePickerMode datePickerMode = CupertinoDatePickerMode.values
54+
.firstWhere(
55+
(a) =>
56+
a.name.toLowerCase() ==
57+
widget.control.attrString("datePickerMode", "")!.toLowerCase(),
58+
orElse: () => CupertinoDatePickerMode.dateAndTime);
59+
60+
Widget dialog;
61+
try {
62+
dialog = CupertinoDatePicker(
63+
initialDateTime: value,
64+
showDayOfWeek: showDayOfWeek,
65+
minimumDate: firstDate,
66+
maximumDate: lastDate,
67+
backgroundColor: bgcolor,
68+
minimumYear: minimumYear,
69+
maximumYear: maximumYear,
70+
itemExtent: itemExtent,
71+
minuteInterval: minuteInterval,
72+
use24hFormat: use24hFormat,
73+
dateOrder: dateOrder,
74+
mode: datePickerMode,
75+
onDateTimeChanged: (DateTime value) {
76+
String stringValue = value.toIso8601String();
77+
widget.backend
78+
.updateControlState(widget.control.id, {"value": stringValue});
79+
widget.backend
80+
.triggerControlEvent(widget.control.id, "change", stringValue);
81+
},
82+
);
83+
} catch (e) {
84+
return ErrorControl("CupertinoDatePicker Error: ${e.toString()}");
85+
}
86+
87+
return constrainedControl(context, dialog, widget.parent, widget.control);
88+
}
89+
}

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,13 @@ import 'form_field.dart';
88
class DatePickerControl extends StatefulWidget {
99
final Control? parent;
1010
final Control control;
11-
final List<Control> children;
1211
final bool parentDisabled;
1312
final FletControlBackend backend;
1413

1514
const DatePickerControl(
1615
{super.key,
1716
this.parent,
1817
required this.control,
19-
required this.children,
2018
required this.parentDisabled,
2119
required this.backend});
2220

sdk/python/packages/flet-core/src/flet_core/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@
7676
from flet_core.cupertino_checkbox import CupertinoCheckbox
7777
from flet_core.cupertino_context_menu import CupertinoContextMenu
7878
from flet_core.cupertino_context_menu_action import CupertinoContextMenuAction
79+
from flet_core.cupertino_date_picker import (
80+
CupertinoDatePicker,
81+
CupertinoDatePickerDateOrder,
82+
CupertinoDatePickerMode,
83+
)
7984
from flet_core.cupertino_dialog_action import CupertinoDialogAction
8085
from flet_core.cupertino_filled_button import CupertinoFilledButton
8186
from flet_core.cupertino_list_tile import CupertinoListTile

sdk/python/packages/flet-core/src/flet_core/container.py

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ def __init__(
118118
url_target: Optional[str] = None,
119119
theme: Optional[Theme] = None,
120120
theme_mode: Optional[ThemeMode] = None,
121-
on_release=None,
122121
on_click=None,
123122
on_long_press=None,
124123
on_hover=None,
@@ -165,8 +164,6 @@ def convert_container_tap_event_data(e):
165164
d = json.loads(e.data)
166165
return ContainerTapEvent(**d)
167166

168-
self.__on_release = EventHandler(convert_container_tap_event_data)
169-
self._add_event_handler("tap", self.__on_release.get_handler())
170167
self.__on_click = EventHandler(convert_container_tap_event_data)
171168
self._add_event_handler("click", self.__on_click.get_handler())
172169

@@ -195,7 +192,6 @@ def convert_container_tap_event_data(e):
195192
self.url_target = url_target
196193
self.theme = theme
197194
self.theme_mode = theme_mode
198-
self.on_release = on_release
199195
self.on_click = on_click
200196
self.on_long_press = on_long_press
201197
self.on_hover = on_hover
@@ -472,16 +468,6 @@ def theme_mode(self, value: Optional[ThemeMode]):
472468
self.__theme_mode = value
473469
self._set_attr("themeMode", value.value if value is not None else None)
474470

475-
# on_release
476-
@property
477-
def on_release(self):
478-
return self._get_event_handler("tap")
479-
480-
@on_release.setter
481-
def on_release(self, handler):
482-
self._add_event_handler("tap", handler)
483-
self._set_attr("onTap", True if handler is not None else None)
484-
485471
# on_click
486472
@property
487473
def on_click(self):
@@ -490,10 +476,8 @@ def on_click(self):
490476
@on_click.setter
491477
def on_click(self, handler):
492478
self.__on_click.subscribe(handler)
493-
if handler is not None:
494-
self._set_attr("onclick", True)
495-
else:
496-
self._set_attr("onclick", None)
479+
self._set_attr("onClick", True if handler is not None else None)
480+
497481

498482
# on_long_press
499483
@property

sdk/python/packages/flet-core/src/flet_core/cupertino_action_sheet_action.py

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ class CupertinoActionSheetAction(ConstrainedControl):
2323

2424
def __init__(
2525
self,
26-
content: Control,
27-
default: Optional[bool] = None,
28-
destructive: Optional[bool] = None,
26+
text: Optional[str] = None,
27+
content: Optional[Control] = None,
28+
is_default_action: Optional[bool] = None,
29+
is_destructive_action: Optional[bool] = None,
2930
on_click=None,
3031
#
3132
# ConstrainedControl
@@ -89,9 +90,10 @@ def __init__(
8990
data=data,
9091
)
9192

93+
self.text = text
9294
self.content = content
93-
self.default = default
94-
self.destructive = destructive
95+
self.is_default_action = is_default_action
96+
self.is_destructive_action = is_destructive_action
9597
self.on_click = on_click
9698

9799
def _get_control_name(self):
@@ -107,23 +109,32 @@ def _get_children(self):
107109
children.append(self.__content)
108110
return children
109111

110-
# default
112+
# text
111113
@property
112-
def default(self) -> Optional[bool]:
113-
return self._get_attr("default", data_type="bool", def_value=False)
114+
def text(self):
115+
return self._get_attr("text")
114116

115-
@default.setter
116-
def default(self, value: Optional[bool]):
117-
self._set_attr("default", value)
117+
@text.setter
118+
def text(self, value):
119+
self._set_attr("text", value)
118120

119-
# destructive
121+
# is_default_action
120122
@property
121-
def destructive(self) -> Optional[bool]:
122-
return self._get_attr("destructive", data_type="bool", def_value=False)
123+
def is_default_action(self) -> Optional[bool]:
124+
return self._get_attr("isDefaultAction")
123125

124-
@destructive.setter
125-
def destructive(self, value: Optional[bool]):
126-
self._set_attr("destructive", value)
126+
@is_default_action.setter
127+
def is_default_action(self, value: Optional[bool]):
128+
self._set_attr("isDefaultAction", value)
129+
130+
# is_destructive_action
131+
@property
132+
def is_destructive_action(self) -> Optional[bool]:
133+
return self._get_attr("isDestructiveAction")
134+
135+
@is_destructive_action.setter
136+
def is_destructive_action(self, value: Optional[bool]):
137+
self._set_attr("isDestructiveAction", value)
127138

128139
# content
129140
@property

0 commit comments

Comments
 (0)