-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Description
Steps to Reproduce
- Execute
flutter run --web-renderer canvaskit -d chromeon the code sample - Verify that the actual and expected results (as described below) differ
- In the sample code: comment out the ìnsertion of the Stack item
const HtmlElementView(viewType: invisibleDivType)and run the sample app again - Verify that the result is as expected.
Expected results: The drawer-like overlay (just a Stack item positioned to the left) should display an orange box (an HtmlElement with a simple html div colored orange).
Actual results: The HtmlElement is not shown; it is actually hidden / covered by the a canvas element on which most of the
used framework widgets are drawn (i.e. the drawer like container and the app scaffold itself).
Please note that this issue only occurs when using the canvaskit renderer for the web.
What seems to go wrong
What seems to happen here is that canvaskit\embedded_views.dart does something wrong when the first embedded view it encounters is an 'invisible' one (by which I mean: a HtmlElementView created by a viewFactory registered with isVisible: false). This results in later HtmlElementViews that are visible not being correctly layered. As one of the attached screenshots shows, the platform view with the visible html is obfuscated by a canvas that draws what should be underneath it; i.e. the emitted scene structure is:
- flt-scene
- flt-canvas-container: on which nothing is drawn
- flt-platform-view-slot: for the invisible
HtmlElementView - flt-platform-view-slot: for the visible
HtmlElementView - flt-canvas-container: on which the scaffold and drawer are drawn
This is not a contrived situation, since PointerInterceptor does exactly that: it inserts an invisible HtmlElementView in order to ensure widgets remain responsive, even when overlayed over, let's say, an embedded iframe. Because we want drawers, modals, overlay entries, ... to be responsive irrespective of what's underneath them, we wrap them in a PointerInterceptor. But because of this issue, PointerInterceptor also disallows its child to embed a visible HtmlElement itself -- at least when there is no visible HtmlElement below it.
Otherwise put: we expect that our overlay entries can overlay embedded html view (and stay responsive); but we also expect that our overlay entries can embed html. And because of the current issue, we don't seem to be able to have both.
Code sample
// ignore_for_file: avoid_web_libraries_in_flutter, undefined_prefixed_name, avoid_dynamic_calls
import 'dart:html' as html;
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
const visibleDivType = 'visibleDiv';
const invisibleDivType = 'invisibleDiv';
const title = 'Flutter issue demo';
void main() {
ui.platformViewRegistry.registerViewFactory(
visibleDivType,
(int viewId) => html.DivElement()
..style.border = 'none'
..style.height = '100%'
..style.width = '100%'
..style.backgroundColor = 'orange'
..appendText('An embedded div'),
isVisible: true);
ui.platformViewRegistry.registerViewFactory(
invisibleDivType,
(int viewId) => html.DivElement()
..style.height = '100%'
..style.width = '100%'
..style.backgroundColor = 'green',
isVisible: false);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: title,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(title: const Text(title)),
body: const Home(),
));
}
}
class Home extends StatelessWidget {
const Home({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
children: [
const Center(),
// Please comment out in order to verify that it is this
// invisible HtmlElementView that causes the wrong layering:
const HtmlElementView(viewType: invisibleDivType),
Positioned(
child: SizedBox(
width: 400,
child: Container(
color: Colors.white,
child: Column(
mainAxisSize: MainAxisSize.max,
children: const [
Text(
'''A dummy drawer or any other type of overlay. One would expect to see an orange div appear below.'''),
SizedBox(
width: 100,
height: 40,
child: HtmlElementView(viewType: 'visibleDiv')),
],
)),
))
],
);
}
}Screenshots
The actual result:
The expected result after commenting out the invisible Html element:
The flt-scene with top canvas hidden:

