Skip to content

Commit

Permalink
CupertinoDatePicker Control (#2795)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
ndonkoHenri and FeodorFitsner authored Mar 6, 2024
1 parent a777b41 commit 2e970dd
Show file tree
Hide file tree
Showing 14 changed files with 464 additions and 134 deletions.
20 changes: 4 additions & 16 deletions packages/flet/lib/src/controls/container.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ class ContainerControl extends StatelessWidget with FletStoreMixin {
children.where((c) => c.name == "content" && c.isVisible);
bool ink = control.attrBool("ink", false)!;
bool onClick = control.attrBool("onclick", false)!;
bool onTap = control.attrBool("ontap", false)!;
String url = control.attrString("url", "")!;
String? urlTarget = control.attrString("urlTarget");
bool onLongPress = control.attrBool("onLongPress", false)!;
Expand Down Expand Up @@ -146,7 +145,7 @@ class ContainerControl extends StatelessWidget with FletStoreMixin {

Widget? result;

if ((onTap || onClick || url != "" || onLongPress || onHover) &&
if ((onClick || url != "" || onLongPress || onHover) &&
ink &&
!disabled) {
var ink = Material(
Expand All @@ -156,12 +155,7 @@ class ContainerControl extends StatelessWidget with FletStoreMixin {
// Dummy callback to enable widget
// see https://github.com/flutter/flutter/issues/50116#issuecomment-582047374
// and https://github.com/flutter/flutter/blob/eed80afe2c641fb14b82a22279d2d78c19661787/packages/flutter/lib/src/material/ink_well.dart#L1125-L1129
onTap: onTap
? () {
debugPrint("Container ${control.id} Tap!");
backend.triggerControlEvent(control.id, "tap", "");
}
: null,
onTap: onHover ? () {} : null,
onTapDown: onClick || url != ""
? (details) {
debugPrint("Container ${control.id} clicked!");
Expand Down Expand Up @@ -257,10 +251,10 @@ class ContainerControl extends StatelessWidget with FletStoreMixin {
: null,
child: child);

if ((onTap || onClick || onLongPress || onHover || url != "") &&
if ((onClick || onLongPress || onHover || url != "") &&
!disabled) {
result = MouseRegion(
cursor: onTap || onClick || url != ""
cursor: onClick || url != ""
? SystemMouseCursors.click
: MouseCursor.defer,
onEnter: onHover
Expand All @@ -278,12 +272,6 @@ class ContainerControl extends StatelessWidget with FletStoreMixin {
}
: null,
child: GestureDetector(
onTap: onTap
? () {
debugPrint("Container ${control.id} onTap!");
backend.triggerControlEvent(control.id, "ontap", "");
}
: null,
onTapDown: onClick || url != ""
? (details) {
debugPrint("Container ${control.id} clicked!");
Expand Down
8 changes: 7 additions & 1 deletion packages/flet/lib/src/controls/create_control.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import 'cupertino_button.dart';
import 'cupertino_checkbox.dart';
import 'cupertino_context_menu.dart';
import 'cupertino_context_menu_action.dart';
import 'cupertino_date_picker.dart';
import 'cupertino_dialog_action.dart';
import 'cupertino_list_tile.dart';
import 'cupertino_navigation_bar.dart';
Expand Down Expand Up @@ -494,7 +495,12 @@ Widget createWidget(
return DatePickerControl(
parent: parent,
control: controlView.control,
children: controlView.children,
parentDisabled: parentDisabled,
backend: backend);
case "cupertinodatepicker":
return CupertinoDatePickerControl(
parent: parent,
control: controlView.control,
parentDisabled: parentDisabled,
backend: backend);
case "timepicker":
Expand Down
15 changes: 10 additions & 5 deletions packages/flet/lib/src/controls/cupertino_action_sheet_action.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class CupertinoActionSheetActionControl extends StatelessWidget {
debugPrint("CupertinoActionSheetActionControl build: ${control.id}");
bool disabled = control.isDisabled || parentDisabled;

String text = control.attrString("text", "")!;
var contentCtrls =
children.where((c) => c.name == "content" && c.isVisible);
if (contentCtrls.isEmpty) {
Expand All @@ -37,13 +38,17 @@ class CupertinoActionSheetActionControl extends StatelessWidget {
return constrainedControl(
context,
CupertinoActionSheetAction(
isDefaultAction: control.attrBool("default", false)!,
isDestructiveAction: control.attrBool("destructive", false)!,
isDefaultAction: control.attrBool("isDefaultAction", false)!,
isDestructiveAction: control.attrBool("isDestructiveAction", false)!,
onPressed: () {
backend.triggerControlEvent(control.id, "click", "");
if (!disabled) {
backend.triggerControlEvent(control.id, "click");
}
},
child: createControl(control, contentCtrls.first.id, disabled,
parentAdaptive: parentAdaptive),
child: contentCtrls.isNotEmpty
? createControl(control, contentCtrls.first.id, disabled,
parentAdaptive: parentAdaptive)
: Text(text),
),
parent,
control);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,13 @@ class _CupertinoContextMenuActionControlState
: Text(text, overflow: TextOverflow.ellipsis));

return CupertinoContextMenuAction(
isDefaultAction: widget.control.attrBool("default", false)!,
isDestructiveAction: widget.control.attrBool("destructive", false)!,
isDefaultAction: widget.control.attrBool("isDefaultAction", false)!,
isDestructiveAction:
widget.control.attrBool("isDestructiveAction", false)!,
onPressed: () {
widget.backend.triggerControlEvent(widget.control.id, "click", "");
if (!disabled) {
widget.backend.triggerControlEvent(widget.control.id, "click");
}
},
trailingIcon: trailingIcon,
child: child,
Expand Down
89 changes: 89 additions & 0 deletions packages/flet/lib/src/controls/cupertino_date_picker.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import 'package:collection/collection.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

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

class CupertinoDatePickerControl extends StatefulWidget {
final Control? parent;
final Control control;
final bool parentDisabled;
final FletControlBackend backend;

const CupertinoDatePickerControl(
{super.key,
this.parent,
required this.control,
required this.parentDisabled,
required this.backend});

@override
State<CupertinoDatePickerControl> createState() =>
_CupertinoDatePickerControlState();
}

class _CupertinoDatePickerControlState
extends State<CupertinoDatePickerControl> {
static const double _kItemExtent = 32.0;

@override
Widget build(BuildContext context) {
debugPrint("CupertinoDatePicker build: ${widget.control.id}");

bool showDayOfWeek = widget.control.attrBool("showDayOfWeek", false)!;
Color? bgcolor = HexColor.fromString(
Theme.of(context), widget.control.attrString("bgcolor", "")!);
DateTime? value = widget.control.attrDateTime("value");
DateTime? firstDate = widget.control.attrDateTime("firstDate");
DateTime? lastDate = widget.control.attrDateTime("lastDate");
int minimumYear = widget.control.attrInt("minimumYear", 1)!;
int? maximumYear = widget.control.attrInt("maximumYear");
double itemExtent = widget.control.attrDouble("itemExtent", _kItemExtent)!;
int minuteInterval = widget.control.attrInt("minuteInterval", 1)!;
bool use24hFormat = widget.control.attrBool("use24hFormat", false)!;

DatePickerDateOrder? dateOrder = DatePickerDateOrder.values
.firstWhereOrNull((a) =>
a.name.toLowerCase() ==
widget.control.attrString("dateOrder", "")!.toLowerCase());
CupertinoDatePickerMode datePickerMode = CupertinoDatePickerMode.values
.firstWhere(
(a) =>
a.name.toLowerCase() ==
widget.control.attrString("datePickerMode", "")!.toLowerCase(),
orElse: () => CupertinoDatePickerMode.dateAndTime);

Widget dialog;
try {
dialog = CupertinoDatePicker(
initialDateTime: value,
showDayOfWeek: showDayOfWeek,
minimumDate: firstDate,
maximumDate: lastDate,
backgroundColor: bgcolor,
minimumYear: minimumYear,
maximumYear: maximumYear,
itemExtent: itemExtent,
minuteInterval: minuteInterval,
use24hFormat: use24hFormat,
dateOrder: dateOrder,
mode: datePickerMode,
onDateTimeChanged: (DateTime value) {
String stringValue = value.toIso8601String();
widget.backend
.updateControlState(widget.control.id, {"value": stringValue});
widget.backend
.triggerControlEvent(widget.control.id, "change", stringValue);
},
);
} catch (e) {
return ErrorControl("CupertinoDatePicker Error: ${e.toString()}");
}

return constrainedControl(context, dialog, widget.parent, widget.control);
}
}
2 changes: 0 additions & 2 deletions packages/flet/lib/src/controls/date_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@ import 'form_field.dart';
class DatePickerControl extends StatefulWidget {
final Control? parent;
final Control control;
final List<Control> children;
final bool parentDisabled;
final FletControlBackend backend;

const DatePickerControl(
{super.key,
this.parent,
required this.control,
required this.children,
required this.parentDisabled,
required this.backend});

Expand Down
5 changes: 5 additions & 0 deletions sdk/python/packages/flet-core/src/flet_core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@
from flet_core.cupertino_checkbox import CupertinoCheckbox
from flet_core.cupertino_context_menu import CupertinoContextMenu
from flet_core.cupertino_context_menu_action import CupertinoContextMenuAction
from flet_core.cupertino_date_picker import (
CupertinoDatePicker,
CupertinoDatePickerDateOrder,
CupertinoDatePickerMode,
)
from flet_core.cupertino_dialog_action import CupertinoDialogAction
from flet_core.cupertino_filled_button import CupertinoFilledButton
from flet_core.cupertino_list_tile import CupertinoListTile
Expand Down
20 changes: 2 additions & 18 deletions sdk/python/packages/flet-core/src/flet_core/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ def __init__(
url_target: Optional[str] = None,
theme: Optional[Theme] = None,
theme_mode: Optional[ThemeMode] = None,
on_release=None,
on_click=None,
on_long_press=None,
on_hover=None,
Expand Down Expand Up @@ -165,8 +164,6 @@ def convert_container_tap_event_data(e):
d = json.loads(e.data)
return ContainerTapEvent(**d)

self.__on_release = EventHandler(convert_container_tap_event_data)
self._add_event_handler("tap", self.__on_release.get_handler())
self.__on_click = EventHandler(convert_container_tap_event_data)
self._add_event_handler("click", self.__on_click.get_handler())

Expand Down Expand Up @@ -195,7 +192,6 @@ def convert_container_tap_event_data(e):
self.url_target = url_target
self.theme = theme
self.theme_mode = theme_mode
self.on_release = on_release
self.on_click = on_click
self.on_long_press = on_long_press
self.on_hover = on_hover
Expand Down Expand Up @@ -472,16 +468,6 @@ def theme_mode(self, value: Optional[ThemeMode]):
self.__theme_mode = value
self._set_attr("themeMode", value.value if value is not None else None)

# on_release
@property
def on_release(self):
return self._get_event_handler("tap")

@on_release.setter
def on_release(self, handler):
self._add_event_handler("tap", handler)
self._set_attr("onTap", True if handler is not None else None)

# on_click
@property
def on_click(self):
Expand All @@ -490,10 +476,8 @@ def on_click(self):
@on_click.setter
def on_click(self, handler):
self.__on_click.subscribe(handler)
if handler is not None:
self._set_attr("onclick", True)
else:
self._set_attr("onclick", None)
self._set_attr("onClick", True if handler is not None else None)


# on_long_press
@property
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ class CupertinoActionSheetAction(ConstrainedControl):

def __init__(
self,
content: Control,
default: Optional[bool] = None,
destructive: Optional[bool] = None,
text: Optional[str] = None,
content: Optional[Control] = None,
is_default_action: Optional[bool] = None,
is_destructive_action: Optional[bool] = None,
on_click=None,
#
# ConstrainedControl
Expand Down Expand Up @@ -89,9 +90,10 @@ def __init__(
data=data,
)

self.text = text
self.content = content
self.default = default
self.destructive = destructive
self.is_default_action = is_default_action
self.is_destructive_action = is_destructive_action
self.on_click = on_click

def _get_control_name(self):
Expand All @@ -107,23 +109,32 @@ def _get_children(self):
children.append(self.__content)
return children

# default
# text
@property
def default(self) -> Optional[bool]:
return self._get_attr("default", data_type="bool", def_value=False)
def text(self):
return self._get_attr("text")

@default.setter
def default(self, value: Optional[bool]):
self._set_attr("default", value)
@text.setter
def text(self, value):
self._set_attr("text", value)

# destructive
# is_default_action
@property
def destructive(self) -> Optional[bool]:
return self._get_attr("destructive", data_type="bool", def_value=False)
def is_default_action(self) -> Optional[bool]:
return self._get_attr("isDefaultAction")

@destructive.setter
def destructive(self, value: Optional[bool]):
self._set_attr("destructive", value)
@is_default_action.setter
def is_default_action(self, value: Optional[bool]):
self._set_attr("isDefaultAction", value)

# is_destructive_action
@property
def is_destructive_action(self) -> Optional[bool]:
return self._get_attr("isDestructiveAction")

@is_destructive_action.setter
def is_destructive_action(self, value: Optional[bool]):
self._set_attr("isDestructiveAction", value)

# content
@property
Expand Down
Loading

0 comments on commit 2e970dd

Please sign in to comment.