Environment
| |
|
| webview_all |
1.2.1 |
| webview_all_windows |
1.2.1 |
| Flutter |
3.44.7 (stable), Dart 3.12.2 |
| OS |
Windows 11 Pro 26200 (x64) |
| WebView2 Runtime |
150.0.4078.105 |
What I'm doing
I migrated a Flutter desktop app from webview_flutter to webview_all to get Windows support. The app shows a web page in a modal dialog for an OAuth sign-in flow: the dialog is opened, the user completes the flow, the dialog closes. This can happen several times per app session (e.g. the user cancels and retries).
The controller is created per dialog, using only the standard webview_all API:
import 'package:flutter/material.dart';
import 'package:webview_all/webview_all.dart';
void main() => runApp(const MaterialApp(home: Home()));
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) => Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () => showDialog(
context: context,
builder: (_) => const WebViewDialog(),
),
child: const Text('open webview dialog'),
),
),
);
}
class WebViewDialog extends StatefulWidget {
const WebViewDialog({super.key});
@override
State<WebViewDialog> createState() => _WebViewDialogState();
}
class _WebViewDialogState extends State<WebViewDialog> {
late final WebViewController _controller;
@override
void initState() {
super.initState();
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..loadRequest(Uri.parse('https://example.com'));
}
@override
Widget build(BuildContext context) => Dialog(
child: SizedBox(
width: 520,
height: 700,
child: WebViewWidget(controller: _controller),
),
);
}
Steps to reproduce
- Run the app on Windows.
- Open the dialog, then close it. Repeat 5 times.
- With every dialog closed, count the app's WebView2 renderer processes. They can be attributed by the default user data dir (
%LOCALAPPDATA%\webview_all_windows\<exe-stem>):
Get-CimInstance Win32_Process -Filter "Name='msedgewebview2.exe'" |
Where-Object { $_.CommandLine -like '*webview_all_windows*' -and
$_.CommandLine -like '*--type=renderer*' } |
Measure-Object | Select-Object -ExpandProperty Count
Expected
Renderer count drops back to 0 once the dialogs are closed and the WebViewWidgets are removed from the tree.
Actual
The renderer count equals the number of times the dialog was opened, and stays there until the process exits. After 5 open/close cycles:
Type PID WS_MB Start
browser(root) 40468 150.9 11:03:48
gpu-process 40408 113.7 11:03:48
utility 40128 43.1 11:03:48
utility 40100 19.5 11:03:48
crashpad-handler 8896 14.0 11:03:48
renderer 8744 62.2 11:03:48 <- open #1
renderer 41668 63.7 11:03:54 <- open #2
renderer 41872 62.8 11:03:56 <- open #3
renderer 40352 62.7 11:03:58 <- open #4
renderer 41480 60.5 11:03:59 <- open #5
All five renderers are still alive with no dialog open — about 312 MB retained on top of the ~341 MB shared browser/GPU/utility processes. The browser root process is a child of the Flutter app process, and the user data dir confirms the attribution.
Each renderer's start time lines up with a dialog open, and none of them exit when the dialog closes.
On app exit, the Chromium module also logs:
[ERROR:ui\gfx\win\window_impl.cc:172] Failed to unregister class Chrome_WidgetWin_0. Error = 1412
(1412 = ERROR_CLASS_HAS_WINDOWS, i.e. windows of that class were still alive at teardown.)
Environment
What I'm doing
I migrated a Flutter desktop app from
webview_fluttertowebview_allto get Windows support. The app shows a web page in a modal dialog for an OAuth sign-in flow: the dialog is opened, the user completes the flow, the dialog closes. This can happen several times per app session (e.g. the user cancels and retries).The controller is created per dialog, using only the standard
webview_allAPI:Steps to reproduce
%LOCALAPPDATA%\webview_all_windows\<exe-stem>):Expected
Renderer count drops back to 0 once the dialogs are closed and the
WebViewWidgets are removed from the tree.Actual
The renderer count equals the number of times the dialog was opened, and stays there until the process exits. After 5 open/close cycles:
All five renderers are still alive with no dialog open — about 312 MB retained on top of the ~341 MB shared browser/GPU/utility processes. The browser root process is a child of the Flutter app process, and the user data dir confirms the attribution.
Each renderer's start time lines up with a dialog open, and none of them exit when the dialog closes.
On app exit, the Chromium module also logs:
(
1412=ERROR_CLASS_HAS_WINDOWS, i.e. windows of that class were still alive at teardown.)