Summary
On Flutter web, TextCapitalization.none (and the other values) are silently dropped between the framework and the engine. The hidden <input class="flt-text-editing"> element that the engine creates ends up without any autocapitalize attribute at all, so the platform IME falls back to its default. On Chromebook OSK this means the first letter is always auto-capitalized regardless of what the developer passes on the Dart side, diverging from iOS behavior.
Related (but separate): the Chromebook suggestion strip also appears
even with enableSuggestions: false. After investigation that turned out
to be a Gboard policy issue, not an attribute-level issue — Gboard ignores
autocomplete="off" / spellcheck="false" regardless of who sets them.
Already filed and closed as
flutter/flutter#182535.
This issue is scoped to the attribute-level bug only.
Steps to reproduce
Live demo (no setup): https://bvg-gynzy.github.io/flutter_web_input_attrs_poc/
— open this URL on a Chromebook (with the on-screen keyboard enabled) to see
the bug end-to-end, or on desktop Chrome to verify the attribute-level
mis-configuration via DevTools.
Full source: https://github.com/BvG-Gynzy/flutter_web_input_attrs_poc
-
Create a Flutter web app with a TextField configured to opt out of
auto-capitalization:
TextField(textCapitalization: TextCapitalization.none)
-
Run on Chrome: flutter run -d chrome.
-
Focus the text field and inspect the hidden editing element
<input class="flt-text-editing"> in DevTools (Elements panel, or
via document.activeElement in the Console with a focusin listener
since clicking in DevTools blurs the field).
-
(Optional, to observe the user-facing symptom) Run on a Chromebook
with the on-screen keyboard enabled (Settings → Accessibility →
Keyboard → Enable on-screen keyboard) and type a single character
without pressing Shift.
Expected results
The hidden editing element should have autocapitalize="none".
On a Chromebook OSK / Gboard, the first character typed should be
lowercase — matching iOS Safari behavior.
Actual results
On Flutter 3.41.7 the hidden editing element has no autocapitalize
attribute at all (getAttribute('autocapitalize') returns null).
On a Chromebook OSK the first character is auto-capitalized despite
TextCapitalization.none. iOS Safari with the same code behaves
correctly because of platform-specific engine handling (see Root
cause).
Desktop Chrome with a hardware keyboard shows the same missing
attribute in DevTools but does not reproduce the visible symptom,
because autocapitalize only affects virtual keyboards per the HTML
spec.
Root cause
All references below are to
engine/src/flutter/lib/web_ui/lib/src/engine/text_editing/text_editing.dart
in Flutter 3.41.7 (engine revision 7a53c052bc).
The implementation exists, but only iOS and Android strategies call it
TextCapitalizationConfig.setAutocapitalizeAttribute in
text_capitalization.dart:60 correctly maps TextCapitalization.none
→ "off", .sentences → "sentences", etc. The mapping is fine. The
problem is who calls it:
| Line |
Strategy |
Calls setAutocapitalizeAttribute? |
| 1312 |
DefaultTextEditingStrategy.initializeTextEditing (base) |
❌ no |
| 1759 |
IOSTextEditingStrategy.initializeTextEditing |
✅ yes |
| 1915 |
AndroidTextEditingStrategy.initializeTextEditing |
✅ yes |
| — |
GloballyPositionedTextEditingStrategy (inherits default; default for Chrome OS / Chromebook) |
❌ no |
| — |
SafariDesktopTextEditingStrategy |
❌ no |
| — |
FirefoxTextEditingStrategy |
❌ no |
createDefaultTextEditingStrategy (line 2092) routes by platform:
if (iOS) → IOSTextEditingStrategy // calls it
else if (android) → AndroidTextEditingStrategy // calls it
else if (webkit) → SafariDesktopTextEditingStrategy // does NOT
else if (firefox) → FirefoxTextEditingStrategy // does NOT
else → GloballyPositionedTextEditingStrategy // does NOT
Why Chromebook lands in the broken bucket — UA detection trace
Chrome OS User Agent (Chrome 137):
Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36
Walking through browser_detection.dart:
detectBrowserEngineByVendorAgent (line 108):
navigator.vendor === "Google Inc." → BrowserEngine.blink.
detectOperatingSystem (line 150): navigator.platform on Chrome OS
is "Linux x86_64" (the CrOS token is in the UA, not in
navigator.platform). userAgent.contains('Android') is false.
platform.startsWith('Linux') → returns OperatingSystem.linux.
Result: (BrowserEngine.blink, OperatingSystem.linux) → strategy
selector falls through to GloballyPositionedTextEditingStrategy. The
engine treats Chrome OS as plain Linux desktop and assumes no IME
configuration is needed.
Proposed fix
Two layers — both small and independent.
Layer 1 — apply the attribute from the base strategy. In
DefaultTextEditingStrategy.applyConfiguration, after the autocorrect
line (1373):
config.textCapitalization.setAutocapitalizeAttribute(activeDomElement);
Once the base writes autocapitalize, the iOS and Android-specific
overrides become redundant and can be deleted in the same patch.
Layer 2 — detect Chrome OS as a virtual-keyboard platform. In
detectOperatingSystem (browser_detection.dart:150), Chrome OS can
be identified by userAgent.contains('CrOS') before falling into the
Linux branch. Either:
- Add
OperatingSystem.chromeOS and route it through
AndroidTextEditingStrategy in createDefaultTextEditingStrategy
(cleanest, but adds enum surface), or
- Introduce a more general
hasOnScreenKeyboard predicate (true for
iOS, Android, Chrome OS) and use it inside the strategy selector.
Layer 1 alone fixes the immediate attribute leak. Layer 2 is needed
for any other IME-related code that branches on operatingSystem and
currently makes the wrong call on Chromebook.
Code sample
A full runnable repository is available at
https://github.com/BvG-Gynzy/flutter_web_input_attrs_poc. The minimal
inline sample below is sufficient to demonstrate the bug:
Code sample
import 'package:flutter/material.dart';
void main() => runApp(const MaterialApp(home: Page()));
class Page extends StatelessWidget {
const Page({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Padding(
padding: EdgeInsets.all(24),
// Expect autocapitalize="none" on the editing <input>.
// Actual: getAttribute('autocapitalize') returns null.
child: TextField(textCapitalization: TextCapitalization.none),
),
);
}
}
Screenshots or Video
OSK is not included when making screenshot on ChromeOS. Screenshot below shows that attributes are not correctly set on input element in Chrome on ChromeOS.
Screenshots / Video demonstration
Logs
Logs
[No exception is thrown; the attribute is simply not written.
No log output is produced by this bug.]
Flutter Doctor output
Doctor output
[✓] Flutter (Channel stable, 3.41.7, on macOS 26.4.1 25E253 darwin-arm64, locale en-NL)
• Flutter version 3.41.7 on channel stable
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision cc0734ac71 (6 weeks ago), 2026-04-15 21:21:08 -0700
• Engine revision 59aa584fdf
• Dart version 3.11.5
• DevTools version 2.54.2
[!] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
✗ Flutter requires Android SDK 36 and the Android BuildTools 28.0.3
! Some Android licenses not accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 26.5)
• Build 17F42
• CocoaPods version 1.16.2
[✓] Chrome - develop for the web
[✓] Connected device (2 available)
• macOS (desktop) • macos • darwin-arm64 • macOS 26.4.1 25E253 darwin-arm64
• Chrome (web) • chrome • web-javascript • Google Chrome 148.0.7778.179
[✓] Network resources
• All expected network resources are available.
Symptoms reproduce on Chrome OS (Chromebook, Chrome 137):
Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36
Summary
On Flutter web,
TextCapitalization.none(and the other values) are silently dropped between the framework and the engine. The hidden<input class="flt-text-editing">element that the engine creates ends up without anyautocapitalizeattribute at all, so the platform IME falls back to its default. On Chromebook OSK this means the first letter is always auto-capitalized regardless of what the developer passes on the Dart side, diverging from iOS behavior.Steps to reproduce
Live demo (no setup): https://bvg-gynzy.github.io/flutter_web_input_attrs_poc/
— open this URL on a Chromebook (with the on-screen keyboard enabled) to see
the bug end-to-end, or on desktop Chrome to verify the attribute-level
mis-configuration via DevTools.
Full source: https://github.com/BvG-Gynzy/flutter_web_input_attrs_poc
Create a Flutter web app with a
TextFieldconfigured to opt out ofauto-capitalization:
Run on Chrome:
flutter run -d chrome.Focus the text field and inspect the hidden editing element
<input class="flt-text-editing">in DevTools (Elements panel, orvia
document.activeElementin the Console with afocusinlistenersince clicking in DevTools blurs the field).
(Optional, to observe the user-facing symptom) Run on a Chromebook
with the on-screen keyboard enabled (Settings → Accessibility →
Keyboard → Enable on-screen keyboard) and type a single character
without pressing Shift.
Expected results
The hidden editing element should have
autocapitalize="none".On a Chromebook OSK / Gboard, the first character typed should be
lowercase — matching iOS Safari behavior.
Actual results
On Flutter 3.41.7 the hidden editing element has no
autocapitalizeattribute at all (
getAttribute('autocapitalize')returnsnull).On a Chromebook OSK the first character is auto-capitalized despite
TextCapitalization.none. iOS Safari with the same code behavescorrectly because of platform-specific engine handling (see Root
cause).
Desktop Chrome with a hardware keyboard shows the same missing
attribute in DevTools but does not reproduce the visible symptom,
because
autocapitalizeonly affects virtual keyboards per the HTMLspec.
Root cause
All references below are to
engine/src/flutter/lib/web_ui/lib/src/engine/text_editing/text_editing.dartin Flutter 3.41.7 (engine revision
7a53c052bc).The implementation exists, but only iOS and Android strategies call it
TextCapitalizationConfig.setAutocapitalizeAttributeintext_capitalization.dart:60correctly mapsTextCapitalization.none→
"off",.sentences→"sentences", etc. The mapping is fine. Theproblem is who calls it:
setAutocapitalizeAttribute?DefaultTextEditingStrategy.initializeTextEditing(base)IOSTextEditingStrategy.initializeTextEditingAndroidTextEditingStrategy.initializeTextEditingGloballyPositionedTextEditingStrategy(inherits default; default for Chrome OS / Chromebook)SafariDesktopTextEditingStrategyFirefoxTextEditingStrategycreateDefaultTextEditingStrategy(line 2092) routes by platform:Why Chromebook lands in the broken bucket — UA detection trace
Chrome OS User Agent (Chrome 137):
Walking through
browser_detection.dart:detectBrowserEngineByVendorAgent(line 108):navigator.vendor === "Google Inc."→BrowserEngine.blink.detectOperatingSystem(line 150):navigator.platformon Chrome OSis
"Linux x86_64"(theCrOStoken is in the UA, not innavigator.platform).userAgent.contains('Android')is false.platform.startsWith('Linux')→ returnsOperatingSystem.linux.Result:
(BrowserEngine.blink, OperatingSystem.linux)→ strategyselector falls through to
GloballyPositionedTextEditingStrategy. Theengine treats Chrome OS as plain Linux desktop and assumes no IME
configuration is needed.
Proposed fix
Two layers — both small and independent.
Layer 1 — apply the attribute from the base strategy. In
DefaultTextEditingStrategy.applyConfiguration, after theautocorrectline (1373):
config.textCapitalization.setAutocapitalizeAttribute(activeDomElement);Once the base writes
autocapitalize, the iOS and Android-specificoverrides become redundant and can be deleted in the same patch.
Layer 2 — detect Chrome OS as a virtual-keyboard platform. In
detectOperatingSystem(browser_detection.dart:150), Chrome OS canbe identified by
userAgent.contains('CrOS')before falling into theLinuxbranch. Either:OperatingSystem.chromeOSand route it throughAndroidTextEditingStrategyincreateDefaultTextEditingStrategy(cleanest, but adds enum surface), or
hasOnScreenKeyboardpredicate (true foriOS, Android, Chrome OS) and use it inside the strategy selector.
Layer 1 alone fixes the immediate attribute leak. Layer 2 is needed
for any other IME-related code that branches on
operatingSystemandcurrently makes the wrong call on Chromebook.
Code sample
A full runnable repository is available at
https://github.com/BvG-Gynzy/flutter_web_input_attrs_poc. The minimal
inline sample below is sufficient to demonstrate the bug:
Code sample
Screenshots or Video
OSK is not included when making screenshot on ChromeOS. Screenshot below shows that attributes are not correctly set on input element in Chrome on ChromeOS.
Screenshots / Video demonstration
Logs
Logs
Flutter Doctor output
Doctor output
Symptoms reproduce on Chrome OS (Chromebook, Chrome 137):