Skip to content

[pointer_interceptor_web] Update package APIs and tests. #5785

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

Merged
merged 8 commits into from
Jan 3, 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
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
## 0.10.1

* Uses `HtmlElementView.fromTagName` instead of custom factories.
* Migrates package and tests to `platform:web`.
* Updates minimum supported SDK version to Flutter 3.16.0/Dart 3.2.0.

## 0.10.0

* Moves web implementation to its own package.

Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,19 @@

// ignore_for_file: avoid_print

import 'dart:html' as html;

// Imports the Flutter Driver API.
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';

import 'package:pointer_interceptor_web_example/main.dart' as app;
import 'package:web/web.dart' as web;

final Finder nonClickableButtonFinder =
find.byKey(const Key('transparent-button'));
final Finder clickableWrappedButtonFinder =
find.byKey(const Key('wrapped-transparent-button'));
final Finder clickableButtonFinder = find.byKey(const Key('clickable-button'));
final Finder backgroundFinder =
find.byKey(const ValueKey<String>('background-widget'));
final Finder backgroundFinder = find.byKey(const Key('background-widget'));

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
Expand All @@ -28,10 +25,9 @@ void main() {
testWidgets(
'on wrapped elements, the browser does not hit the background-html-view',
(WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
await _fullyRenderApp(tester);

final html.Element element =
final web.Element element =
_getHtmlElementAtCenter(clickableButtonFinder, tester);

expect(element.id, isNot('background-html-view'));
Expand All @@ -40,10 +36,9 @@ void main() {
testWidgets(
'on wrapped elements with intercepting set to false, the browser hits the background-html-view',
(WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
await _fullyRenderApp(tester);

final html.Element element =
final web.Element element =
_getHtmlElementAtCenter(clickableWrappedButtonFinder, tester);

expect(element.id, 'background-html-view');
Expand All @@ -52,20 +47,18 @@ void main() {
testWidgets(
'on unwrapped elements, the browser hits the background-html-view',
(WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
await _fullyRenderApp(tester);

final html.Element element =
final web.Element element =
_getHtmlElementAtCenter(nonClickableButtonFinder, tester);

expect(element.id, 'background-html-view');
}, semanticsEnabled: false);

testWidgets('on background directly', (WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
await _fullyRenderApp(tester);

final html.Element element =
final web.Element element =
_getHtmlElementAt(tester.getTopLeft(backgroundFinder));

expect(element.id, 'background-html-view');
Expand All @@ -75,15 +68,9 @@ void main() {
group('With semantics', () {
testWidgets('finds semantics of wrapped widgets',
(WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
await _fullyRenderApp(tester);

if (!_newSemanticsAvailable()) {
print('Skipping test: Needs flutter > 2.10');
return;
}

final html.Element element =
final web.Element element =
_getHtmlElementAtCenter(clickableButtonFinder, tester);

expect(element.tagName.toLowerCase(), 'flt-semantics');
Expand All @@ -93,15 +80,9 @@ void main() {
testWidgets(
'finds semantics of wrapped widgets with intercepting set to false',
(WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();

if (!_newSemanticsAvailable()) {
print('Skipping test: Needs flutter > 2.10');
return;
}
await _fullyRenderApp(tester);

final html.Element element =
final web.Element element =
_getHtmlElementAtCenter(clickableWrappedButtonFinder, tester);

expect(element.tagName.toLowerCase(), 'flt-semantics');
Expand All @@ -111,15 +92,9 @@ void main() {

testWidgets('finds semantics of unwrapped elements',
(WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
await _fullyRenderApp(tester);

if (!_newSemanticsAvailable()) {
print('Skipping test: Needs flutter > 2.10');
return;
}

final html.Element element =
final web.Element element =
_getHtmlElementAtCenter(nonClickableButtonFinder, tester);

expect(element.tagName.toLowerCase(), 'flt-semantics');
Expand All @@ -134,20 +109,26 @@ void main() {
// simply allows the hit test to land on the platform view by making itself
// hit test transparent.
testWidgets('on background directly', (WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
await _fullyRenderApp(tester);

final html.Element element =
final web.Element element =
_getHtmlElementAt(tester.getTopLeft(backgroundFinder));

expect(element.id, 'background-html-view');
});
});
}

Future<void> _fullyRenderApp(WidgetTester tester) async {
await tester.pumpWidget(const app.MyApp());
// Pump 2 frames so the framework injects the platform view into the DOM.
await tester.pump();
await tester.pump();
}

// Calls [_getHtmlElementAt] passing it the center of the widget identified by
// the `finder`.
html.Element _getHtmlElementAtCenter(Finder finder, WidgetTester tester) {
web.Element _getHtmlElementAtCenter(Finder finder, WidgetTester tester) {
final Offset point = tester.getCenter(finder);
return _getHtmlElementAt(point);
}
Expand All @@ -158,22 +139,20 @@ html.Element _getHtmlElementAtCenter(Finder finder, WidgetTester tester) {
// sensitive to the presence of shadow roots and browser quirks (not all
// browsers agree on what it should return in all situations). Since this test
// runs only in Chromium, it relies on Chromium's behavior.
html.Element _getHtmlElementAt(Offset point) {
web.Element _getHtmlElementAt(Offset point) {
// Probe at the shadow so the browser reports semantics nodes in addition to
// platform view elements. If probed from `html.document` the browser hides
// the contents of <flt-glass-name> as an implementation detail.
final html.ShadowRoot glassPaneShadow =
html.document.querySelector('flt-glass-pane')!.shadowRoot!;
return glassPaneShadow.elementFromPoint(point.dx.toInt(), point.dy.toInt())!;
final web.ShadowRoot glassPaneShadow =
web.document.querySelector('flt-glass-pane')!.shadowRoot!;
// Use `round` below to ensure clicks always fall *inside* the located
// element, rather than truncating the decimals.
// Truncating decimals makes some tests fail when a centered element (in high
// DPI) is not exactly aligned to the pixel grid (because the browser *rounds*)
return glassPaneShadow.elementFromPoint(point.dx.round(), point.dy.round());
}

// TODO(dit): Remove this after flutter master (2.13) lands into stable.
// This detects that we can do new semantics assertions by looking at the 'id'
// attribute on flt-semantics elements (it is now set in 2.13 and up).
bool _newSemanticsAvailable() {
final html.ShadowRoot glassPaneShadow =
html.document.querySelector('flt-glass-pane')!.shadowRoot!;
final List<html.Element> elements =
glassPaneShadow.querySelectorAll('flt-semantics[id]');
return elements.isNotEmpty;
/// Shady API: https://github.com/w3c/csswg-drafts/issues/556
extension ElementFromPointInShadowRoot on web.ShadowRoot {
external web.Element elementFromPoint(int x, int y);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,51 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// ignore: avoid_web_libraries_in_flutter
import 'dart:html' as html;
import 'dart:js_interop';
import 'dart:ui_web' as ui_web;

import 'package:flutter/material.dart';
import 'package:pointer_interceptor_platform_interface/pointer_interceptor_platform_interface.dart';
import 'package:pointer_interceptor_web/pointer_interceptor_web.dart';
import 'package:web/web.dart' as web;

const String _htmlElementViewType = '_htmlElementViewType';
const double _videoWidth = 640;
const double _videoHeight = 480;
const double _containerWidth = 640;
const double _containerHeight = 480;

/// The html.Element that will be rendered underneath the flutter UI.
html.Element htmlElement = html.DivElement()
..style.width = '100%'
..style.height = '100%'
..style.backgroundColor = '#fabada'
..style.cursor = 'auto'
..id = 'background-html-view';
final web.Element _htmlElement =
(web.document.createElement('div') as web.HTMLDivElement)
..style.width = '100%'
..style.height = '100%'
..style.backgroundColor = '#fabada'
..style.cursor = 'auto'
..id = 'background-html-view';

// See other examples commented out below...

// html.Element htmlElement = html.VideoElement()
// ..style.width = '100%'
// ..style.height = '100%'
// ..style.cursor = 'auto'
// ..style.backgroundColor = 'black'
// ..id = 'background-html-view'
// ..src = 'https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4'
// ..poster = 'https://peach.blender.org/wp-content/uploads/title_anouncement.jpg?x11217'
// ..controls = true;

// html.Element htmlElement = html.IFrameElement()
// final web.Element _htmlElement =
// (web.document.createElement('video') as web.HTMLVideoElement)
// ..style.width = '100%'
// ..style.height = '100%'
// ..style.cursor = 'auto'
// ..style.backgroundColor = 'black'
// ..id = 'background-html-view'
// ..src =
// 'https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4'
// ..poster =
// 'https://peach.blender.org/wp-content/uploads/title_anouncement.jpg?x11217'
// ..controls = true;

// final web.Element _htmlElement =
// (web.document.createElement('video') as web.HTMLIFrameElement)
// ..width = '100%'
// ..height = '100%'
// ..id = 'background-html-view'
// ..src = 'https://www.youtube.com/embed/IyFZznAk69U'
// ..style.border = 'none';

void main() {
ui_web.platformViewRegistry.registerViewFactory(
_htmlElementViewType,
(int viewId) => htmlElement,
);
runApp(const MyApp());
}

Expand Down Expand Up @@ -81,6 +82,15 @@ class _MyHomePageState extends State<MyHomePage> {
});
}

@override
void initState() {
super.initState();
ui_web.platformViewRegistry.registerViewFactory(
_htmlElementViewType,
(int viewId) => _htmlElement,
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
Expand Down Expand Up @@ -109,13 +119,13 @@ class _MyHomePageState extends State<MyHomePage> {
),
Container(
color: Colors.black,
width: _videoWidth,
height: _videoHeight,
width: _containerWidth,
height: _containerHeight,
child: Stack(
alignment: Alignment.center,
children: <Widget>[
HtmlElement(
key: const ValueKey<String>('background-widget'),
key: const Key('background-widget'),
onClick: () {
_clickedOn('html-element');
},
Expand Down Expand Up @@ -207,9 +217,12 @@ class HtmlElement extends StatelessWidget {

@override
Widget build(BuildContext context) {
htmlElement.onClick.listen((_) {
onClick();
});
_htmlElement.addEventListener(
'click',
(JSAny? _) {
onClick();
}.toJS,
);

return const HtmlElementView(
viewType: _htmlElementViewType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ description: "Demonstrates how to use the pointer_interceptor_web plugin."
publish_to: 'none'

environment:
sdk: ">=3.1.0 <4.0.0"
flutter: ">=3.13.0"
sdk: ^3.2.0
flutter: '>=3.16.0'

dependencies:
cupertino_icons: ^1.0.2
Expand All @@ -13,6 +13,7 @@ dependencies:
pointer_interceptor_platform_interface: ^0.10.0
pointer_interceptor_web:
path: ../../pointer_interceptor_web
web: '>=0.3.0 <0.5.0'

dev_dependencies:
flutter_test:
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@
The path provided below has to start and end with a slash "/" in order for
it to work correctly.

Fore more details:
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base

This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="/">
<base href="$FLUTTER_BASE_HREF">

<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
Expand All @@ -31,18 +34,21 @@

<title>example</title>
<link rel="manifest" href="manifest.json">

<!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script>
</head>
<body>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('flutter-first-frame', function () {
navigator.serviceWorker.register('flutter_service_worker.js');
window.addEventListener('load', function(ev) {
// Download main.dart.js
_flutter.loader.loadEntrypoint({
onEntrypointLoaded: async function(engineInitializer) {
let appRunner = await engineInitializer.initializeEngine();
appRunner.runApp();
}
});
}
});
</script>
<script src="main.dart.js" type="application/javascript"></script>
</body>
</html>
Loading