Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CupertinoActionSheet and CupertinoActionSheetAction controls #2763

Merged
merged 7 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions packages/flet/lib/src/controls/create_control.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import 'circle_avatar.dart';
import 'clipboard.dart';
import 'column.dart';
import 'container.dart';
import 'cupertino_action_sheet.dart';
import 'cupertino_action_sheet_action.dart';
import 'cupertino_activity_indicator.dart';
import 'cupertino_alert_dialog.dart';
import 'cupertino_button.dart';
Expand Down Expand Up @@ -590,6 +592,25 @@ Widget createWidget(
parentDisabled: parentDisabled,
parentAdaptive: parentAdaptive,
backend: backend);
case "cupertinoactionsheet":
return CupertinoActionSheetControl(
key: key,
parent: parent,
control: controlView.control,
children: controlView.children,
parentDisabled: parentDisabled,
parentAdaptive: parentAdaptive,
nextChild: nextChild,
backend: backend);
case "cupertinoactionsheetaction":
return CupertinoActionSheetActionControl(
key: key,
parent: parent,
control: controlView.control,
children: controlView.children,
parentDisabled: parentDisabled,
parentAdaptive: parentAdaptive,
backend: backend);
case "expansiontile":
return ExpansionTileControl(
key: key,
Expand Down
118 changes: 118 additions & 0 deletions packages/flet/lib/src/controls/cupertino_action_sheet.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import 'package:flutter/cupertino.dart';

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

class CupertinoActionSheetControl extends StatefulWidget {
final Control? parent;
final Control control;
final List<Control> children;
final bool parentDisabled;
final bool? parentAdaptive;
final Widget? nextChild;
final FletControlBackend backend;

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

@override
State<CupertinoActionSheetControl> createState() =>
_CupertinoActionSheetControlState();
}

class _CupertinoActionSheetControlState
extends State<CupertinoActionSheetControl> {
Widget _createActionSheet() {
bool disabled = widget.control.isDisabled || widget.parentDisabled;

var titleCtrls =
widget.children.where((c) => c.name == "title" && c.isVisible);
var messageCtrls =
widget.children.where((c) => c.name == "message" && c.isVisible);
var cancelButtonCtrls =
widget.children.where((c) => c.name == "cancel" && c.isVisible);
var actionCtrls =
widget.children.where((c) => c.name == "action" && c.isVisible);

return CupertinoActionSheet(
title: titleCtrls.isNotEmpty
? createControl(widget.control, titleCtrls.first.id, disabled,
parentAdaptive: widget.parentAdaptive)
: null,
message: messageCtrls.isNotEmpty
? createControl(widget.control, messageCtrls.first.id, disabled,
parentAdaptive: widget.parentAdaptive)
: null,
cancelButton: cancelButtonCtrls.isNotEmpty
? createControl(widget.control, cancelButtonCtrls.first.id, disabled,
parentAdaptive: widget.parentAdaptive)
: null,
actions: actionCtrls.isNotEmpty
? actionCtrls
.map((c) => createControl(widget.control, c.id, disabled,
parentAdaptive: widget.parentAdaptive))
.toList()
: null,
);
}

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

bool lastOpen = widget.control.state["open"] ?? false;

var open = widget.control.attrBool("open", false)!;
var modal = widget.control.attrBool("modal", false)!;

debugPrint("Current open state: $lastOpen");
debugPrint("New open state: $open");

if (open && (open != lastOpen)) {
var dialog = _createActionSheet();
if (dialog is ErrorControl) {
return dialog;
}

// close previous dialog
if (ModalRoute.of(context)?.isCurrent != true) {
Navigator.of(context).pop();
}

widget.control.state["open"] = open;

WidgetsBinding.instance.addPostFrameCallback((_) {
showCupertinoModalPopup(
barrierDismissible: !modal,
useRootNavigator: false,
context: context,
builder: (context) => _createActionSheet()).then((value) {
lastOpen = widget.control.state["open"] ?? false;
debugPrint("Action sheet should be dismissed ($hashCode): $lastOpen");
bool shouldDismiss = lastOpen;
widget.control.state["open"] = false;

if (shouldDismiss) {
widget.backend
.updateControlState(widget.control.id, {"open": "false"});
widget.backend
.triggerControlEvent(widget.control.id, "dismiss", "");
}
});
});
} else if (open != lastOpen && lastOpen) {
Navigator.of(context).pop();
}

return widget.nextChild ?? const SizedBox.shrink();
}
}
51 changes: 51 additions & 0 deletions packages/flet/lib/src/controls/cupertino_action_sheet_action.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import 'package:flutter/cupertino.dart';

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

class CupertinoActionSheetActionControl extends StatelessWidget {
final Control? parent;
final Control control;
final List<Control> children;
final bool parentDisabled;
final bool? parentAdaptive;
final FletControlBackend backend;

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

@override
Widget build(BuildContext context) {
debugPrint("CupertinoActionSheetActionControl build: ${control.id}");
bool disabled = control.isDisabled || parentDisabled;

var contentCtrls =
children.where((c) => c.name == "content" && c.isVisible);
if (contentCtrls.isEmpty) {
return const ErrorControl(
"CupertinoActionSheetAction must have a content control!");
}

return constrainedControl(
context,
CupertinoActionSheetAction(
isDefaultAction: control.attrBool("default", false)!,
isDestructiveAction: control.attrBool("destructive", false)!,
onPressed: () {
backend.triggerControlEvent(control.id, "click", "");
},
child: createControl(control, contentCtrls.first.id, disabled,
parentAdaptive: parentAdaptive),
),
parent,
control);
}
}
2 changes: 2 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 @@ -66,6 +66,8 @@
from flet_core.container import Container, ContainerTapEvent
from flet_core.control import Control, OptionalNumber
from flet_core.control_event import ControlEvent
from flet_core.cupertino_action_sheet import CupertinoActionSheet
from flet_core.cupertino_action_sheet_action import CupertinoActionSheetAction
from flet_core.cupertino_activity_indicator import CupertinoActivityIndicator
from flet_core.cupertino_alert_dialog import CupertinoAlertDialog
from flet_core.cupertino_app_bar import CupertinoAppBar
Expand Down
143 changes: 143 additions & 0 deletions sdk/python/packages/flet-core/src/flet_core/cupertino_action_sheet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
from typing import Any, List, Optional, Union

from flet_core.adaptive_control import AdaptiveControl
from flet_core.control import Control, OptionalNumber
from flet_core.ref import Ref
from flet_core.types import (
AnimationValue,
OffsetValue,
ResponsiveNumber,
RotateValue,
ScaleValue,
)


class CupertinoActionSheet(AdaptiveControl):
"""
An iOS-style action sheet.

-----

Online docs: https://flet.dev/docs/controls/cupertinoactionsheet
"""

def __init__(
self,
title: Optional[Control] = None,
message: Optional[Control] = None,
actions: Optional[List[Control]] = None,
cancel: Optional[Control] = None,
modal: bool = False,
open: bool = False,
on_dismiss=None,
#
# ConstrainedControl and AdaptiveControl
#
ref: Optional[Ref] = None,
visible: Optional[bool] = None,
disabled: Optional[bool] = None,
data: Any = None,
adaptive: Optional[bool] = None,
):
Control.__init__(
self,
ref=ref,
disabled=disabled,
visible=visible,
data=data,
)

AdaptiveControl.__init__(self, adaptive=adaptive)

self.cancel = cancel
self.title = title
self.message = message
self.actions = actions
self.modal = modal
self.open = open
self.on_dismiss = on_dismiss

def _get_control_name(self):
return "cupertinoactionsheet"

def before_update(self):
super().before_update()

def _get_children(self):
children = []
if self.__cancel:
self.__cancel._set_attr_internal("n", "cancel")
children.append(self.__cancel)
if self.__title:
self.__title._set_attr_internal("n", "title")
children.append(self.__title)
if self.__message:
self.__message._set_attr_internal("n", "message")
children.append(self.__message)
for action in self.__actions:
action._set_attr_internal("n", "action")
children.append(action)
return children

# cancel
@property
def cancel(self) -> Optional[Control]:
return self.__cancel

@cancel.setter
def cancel(self, value: Optional[Control]):
self.__cancel = value

# title
@property
def title(self) -> Optional[Control]:
return self.__title

@title.setter
def title(self, value: Optional[Control]):
self.__title = value

# message
@property
def message(self) -> Optional[Control]:
return self.__message

@message.setter
def message(self, value: Optional[Control]):
self.__message = value

# actions
@property
def actions(self):
return self.__actions

@actions.setter
def actions(self, value):
self.__actions = value if value is not None else []

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

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

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

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

# on_dismiss
@property
def on_dismiss(self):
return self._get_event_handler("dismiss")

@on_dismiss.setter
def on_dismiss(self, handler):
self._add_event_handler("dismiss", handler)
Loading