From f51077601001ba88931b2987c926c17726cd9d00 Mon Sep 17 00:00:00 2001 From: Stefan Schaller Date: Wed, 15 Jul 2026 05:51:58 +0200 Subject: [PATCH] add some more widgets --- lib/src/intercept_platform_view.dart | 49 ++++++++++++++++++++++++++++ lib/src/keyboard_dismiss_on_tap.dart | 33 +++++++++++++++++++ lib/src/measure_size.dart | 40 +++++++++++++++++++++++ lib/tapped_toolkit.dart | 3 ++ 4 files changed, 125 insertions(+) create mode 100644 lib/src/intercept_platform_view.dart create mode 100644 lib/src/keyboard_dismiss_on_tap.dart create mode 100644 lib/src/measure_size.dart diff --git a/lib/src/intercept_platform_view.dart b/lib/src/intercept_platform_view.dart new file mode 100644 index 0000000..d770bbc --- /dev/null +++ b/lib/src/intercept_platform_view.dart @@ -0,0 +1,49 @@ +import 'package:flutter/cupertino.dart'; +import 'package:universal_platform/universal_platform.dart'; + +/// Inspirited of: https://pub.dev/packages/pointer_interceptor +/// +/// 😵‍💫 Whats the problem? +/// When overlaying Flutter widgets on top of HtmlElementView/PlatformView widgets +/// that respond to mouse gestures (handle clicks, for example), +/// the clicks will be consumed by the HtmlElementView/PlatformView, +/// and not relayed to Flutter. +/// The result is that Flutter widget's onTap (and other) handlers won't fire as expected, +/// but they'll affect the underlying native platform view. +/// +/// 🔎 Where do we have an problem? +/// We had issues that the [VectorMap] received events from the Flutter widgets above. +class InterceptPlatformView extends StatelessWidget { + final Widget child; + + final bool intercepting; + + const InterceptPlatformView({ + this.intercepting = true, + required this.child, + super.key, + }); + + @override + Widget build(BuildContext context) { + if (UniversalPlatform.isWeb) { + return Stack( + alignment: Alignment.center, + children: [ + if (intercepting) + // We need to add the HtmlElementView
to prevent events are captured by an underlying HtmlElementView in web. + ExcludeFocus( + child: Positioned.fill( + child: HtmlElementView.fromTagName( + tagName: 'div', + isVisible: false, + ), + ), + ), + child, + ], + ); + } + return child; + } +} diff --git a/lib/src/keyboard_dismiss_on_tap.dart b/lib/src/keyboard_dismiss_on_tap.dart new file mode 100644 index 0000000..1e3cdec --- /dev/null +++ b/lib/src/keyboard_dismiss_on_tap.dart @@ -0,0 +1,33 @@ +import 'package:flutter/widgets.dart'; + +/// ⚠️⚠️⚠️⚠️⚠️⚠️ +/// A copy of the package: [https://pub.dev/packages/flutter_keyboard_visibility] +/// Because of two reasons: +/// - https://github.com/MisterJimson/flutter_keyboard_visibility/issues/150 is not merged and we have a warning when building web: +/// Error in pipeline, when building web: Found incompatibilities with WebAssembly. package:flutter_keyboard_visibility_web/flutter_keyboard_visibility_web.dart 1:1 - dart:html unsupported (0) +/// - The package is huge and we only need one specific file, which is [KeyboardDismissOnTap] +/// - The support of the package is terrible +/// +/// We basically copied one file and cleaned it up. +/// ⚠️⚠️⚠️⚠️⚠️⚠️ + +/// Removes the current focus and hides the keyboard when +/// the user taps on this widget. +/// +/// Place this widget somewhere near the top of your widget +/// tree and when the user taps outside of a focused widget, +/// the focus will be removed and the keyboard will be hidden. +class KeyboardDismissOnTap extends StatelessWidget { + const KeyboardDismissOnTap({super.key, required this.child}); + + final Widget child; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => FocusManager.instance.primaryFocus?.unfocus(), + behavior: HitTestBehavior.translucent, + child: child, + ); + } +} diff --git a/lib/src/measure_size.dart b/lib/src/measure_size.dart new file mode 100644 index 0000000..1368a57 --- /dev/null +++ b/lib/src/measure_size.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +class MeasureSize extends SingleChildRenderObjectWidget { + final void Function(Size size) onChange; + + const MeasureSize({ + super.key, + required this.onChange, + required Widget super.child, + }); + + @override + RenderObject createRenderObject(BuildContext context) { + return _MeasureSizeRenderObject(onChange); + } + + @override + void updateRenderObject(BuildContext context, RenderObject renderObject) { + (renderObject as _MeasureSizeRenderObject).onChange = onChange; + } +} + +class _MeasureSizeRenderObject extends RenderProxyBox { + Size? oldSize; + void Function(Size size) onChange; + + _MeasureSizeRenderObject(this.onChange); + + @override + void performLayout() { + super.performLayout(); + + final newSize = child!.size; + if (oldSize == newSize) return; + + oldSize = newSize; + WidgetsBinding.instance.addPostFrameCallback((_) => onChange(newSize)); + } +} diff --git a/lib/tapped_toolkit.dart b/lib/tapped_toolkit.dart index d12b9a9..d6e5a49 100644 --- a/lib/tapped_toolkit.dart +++ b/lib/tapped_toolkit.dart @@ -4,3 +4,6 @@ export 'src/after_first_build/after_first_build_mixin.dart'; export 'src/after_first_build/on_next_frame_extension.dart'; export 'src/custom_drop_down/custom_drop_down.dart'; export 'src/text_field/base_text_field.dart'; +export 'src/intercept_platform_view.dart'; +export 'src/keyboard_dismiss_on_tap.dart'; +export 'src/measure_size.dart';