[web] Use platform detection from Flutter web engine. (#147346)

> [!IMPORTANT]
> Requires the following engine PR:
> * https://github.com/flutter/engine/pull/52380
> ----

This PR refactors Flutter `foundation`'s library `platform` for the web with the same code we use to detect platforms in the engine.

## Issues

* Fixes: https://github.com/flutter/flutter/issues/128943

## Testing

Demo app deployed here:

* https://dit-browser-detect.web.app
This commit is contained in:
David Iglesias 2024-05-07 15:44:56 -07:00 committed by GitHub
parent f20c853d20
commit d7656f2fcf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 68 additions and 37 deletions

View File

@ -4,7 +4,6 @@
import 'dart:ui_web' as ui_web;
import '../web.dart' as web;
import 'platform.dart' as platform;
export 'platform.dart' show TargetPlatform;
@ -19,46 +18,45 @@ platform.TargetPlatform get defaultTargetPlatform {
_browserPlatform;
}
final platform.TargetPlatform? _testPlatform = () {
platform.TargetPlatform? result;
// The TargetPlatform used on Web tests, unless overridden.
//
// Respects the `ui_web.browser.debugOperatingSystemOverride` value (when set).
platform.TargetPlatform? get _testPlatform {
platform.TargetPlatform? testPlatform;
assert(() {
if (ui_web.debugEmulateFlutterTesterEnvironment) {
result = platform.TargetPlatform.android;
// Return the overridden operatingSystem in tests, if any...
if (ui_web.browser.debugOperatingSystemOverride != null) {
testPlatform =
_operatingSystemToTargetPlatform(ui_web.browser.operatingSystem);
} else {
// Fall back to `android` for tests.
testPlatform = platform.TargetPlatform.android;
}
}
return true;
}());
return result;
}();
return testPlatform;
}
// Lazy-initialized and forever cached current browser platform.
// Current browser platform.
//
// Computing the platform is expensive as it uses `window.matchMedia`, which
// needs to parse and evaluate a CSS selector. On some devices this takes up to
// 0.20ms. As `defaultTargetPlatform` is routinely called dozens of times per
// frame this value should be cached.
final platform.TargetPlatform _browserPlatform = () {
final String navigatorPlatform = web.window.navigator.platform.toLowerCase();
if (navigatorPlatform.startsWith('mac')) {
return platform.TargetPlatform.macOS;
}
if (navigatorPlatform.startsWith('win')) {
return platform.TargetPlatform.windows;
}
if (navigatorPlatform.contains('iphone') ||
navigatorPlatform.contains('ipad') ||
navigatorPlatform.contains('ipod')) {
return platform.TargetPlatform.iOS;
}
if (navigatorPlatform.contains('android')) {
return platform.TargetPlatform.android;
}
// Since some phones can report a window.navigator.platform as Linux, fall
// back to use CSS to disambiguate Android vs Linux desktop. If the CSS
// indicates that a device has a "fine pointer" (mouse) as the primary
// pointing device, then we'll assume desktop linux, and otherwise we'll
// assume Android.
if (web.window.matchMedia('only screen and (pointer: fine)').matches) {
return platform.TargetPlatform.linux;
}
return platform.TargetPlatform.android;
}();
// The computation of `operatingSystem` is cached in the ui_web package;
// this getter may be called dozens of times per frame.
//
// _browserPlatform is lazily initialized, and cached forever.
final platform.TargetPlatform _browserPlatform =
_operatingSystemToTargetPlatform(ui_web.browser.operatingSystem);
// Converts an ui_web.OperatingSystem enum into a platform.TargetPlatform.
platform.TargetPlatform _operatingSystemToTargetPlatform(ui_web.OperatingSystem os) {
return switch (os) {
ui_web.OperatingSystem.android => platform.TargetPlatform.android,
ui_web.OperatingSystem.iOs => platform.TargetPlatform.iOS,
ui_web.OperatingSystem.linux => platform.TargetPlatform.linux,
ui_web.OperatingSystem.macOs => platform.TargetPlatform.macOS,
ui_web.OperatingSystem.windows => platform.TargetPlatform.windows,
// Resolve 'unknown' OS values to `android`.
ui_web.OperatingSystem.unknown => platform.TargetPlatform.android,
};
}

View File

@ -0,0 +1,33 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@TestOn('chrome')
library;
import 'dart:ui_web' as ui_web;
import 'package:flutter/foundation.dart' show TargetPlatform, defaultTargetPlatform;
import 'package:flutter_test/flutter_test.dart';
void main() {
tearDown(() {
// Remove the `debugOperatingSystemOverride`.
ui_web.browser.debugOperatingSystemOverride = null;
});
group('defaultTargetPlatform', () {
testWidgets('returns what ui_web says', (WidgetTester _) async {
// Set the OS reported by web_ui to anything that is not linux.
ui_web.browser.debugOperatingSystemOverride = ui_web.OperatingSystem.iOs;
expect(defaultTargetPlatform, TargetPlatform.iOS);
});
testWidgets('defaults `unknown` to android', (WidgetTester _) async {
ui_web.browser.debugOperatingSystemOverride = ui_web.OperatingSystem.unknown;
expect(defaultTargetPlatform, TargetPlatform.android);
});
});
}