Skip to content
98 changes: 98 additions & 0 deletions lib/src/render_webview.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';

class RenderWebview extends LeafRenderObjectWidget {
const RenderWebview({
Key? key,
required this.textureId,
required this.filterQuality,
required this.onSizeChanged,
}) : super(key: key);

final int textureId;
final FilterQuality filterQuality;
final void Function(Size) onSizeChanged;

@override
RenderObject createRenderObject(BuildContext context) {
return WebviewBox(
textureId: textureId,
filterQuality: filterQuality,
onSizeChanged: onSizeChanged);
}

@override
void updateRenderObject(BuildContext context, WebviewBox renderObject) {
renderObject.textureId = textureId;
renderObject.filterQuality = filterQuality;
renderObject.onSizeChanged = onSizeChanged;
}
}

class WebviewBox extends RenderBox {
WebviewBox({
required int textureId,
FilterQuality filterQuality = FilterQuality.low,
required this.onSizeChanged,
}) : _textureId = textureId,
_filterQuality = filterQuality;

void Function(Size) onSizeChanged;

Size? _lastNotifiedSize;

int get textureId => _textureId;
int _textureId;
set textureId(int value) {
if (value != _textureId) {
_textureId = value;
markNeedsPaint();
}
}

FilterQuality get filterQuality => _filterQuality;
FilterQuality _filterQuality;
set filterQuality(FilterQuality value) {
if (value != _filterQuality) {
_filterQuality = value;
markNeedsPaint();
}
}

@override
bool get sizedByParent => true;

@override
bool get alwaysNeedsCompositing => true;

@override
bool get isRepaintBoundary => true;

@override
@protected
Size computeDryLayout(covariant BoxConstraints constraints) {
return constraints.biggest;
}

@override
void performLayout() {
if (_lastNotifiedSize != size) {
_lastNotifiedSize = size;
onSizeChanged(size);
}
}

@override
bool hitTestSelf(Offset position) => true;

@override
void paint(PaintingContext context, Offset offset) {
context.addLayer(
TextureLayer(
rect: Rect.fromLTWH(offset.dx, offset.dy, size.width, size.height),
textureId: _textureId,
filterQuality: _filterQuality,
),
);
}
}
211 changes: 104 additions & 107 deletions lib/src/webview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:webview_windows/src/render_webview.dart';

import 'cursor.dart';
import 'enums.dart';
Expand Down Expand Up @@ -625,7 +626,6 @@ class Webview extends StatefulWidget {
}

class _WebviewState extends State<Webview> {
final GlobalKey _key = GlobalKey();
final _downButtons = <int, PointerButton>{};

PointerDeviceKind _pointerKind = PointerDeviceKind.unknown;
Expand All @@ -636,6 +636,8 @@ class _WebviewState extends State<Webview> {

StreamSubscription? _cursorSubscription;

int _updateSizeRequestId = 0;

@override
void initState() {
super.initState();
Expand All @@ -644,9 +646,6 @@ class _WebviewState extends State<Webview> {
// remove this line
_controller._permissionRequested = widget.permissionRequested;

// Report initial surface size
WidgetsBinding.instance.addPostFrameCallback((_) => _reportSurfaceSize());

_cursorSubscription = _controller._cursor.listen((cursor) {
setState(() {
_cursor = cursor;
Expand All @@ -656,114 +655,112 @@ class _WebviewState extends State<Webview> {

@override
Widget build(BuildContext context) {
return (widget.height != null && widget.width != null)
? SizedBox(
key: _key,
width: widget.width,
height: widget.height,
child: _buildInner())
: SizedBox.expand(key: _key, child: _buildInner());
return SizedBox(
width: widget.width ?? double.infinity,
height: widget.height ?? double.infinity,
child: _buildInner());
}

Widget _buildInner() {
return NotificationListener<SizeChangedLayoutNotification>(
onNotification: (notification) {
_reportSurfaceSize();
return true;
if (!_controller.value.isInitialized) {
return const SizedBox.shrink();
}

return Listener(
onPointerHover: (ev) {
// ev.kind is for whatever reason not set to touch
// even on touch input
if (_pointerKind == PointerDeviceKind.touch) {
// Ignoring hover events on touch for now
return;
}
_controller._setCursorPos(ev.localPosition);
},
onPointerDown: (ev) {
_pointerKind = ev.kind;
if (ev.kind == PointerDeviceKind.touch) {
_controller._setPointerUpdate(WebviewPointerEventKind.down,
ev.pointer, ev.localPosition, ev.size, ev.pressure);
return;
}
final button = getButton(ev.buttons);
_downButtons[ev.pointer] = button;
_controller._setPointerButtonState(button, true);
},
onPointerUp: (ev) {
_pointerKind = ev.kind;
if (ev.kind == PointerDeviceKind.touch) {
_controller._setPointerUpdate(WebviewPointerEventKind.up,
ev.pointer, ev.localPosition, ev.size, ev.pressure);
return;
}
final button = _downButtons.remove(ev.pointer);
if (button != null) {
_controller._setPointerButtonState(button, false);
}
},
child: SizeChangedLayoutNotifier(
child: _controller.value.isInitialized
? Listener(
onPointerHover: (ev) {
// ev.kind is for whatever reason not set to touch
// even on touch input
if (_pointerKind == PointerDeviceKind.touch) {
// Ignoring hover events on touch for now
return;
}
_controller._setCursorPos(ev.localPosition);
},
onPointerDown: (ev) {
_pointerKind = ev.kind;
if (ev.kind == PointerDeviceKind.touch) {
_controller._setPointerUpdate(
WebviewPointerEventKind.down,
ev.pointer,
ev.localPosition,
ev.size,
ev.pressure);
return;
}
final button = getButton(ev.buttons);
_downButtons[ev.pointer] = button;
_controller._setPointerButtonState(button, true);
},
onPointerUp: (ev) {
_pointerKind = ev.kind;
if (ev.kind == PointerDeviceKind.touch) {
_controller._setPointerUpdate(
WebviewPointerEventKind.up,
ev.pointer,
ev.localPosition,
ev.size,
ev.pressure);
return;
}
final button = _downButtons.remove(ev.pointer);
if (button != null) {
_controller._setPointerButtonState(button, false);
}
},
onPointerCancel: (ev) {
_pointerKind = ev.kind;
final button = _downButtons.remove(ev.pointer);
if (button != null) {
_controller._setPointerButtonState(button, false);
}
},
onPointerMove: (ev) {
_pointerKind = ev.kind;
if (ev.kind == PointerDeviceKind.touch) {
_controller._setPointerUpdate(
WebviewPointerEventKind.update,
ev.pointer,
ev.localPosition,
ev.size,
ev.pressure);
} else {
_controller._setCursorPos(ev.localPosition);
}
},
onPointerSignal: (signal) {
if (signal is PointerScrollEvent) {
_controller._setScrollDelta(
-signal.scrollDelta.dx, -signal.scrollDelta.dy);
}
},
onPointerPanZoomUpdate: (signal) {
if (signal.panDelta.dx.abs() > signal.panDelta.dy.abs()) {
_controller._setScrollDelta(-signal.panDelta.dx, 0);
} else {
_controller._setScrollDelta(0, signal.panDelta.dy);
}
},
child: MouseRegion(
cursor: _cursor,
child: Texture(
textureId: _controller._textureId,
filterQuality: widget.filterQuality,
)),
)
: const SizedBox()));
}

void _reportSurfaceSize() async {
final box = _key.currentContext?.findRenderObject() as RenderBox?;
if (box != null) {
await _controller.ready;
unawaited(_controller._setSize(
box.size, widget.scaleFactor ?? window.devicePixelRatio));
onPointerCancel: (ev) {
_pointerKind = ev.kind;
final button = _downButtons.remove(ev.pointer);
if (button != null) {
_controller._setPointerButtonState(button, false);
}
},
onPointerMove: (ev) {
_pointerKind = ev.kind;
if (ev.kind == PointerDeviceKind.touch) {
_controller._setPointerUpdate(WebviewPointerEventKind.update,
ev.pointer, ev.localPosition, ev.size, ev.pressure);
} else {
_controller._setCursorPos(ev.localPosition);
}
},
onPointerSignal: (signal) {
if (signal is PointerScrollEvent) {
_controller._setScrollDelta(
-signal.scrollDelta.dx, -signal.scrollDelta.dy);
}
},
onPointerPanZoomUpdate: (signal) {
if (signal.panDelta.dx.abs() > signal.panDelta.dy.abs()) {
_controller._setScrollDelta(-signal.panDelta.dx, 0);
} else {
_controller._setScrollDelta(0, signal.panDelta.dy);
}
},
child: MouseRegion(
cursor: _cursor,
child: RenderWebview(
textureId: _controller._textureId,
filterQuality: widget.filterQuality,
onSizeChanged: _updateSurfaceSize,
),
));
}

void _updateSurfaceSize(Size size) {
_updateSizeRequestId++;

if (!_controller._creatingCompleter.isCompleted) {
final requestId = _updateSizeRequestId;

_controller.ready.then((_) {
if (!mounted) {
return;
}

if (requestId != _updateSizeRequestId) {
return;
}

_updateSurfaceSize(size);
});
}

_controller._setSize(
size,
widget.scaleFactor ?? window.devicePixelRatio,
);
}

@override
Expand Down
Loading