Repository navigation
Description
Steps to reproduce
-
Create an app with
flutter create, replacelib/main.dartwith the code sample below, and add the following<style>block to the<head>ofweb/index.html. The CSS lets the page scroll, which is what makes iOS collapse the browser toolbar. Nothing else in the app changes.<style> /* Let the page scroll, so iOS collapses its toolbar when you swipe. html overflow stops body's overflow: hidden from applying to the page. */ html { overflow: auto; } body { touch-action: pan-y !important; } html::after { content: ""; display: block; height: 1000vh; } </style>
-
Build with
flutter build weband open the app in Safari or Chrome on an iPhone. -
Swipe up until the browser toolbar collapses, then stop.
Deployed repro: https://flutter-demo-71-before.web.app
Expected results
The Flutter view grows to fill the area the toolbar gave up, as it does on Android and desktop.
Actual results
The Flutter view keeps its toolbar-expanded height. The page behind it shows through as a white strip at the bottom of the screen.
| Browser, toolbar collapsed | Flutter view height | window.innerHeight |
visualViewport.height |
documentElement.clientHeight |
|---|---|---|---|---|
| Safari, iOS 27 | 775 | 815 | 815 | 775 |
| Chrome, iOS 27 | 745 | 853 | 853 | 745 |
With the toolbar expanded, all four values match and there is no strip.
Note: in Chrome on iOS, swiping all the way to the end of the page can also shift the whole page up, revealing white below. That is a separate Chrome behavior with position: fixed content, it happens with the current engine too, and it is not this bug. Stop swiping once the toolbar collapses.
Cause
On iOS, FullPageDimensionsProvider.computePhysicalSize in lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/full_page_dimensions_provider.dart sizes the view from documentElement.clientHeight instead of visualViewport.height. That branch was added in flutter-team-archive/engine#25895 to fix #81430, where Chrome on iOS reported a wrong visualViewport.height after rotating.
When the toolbar collapses because the page scrolled, iOS grows innerHeight and visualViewport.height but leaves documentElement.clientHeight at the toolbar-expanded value, so the view never grows.
computeKeyboardInsets has the same iOS branch: it reports the keyboard inset as the view height minus documentElement.clientHeight. Growing only the view height turns the difference into a fake keyboard inset, 40px in Safari and 108px in Chrome, and Scaffold shrinks its body for a keyboard that is not there. Both functions need the same height.
Collapsing the toolbar from Safari's page menu with Hide Toolbar does not reproduce this, because that path also updates documentElement.clientHeight. A default Flutter app cannot scroll the page, so today the bug only shows once the page itself scrolls, which is what #69529 asks for. Any fix for #69529, such as the approach discussed in #184281, runs into it.
Code sample
Code sample
The package:web calls only feed the on-screen readout. The bug reproduces without them.
import 'dart:js_interop';
import 'package:flutter/material.dart';
import 'package:web/web.dart' as web;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(home: HomePage());
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
late final JSFunction _onResize = ((web.Event _) => setState(() {})).toJS;
@override
void initState() {
super.initState();
web.window.addEventListener('resize', _onResize);
web.window.visualViewport?.addEventListener('resize', _onResize);
}
@override
void dispose() {
web.window.removeEventListener('resize', _onResize);
web.window.visualViewport?.removeEventListener('resize', _onResize);
super.dispose();
}
@override
Widget build(BuildContext context) {
final double flutterHeight = MediaQuery.sizeOf(context).height;
final double insetBottom = MediaQuery.viewInsetsOf(context).bottom;
final double innerHeight = web.window.innerHeight.toDouble();
final double visualHeight =
web.window.visualViewport?.height.toDouble() ?? -1;
final double docHeight =
web.document.documentElement?.clientHeight.toDouble() ?? -1;
return Scaffold(
backgroundColor: Colors.lightBlue.shade100,
appBar: AppBar(title: const Text('iOS toolbar view size')),
body: Column(
children: <Widget>[
Container(
width: double.infinity,
color: Colors.white,
padding: const EdgeInsets.all(8),
child: Text(
'Flutter view height: ${flutterHeight.toStringAsFixed(0)}\n'
'window.innerHeight: ${innerHeight.toStringAsFixed(0)}\n'
'visualViewport.height: ${visualHeight.toStringAsFixed(0)}\n'
'documentElement.clientHeight: ${docHeight.toStringAsFixed(0)}\n'
'viewInsets.bottom: ${insetBottom.toStringAsFixed(0)}',
),
),
Expanded(
child: ListView.builder(
itemCount: 100,
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text('Item $index'));
},
),
),
],
),
);
}
}Screenshots or Video
Screenshots
Screenshots attached below: Safari with the toolbar collapsed, Safari with the toolbar expanded, and Chrome with the toolbar collapsed.
Logs
Logs
No console errors. The readout values in the table above are the evidence.Flutter Doctor output
Doctor output
Flutter master at 9c931498d505ba8d3ce0c1f033333afa7d445553, dated 2026-09-29, building with `flutter build web`.Reproduced on Flutter master 9c931498d5, iPhone, iOS 27, Safari 27 and Chrome for iOS.
Activity
[web] Grow the iOS view when the browser toolbar collapses (flutter#1…
github-actions commentedon Oct 22, 2026
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of
flutter doctor -vand a minimal reproduction of the issue.[web] Grow the iOS view when the browser toolbar collapses (flutter#1…