From 13162228e72be9cf04c9ec67c205878c8720d25c Mon Sep 17 00:00:00 2001 From: TheEthicalBoy <98978078+ndonkoHenri@users.noreply.github.com> Date: Fri, 12 Jan 2024 23:39:46 +0100 Subject: [PATCH] `Dismissible.confirmDismiss` prop (#2359) * initial commit * Dismissible.on_confirm_dismiss * rename onDismissed param * update event details --------- Co-authored-by: Feodor Fitsner --- package/lib/src/controls/create_control.dart | 10 +- package/lib/src/controls/dismissible.dart | 97 +++++++++++++++---- .../flet-core/src/flet_core/__init__.py | 2 +- .../flet-core/src/flet_core/dismissible.py | 60 ++++++++++-- 4 files changed, 139 insertions(+), 30 deletions(-) diff --git a/package/lib/src/controls/create_control.dart b/package/lib/src/controls/create_control.dart index e5e199525..57c86cb4d 100644 --- a/package/lib/src/controls/create_control.dart +++ b/package/lib/src/controls/create_control.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_redux/flutter_redux.dart'; import '../flet_app_services.dart'; +import '../flet_server.dart'; import '../models/app_state.dart'; import '../models/control.dart'; import '../models/control_view_model.dart'; @@ -131,8 +132,8 @@ Widget createControl(Control? parent, String id, bool parentDisabled, } // create control widget - var widget = createWidget( - controlKey, controlView, parent, parentDisabled, nextChild); + var widget = createWidget(controlKey, controlView, parent, parentDisabled, + nextChild, FletAppServices.of(context).server); // no theme defined? return widget! if (id == "page" || controlView.control.attrString("theme") == null) { @@ -170,7 +171,7 @@ Widget createControl(Control? parent, String id, bool parentDisabled, } Widget createWidget(Key? key, ControlViewModel controlView, Control? parent, - bool parentDisabled, Widget? nextChild) { + bool parentDisabled, Widget? nextChild, FletServer server) { switch (controlView.control.type) { case "page": return PageControl( @@ -618,7 +619,8 @@ Widget createWidget(Key? key, ControlViewModel controlView, Control? parent, parent: parent, control: controlView.control, children: controlView.children, - parentDisabled: parentDisabled); + parentDisabled: parentDisabled, + server: server); case "alertdialog": return AlertDialogControl( parent: parent, diff --git a/package/lib/src/controls/dismissible.dart b/package/lib/src/controls/dismissible.dart index 53bd38f46..39b760cb2 100644 --- a/package/lib/src/controls/dismissible.dart +++ b/package/lib/src/controls/dismissible.dart @@ -1,8 +1,11 @@ -import 'package:flet/src/utils/dismissible.dart'; +import 'dart:async'; +import 'dart:convert'; + import 'package:flutter/material.dart'; -import '../flet_app_services.dart'; +import '../flet_server.dart'; import '../models/control.dart'; +import '../utils/dismissible.dart'; import 'create_control.dart'; import 'error.dart'; @@ -11,17 +14,18 @@ class DismissibleControl extends StatelessWidget { final Control control; final List children; final bool parentDisabled; + final FletServer server; const DismissibleControl( {super.key, this.parent, required this.control, required this.children, - required this.parentDisabled}); + required this.parentDisabled, + required this.server}); @override Widget build(BuildContext context) { - var server = FletAppServices.of(context).server; debugPrint("Dismissible build: ${control.id}"); bool disabled = control.isDisabled || parentDisabled; @@ -45,6 +49,17 @@ class DismissibleControl extends StatelessWidget { control.attrString("dismissDirection", "")!.toLowerCase(), orElse: () => DismissDirection.horizontal); + server.controlInvokeMethods[control.id] = (methodName, args) async { + debugPrint("Dismissible.onMethod(${control.id})"); + if (methodName == "confirm_dismiss") { + control.state["confirm_dismiss"] + ?.complete(bool.tryParse(args["dismiss"] ?? "")); + server.controlInvokeMethods.remove(control.id); + } + + return null; + }; + return constrainedControl( context, Dismissible( @@ -57,19 +72,47 @@ class DismissibleControl extends StatelessWidget { ? createControl( control, secondaryBackgroundCtrls.first.id, disabled) : Container(color: Colors.transparent), - onDismissed: (DismissDirection d) { - server.sendPageEvent( - eventTarget: control.id, eventName: "dismiss", eventData: d.name); - }, - onResize: () { - server.sendPageEvent( - eventTarget: control.id, eventName: "resize", eventData: ""); - }, - onUpdate: (DismissUpdateDetails d) { - server.sendPageEvent( - eventTarget: control.id, eventName: "update", eventData: ""); - }, - // confirmDismiss: // TODO: implement + onDismissed: control.attrBool("onDismiss", false)! + ? (DismissDirection direction) { + server.sendPageEvent( + eventTarget: control.id, + eventName: "dismiss", + eventData: direction.name); + } + : null, + onResize: control.attrBool("onResize", false)! + ? () { + server.sendPageEvent( + eventTarget: control.id, + eventName: "resize", + eventData: ""); + } + : null, + onUpdate: control.attrBool("onUpdate", false)! + ? (DismissUpdateDetails details) { + server.sendPageEvent( + eventTarget: control.id, + eventName: "update", + eventData: json.encode(DismissibleUpdateEvent( + direction: details.direction.name, + previousReached: details.previousReached, + progress: details.progress, + reached: details.reached) + .toJson())); + } + : null, + confirmDismiss: control.attrBool("onConfirmDismiss", false)! + ? (DismissDirection direction) { + debugPrint("Dismissible.confirmDismiss(${control.id})"); + var completer = Completer(); + control.state["confirm_dismiss"] = completer; + server.sendPageEvent( + eventTarget: control.id, + eventName: "confirm_dismiss", + eventData: direction.name); + return completer.future; + } + : null, movementDuration: Duration(milliseconds: control.attrInt("duration", 200)!), resizeDuration: @@ -81,3 +124,23 @@ class DismissibleControl extends StatelessWidget { control); } } + +class DismissibleUpdateEvent { + final String direction; + final bool previousReached; + final double progress; + final bool reached; + + DismissibleUpdateEvent( + {required this.direction, + required this.progress, + required this.previousReached, + required this.reached}); + + Map toJson() => { + 'direction': direction, + 'progress': progress, + 'reached': reached, + 'previous_reached': previousReached + }; +} diff --git a/sdk/python/packages/flet-core/src/flet_core/__init__.py b/sdk/python/packages/flet-core/src/flet_core/__init__.py index 771d2798f..5da9179bd 100644 --- a/sdk/python/packages/flet-core/src/flet_core/__init__.py +++ b/sdk/python/packages/flet-core/src/flet_core/__init__.py @@ -76,7 +76,7 @@ DataTable, ) from flet_core.date_picker import DatePicker, DatePickerEntryMode, DatePickerMode -from flet_core.dismissible import Dismissible, DismissibleDismissEvent +from flet_core.dismissible import Dismissible, DismissibleDismissEvent, DismissibleUpdateEvent from flet_core.divider import Divider from flet_core.drag_target import DragTarget, DragTargetAcceptEvent from flet_core.draggable import Draggable diff --git a/sdk/python/packages/flet-core/src/flet_core/dismissible.py b/sdk/python/packages/flet-core/src/flet_core/dismissible.py index 97f286087..71eb306e8 100644 --- a/sdk/python/packages/flet-core/src/flet_core/dismissible.py +++ b/sdk/python/packages/flet-core/src/flet_core/dismissible.py @@ -1,4 +1,5 @@ -from typing import Any, Optional, Dict, Union +import json +from typing import Any, Dict, Optional, Union from flet_core.constrained_control import ConstrainedControl from flet_core.control import Control, OptionalNumber @@ -7,11 +8,11 @@ from flet_core.ref import Ref from flet_core.snack_bar import DismissDirection from flet_core.types import ( - ResponsiveNumber, - ScaleValue, AnimationValue, - RotateValue, OffsetValue, + ResponsiveNumber, + RotateValue, + ScaleValue, ) @@ -70,6 +71,7 @@ def __init__( cross_axis_end_offset: OptionalNumber = None, on_update=None, on_dismiss=None, + on_confirm_dismiss=None, on_resize=None, ): ConstrainedControl.__init__( @@ -103,7 +105,16 @@ def __init__( ) self.__on_dismiss = EventHandler(lambda e: DismissibleDismissEvent(e.data)) + self.__on_update = EventHandler(lambda e: DismissibleUpdateEvent(**json.loads(e.data))) + self.__on_confirm_dismiss = EventHandler( + lambda e: DismissibleDismissEvent(e.data) + ) + self._add_event_handler("dismiss", self.__on_dismiss.get_handler()) + self._add_event_handler("update", self.__on_update.get_handler()) + self._add_event_handler( + "confirm_dismiss", self.__on_confirm_dismiss.get_handler() + ) self.content = content self.background = background @@ -115,6 +126,7 @@ def __init__( self.cross_axis_end_offset = cross_axis_end_offset self.on_update = on_update self.on_dismiss = on_dismiss + self.on_confirm_dismiss = on_confirm_dismiss self.on_resize = on_resize def _get_control_name(self): @@ -137,6 +149,16 @@ def _before_build_command(self): super()._before_build_command() self._set_attr_json("dismissThresholds", self.__dismiss_thresholds) + def confirm_dismiss(self, dismiss: bool): + self.page.invoke_method( + "confirm_dismiss", {"dismiss": str(dismiss).lower()}, control_id=self.uid + ) + + async def confirm_dismiss_async(self, dismiss: bool): + await self.page.invoke_method_async( + "confirm_dismiss", {"dismiss": str(dismiss).lower()}, control_id=self.uid + ) + # content @property def content(self) -> Control: @@ -223,7 +245,17 @@ def on_dismiss(self): @on_dismiss.setter def on_dismiss(self, handler): self.__on_dismiss.subscribe(handler) - self._set_attr("dismiss", True if handler is not None else None) + self._set_attr("onDismiss", True if handler is not None else None) + + # on_confirm_dismiss + @property + def on_confirm_dismiss(self): + return self._get_event_handler("confirm_dismiss") + + @on_confirm_dismiss.setter + def on_confirm_dismiss(self, handler): + self.__on_confirm_dismiss.subscribe(handler) + self._set_attr("onConfirmDismiss", True if handler is not None else None) # on_update @property @@ -232,7 +264,8 @@ def on_update(self): @on_update.setter def on_update(self, handler): - self._add_event_handler("update", handler) + self.__on_update.subscribe(handler) + self._set_attr("onUpdate", True if handler is not None else None) # on_resize @property @@ -242,8 +275,19 @@ def on_resize(self): @on_resize.setter def on_resize(self, handler): self._add_event_handler("resize", handler) + self._set_attr("onResize", True if handler is not None else None) class DismissibleDismissEvent(ControlEvent): - def __init__(self, d: str) -> None: - self.direction: DismissDirection = DismissDirection(d) + def __init__(self, direction: str) -> None: + self.direction: DismissDirection = DismissDirection(direction) + + +class DismissibleUpdateEvent(ControlEvent): + def __init__( + self, direction: str, progress: float, reached: bool, previous_reached: bool + ) -> None: + self.direction: DismissDirection = DismissDirection(direction) + self.progress = progress + self.reached = reached + self.previous_reached = previous_reached