Skip to content

[web][iOS] Full-page Flutter view keeps a stale height when the browser toolbar collapses #193743

Closed

Description

@flutter-zl

Steps to reproduce

  1. Create an app with flutter create, replace lib/main.dart with the code sample below, and add the following <style> block to the <head> of web/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>
  2. Build with flutter build web and open the app in Safari or Chrome on an iPhone.

  3. 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

  1. self-assigned this
    on Oct 2, 2026
  2. added
    team-webOwned by Web platform team
    P1High-priority issues at the top of the work list
    triaged-webTriaged by Web platform team
    and removed
    P1High-priority issues at the top of the work list
    team-webOwned by Web platform team
    on Oct 2, 2026
  3. added a commit that references this issue on Oct 8, 2026
    12b69d6
  4. github-actions commented on Oct 22, 2026

    @github-actions

    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 -v and a minimal reproduction of the issue.

  5. locked as resolved and limited conversation to collaborators on Oct 22, 2026
  6. added a commit that references this issue on Oct 29, 2026
    f643128
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P1High-priority issues at the top of the work listteam-webOwned by Web platform teamtriaged-webTriaged by Web platform team

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions