From c224b01feb27c01aa47678deea8e699d09f655dc Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Thu, 6 Nov 2025 11:08:14 -0800 Subject: [PATCH 1/3] Set notifyParent flag for chart controls Added notifyParent = true to relevant controls in scatter and radar chart implementations to ensure parent widgets are notified of changes in child controls. This improves reactivity and data propagation within chart components. --- .../src/flutter/flet_charts/lib/src/scatter_chart.dart | 1 + .../flutter/flet_charts/lib/src/utils/radar_chart.dart | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/sdk/python/packages/flet-charts/src/flutter/flet_charts/lib/src/scatter_chart.dart b/sdk/python/packages/flet-charts/src/flutter/flet_charts/lib/src/scatter_chart.dart index c4072ab9a8..e04d606772 100644 --- a/sdk/python/packages/flet-charts/src/flutter/flet_charts/lib/src/scatter_chart.dart +++ b/sdk/python/packages/flet-charts/src/flutter/flet_charts/lib/src/scatter_chart.dart @@ -38,6 +38,7 @@ class _ScatterChartControlState extends State { // Build list of ScatterSpotData final spotsAsControls = widget.control.children('spots'); final spots = spotsAsControls.map((spot) { + spot.notifyParent = true; var x = spot.getDouble('x', 0)!; var y = spot.getDouble('y', 0)!; return ScatterSpot(x, y, diff --git a/sdk/python/packages/flet-charts/src/flutter/flet_charts/lib/src/utils/radar_chart.dart b/sdk/python/packages/flet-charts/src/flutter/flet_charts/lib/src/utils/radar_chart.dart index fadd9f77e8..4ceac56bb6 100644 --- a/sdk/python/packages/flet-charts/src/flutter/flet_charts/lib/src/utils/radar_chart.dart +++ b/sdk/python/packages/flet-charts/src/flutter/flet_charts/lib/src/utils/radar_chart.dart @@ -51,16 +51,17 @@ class RadarChartEventData extends Equatable { RadarDataSet parseRadarDataSet( Control dataSet, ThemeData theme, BuildContext context) { + dataSet.notifyParent = true; final fillColor = dataSet.getColor("fill_color", context, Colors.cyan)!; final fillGradient = dataSet.getGradient("fill_gradient", theme); final borderColor = dataSet.getColor("border_color", context, Colors.cyan)!; final borderWidth = dataSet.getDouble("border_width", 2.0)!; final entryRadius = dataSet.getDouble("entry_radius", 5.0)!; - final entries = dataSet - .children("entries") - .map((entry) => RadarEntry(value: entry.getDouble("value", 0)!)) - .toList(); + final entries = dataSet.children("entries").map((entry) { + entry.notifyParent = true; + return RadarEntry(value: entry.getDouble("value", 0)!); + }).toList(); return RadarDataSet( dataEntries: entries, @@ -74,6 +75,7 @@ RadarDataSet parseRadarDataSet( RadarChartTitle parseRadarChartTitle( Control title, ThemeData theme, double defaultAngle) { + title.notifyParent = true; final spansValue = title.get("text_spans"); final spans = spansValue != null ? parseTextSpans(spansValue, theme, (control, eventName, [eventData]) { From 1b9bdccef3a32eb68effb00874d3606715619337 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Thu, 6 Nov 2025 11:50:37 -0800 Subject: [PATCH 2/3] Pass TapDownDetails to tap event handlers Tap, secondary tap, and double tap event handlers now receive the last TapDownDetails as event data. This enables more detailed event information to be available for these gesture events. Fix #5784 --- .../lib/src/controls/gesture_detector.dart | 48 +++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/packages/flet/lib/src/controls/gesture_detector.dart b/packages/flet/lib/src/controls/gesture_detector.dart index 0752cc8040..c733289142 100644 --- a/packages/flet/lib/src/controls/gesture_detector.dart +++ b/packages/flet/lib/src/controls/gesture_detector.dart @@ -39,6 +39,9 @@ class _GestureDetectorControlState extends State { bool _rightPanActive = false; int _rightPanTimestamp = DateTime.now().millisecondsSinceEpoch; Offset _rightPanStart = Offset.zero; + TapDownDetails? _lastTapDownDetails; + TapDownDetails? _lastSecondaryTapDownDetails; + TapDownDetails? _lastDoubleTapDownDetails; @override void initState() { @@ -217,26 +220,32 @@ class _GestureDetectorControlState extends State { .nonNulls .toSet(); }(), - onTap: onTap ? () => widget.control.triggerEvent("tap") : null, - onTapDown: onTapDown - ? (TapDownDetails details) { - widget.control.triggerEvent("tap_down", details.toMap()); - } + onTap: onTap + ? () => widget.control + .triggerEvent("tap", _lastTapDownDetails?.toMap()) : null, + onTapDown: (TapDownDetails details) { + if (onTapDown) { + widget.control.triggerEvent("tap_down", details.toMap()); + } + _lastTapDownDetails = details; + }, onTapUp: onTapUp ? (TapUpDetails details) { widget.control.triggerEvent("tap_up", details.toMap()); } : null, onSecondaryTap: onSecondaryTap - ? () => widget.control.triggerEvent("secondary_tap") - : null, - onSecondaryTapDown: onSecondaryTapDown - ? (TapDownDetails details) { - widget.control - .triggerEvent("secondary_tap_down", details.toMap()); - } + ? () => widget.control.triggerEvent( + "secondary_tap", _lastSecondaryTapDownDetails?.toMap()) : null, + onSecondaryTapDown: (TapDownDetails details) { + if (onSecondaryTapDown) { + widget.control + .triggerEvent("secondary_tap_down", details.toMap()); + } + _lastSecondaryTapDownDetails = details; + }, onSecondaryTapUp: onSecondaryTapUp ? (TapUpDetails details) { widget.control @@ -268,14 +277,15 @@ class _GestureDetectorControlState extends State { } : null, onDoubleTap: onDoubleTap - ? () => widget.control.triggerEvent("double_tap") - : null, - onDoubleTapDown: onDoubleTapDown - ? (TapDownDetails details) { - widget.control - .triggerEvent("double_tap_down", details.toMap()); - } + ? () => widget.control.triggerEvent( + "double_tap", _lastDoubleTapDownDetails?.toMap()) : null, + onDoubleTapDown: (TapDownDetails details) { + if (onDoubleTapDown) { + widget.control.triggerEvent("double_tap_down", details.toMap()); + } + _lastDoubleTapDownDetails = details; + }, onHorizontalDragStart: (onHorizontalDragStart || onHorizontalDragUpdate) ? handleHorizontalDragStart From f37edbfef3b21d99dda9067bbe93c71b5ea7ffa5 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Thu, 6 Nov 2025 12:06:40 -0800 Subject: [PATCH 3/3] Add tertiary tap and long press event support Introduces handling for tertiary tap down, tap up, long press start, and long press end events in both Dart and Python GestureDetector controls. This enables detection and response to gestures performed with a tertiary button, expanding the range of supported pointer interactions. --- .../lib/src/controls/gesture_detector.dart | 36 +++++++++++++++++++ .../flet/controls/core/gesture_detector.py | 30 ++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/packages/flet/lib/src/controls/gesture_detector.dart b/packages/flet/lib/src/controls/gesture_detector.dart index c733289142..4f9409f724 100644 --- a/packages/flet/lib/src/controls/gesture_detector.dart +++ b/packages/flet/lib/src/controls/gesture_detector.dart @@ -71,6 +71,10 @@ class _GestureDetectorControlState extends State { widget.control.getBool("on_secondary_tap_down", false)!; var onSecondaryTapUp = widget.control.getBool("on_secondary_tap_up", false)!; + var onTertiaryTapDown = + widget.control.getBool("on_tertiary_tap_down", false)!; + var onTertiaryTapUp = + widget.control.getBool("on_tertiary_tap_up", false)!; var onLongPressStart = widget.control.getBool("on_long_press_start", false)!; var onLongPressEnd = widget.control.getBool("on_long_press_end", false)!; @@ -78,6 +82,10 @@ class _GestureDetectorControlState extends State { widget.control.getBool("on_secondary_long_press_start", false)!; var onSecondaryLongPressEnd = widget.control.getBool("on_secondary_long_press_end", false)!; + var onTertiaryLongPressStart = + widget.control.getBool("on_tertiary_long_press_start", false)!; + var onTertiaryLongPressEnd = + widget.control.getBool("on_tertiary_long_press_end", false)!; var onDoubleTap = widget.control.getBool("on_double_tap", false)!; var onDoubleTapDown = widget.control.getBool("on_double_tap_down", false)!; var onHorizontalDragStart = @@ -188,10 +196,14 @@ class _GestureDetectorControlState extends State { onSecondaryTap | onSecondaryTapDown | onSecondaryTapUp | + onTertiaryTapDown | + onTertiaryTapUp | onLongPressStart | onLongPressEnd | onSecondaryLongPressStart | onSecondaryLongPressEnd | + onTertiaryLongPressStart | + onTertiaryLongPressEnd | onDoubleTap | onDoubleTapDown | onHorizontalDragStart | @@ -252,6 +264,18 @@ class _GestureDetectorControlState extends State { .triggerEvent("secondary_tap_up", details.toMap()); } : null, + onTertiaryTapDown: (TapDownDetails details) { + if (onTertiaryTapDown) { + widget.control + .triggerEvent("tertiary_tap_down", details.toMap()); + } + }, + onTertiaryTapUp: onTertiaryTapUp + ? (TapUpDetails details) { + widget.control + .triggerEvent("tertiary_tap_up", details.toMap()); + } + : null, onLongPressStart: onLongPressStart ? (LongPressStartDetails details) { widget.control @@ -276,6 +300,18 @@ class _GestureDetectorControlState extends State { "secondary_long_press_end", details.toMap()); } : null, + onTertiaryLongPressStart: onTertiaryLongPressStart + ? (LongPressStartDetails details) { + widget.control.triggerEvent( + "tertiary_long_press_start", details.toMap()); + } + : null, + onTertiaryLongPressEnd: onTertiaryLongPressEnd + ? (LongPressEndDetails details) { + widget.control.triggerEvent( + "tertiary_long_press_end", details.toMap()); + } + : null, onDoubleTap: onDoubleTap ? () => widget.control.triggerEvent( "double_tap", _lastDoubleTapDownDetails?.toMap()) diff --git a/sdk/python/packages/flet/src/flet/controls/core/gesture_detector.py b/sdk/python/packages/flet/src/flet/controls/core/gesture_detector.py index 965681fb85..19872ad290 100644 --- a/sdk/python/packages/flet/src/flet/controls/core/gesture_detector.py +++ b/sdk/python/packages/flet/src/flet/controls/core/gesture_detector.py @@ -128,6 +128,18 @@ class GestureDetector(LayoutControl, AdaptiveControl): contacting the screen at a particular location. """ + on_tertiary_tap_down: Optional[EventHandler[TapEvent["GestureDetector"]]] = None + """ + Called when a pointer that might cause a tap with a tertiary button has contacted + the screen at a particular location. + """ + + on_tertiary_tap_up: Optional[EventHandler[TapEvent["GestureDetector"]]] = None + """ + Called when a pointer that will trigger a tap with a tertiary button has stopped + contacting the screen at a particular location. + """ + on_long_press_start: Optional[ EventHandler[LongPressStartEvent["GestureDetector"]] ] = None @@ -164,6 +176,24 @@ class GestureDetector(LayoutControl, AdaptiveControl): stopped contacting the screen. """ + on_tertiary_long_press_start: Optional[ + EventHandler[LongPressStartEvent["GestureDetector"]] + ] = None + """ + Called when a long press gesture with a tertiary button has been recognized. + + Triggered when a pointer has remained in contact with the screen at the same + location for a long period of time. + """ + + on_tertiary_long_press_end: Optional[ + EventHandler[LongPressEndEvent["GestureDetector"]] + ] = None + """ + Called when a pointer that has triggered a long-press with a tertiary button has + stopped contacting the screen. + """ + on_double_tap: Optional[EventHandler[TapEvent["GestureDetector"]]] = None """ The user has tapped the screen with a primary button at the same location twice