Skip to content

Commit

Permalink
[web] ScreenOrientation singleton (#45304)
Browse files Browse the repository at this point in the history
One more piece moving out of `FlutterViewEmbedder`.

`ScreenOrientation` is a singleton class that can be used to control the screen orientation of the browser.

Part of flutter/flutter#134443
  • Loading branch information
mdebbar authored and harryterkelsen committed Oct 23, 2023
1 parent 01e7ba1 commit 7a5ed32
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 80 deletions.
75 changes: 75 additions & 0 deletions lib/web_ui/lib/src/engine/display.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';

import 'package:ui/ui.dart' as ui;

import '../engine.dart';
Expand Down Expand Up @@ -59,3 +61,76 @@ class EngineFlutterDisplay extends ui.Display {

double? _debugDevicePixelRatioOverride;
}

/// Controls the screen orientation using the browser's screen orientation API.
class ScreenOrientation {
const ScreenOrientation();

static ScreenOrientation get instance => _instance;
static const ScreenOrientation _instance = ScreenOrientation();

static const String lockTypeAny = 'any';
static const String lockTypeNatural = 'natural';
static const String lockTypeLandscape = 'landscape';
static const String lockTypePortrait = 'portrait';
static const String lockTypePortraitPrimary = 'portrait-primary';
static const String lockTypePortraitSecondary = 'portrait-secondary';
static const String lockTypeLandscapePrimary = 'landscape-primary';
static const String lockTypeLandscapeSecondary = 'landscape-secondary';

/// Sets preferred screen orientation.
///
/// Specifies the set of orientations the application interface can be
/// displayed in.
///
/// The [orientations] argument is a list of DeviceOrientation values.
/// The empty list uses Screen unlock api and causes the application to
/// defer to the operating system default.
///
/// See w3c screen api: https://www.w3.org/TR/screen-orientation/
Future<bool> setPreferredOrientation(List<dynamic> orientations) async {
final DomScreen? screen = domWindow.screen;
if (screen != null) {
final DomScreenOrientation? screenOrientation = screen.orientation;
if (screenOrientation != null) {
if (orientations.isEmpty) {
screenOrientation.unlock();
return true;
} else {
final String? lockType =
_deviceOrientationToLockType(orientations.first as String?);
if (lockType != null) {
try {
await screenOrientation.lock(lockType);
return true;
} catch (_) {
// On Chrome desktop an error with 'not supported on this device
// error' is fired.
return Future<bool>.value(false);
}
}
}
}
}
// API is not supported on this browser return false.
return false;
}

// Converts device orientation to w3c OrientationLockType enum.
//
// See also: https://developer.mozilla.org/en-US/docs/Web/API/ScreenOrientation/lock
static String? _deviceOrientationToLockType(String? deviceOrientation) {
switch (deviceOrientation) {
case 'DeviceOrientation.portraitUp':
return lockTypePortraitPrimary;
case 'DeviceOrientation.portraitDown':
return lockTypePortraitSecondary;
case 'DeviceOrientation.landscapeLeft':
return lockTypeLandscapePrimary;
case 'DeviceOrientation.landscapeRight':
return lockTypeLandscapeSecondary;
default:
return null;
}
}
}
74 changes: 0 additions & 74 deletions lib/web_ui/lib/src/engine/embedder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';

import 'package:ui/src/engine/safe_browser_api.dart';
import 'package:ui/ui.dart' as ui;

Expand Down Expand Up @@ -288,78 +286,6 @@ class FlutterViewEmbedder {
}
}

static const String orientationLockTypeAny = 'any';
static const String orientationLockTypeNatural = 'natural';
static const String orientationLockTypeLandscape = 'landscape';
static const String orientationLockTypePortrait = 'portrait';
static const String orientationLockTypePortraitPrimary = 'portrait-primary';
static const String orientationLockTypePortraitSecondary =
'portrait-secondary';
static const String orientationLockTypeLandscapePrimary = 'landscape-primary';
static const String orientationLockTypeLandscapeSecondary =
'landscape-secondary';

/// Sets preferred screen orientation.
///
/// Specifies the set of orientations the application interface can be
/// displayed in.
///
/// The [orientations] argument is a list of DeviceOrientation values.
/// The empty list uses Screen unlock api and causes the application to
/// defer to the operating system default.
///
/// See w3c screen api: https://www.w3.org/TR/screen-orientation/
Future<bool> setPreferredOrientation(List<dynamic> orientations) {
final DomScreen? screen = domWindow.screen;
if (screen != null) {
final DomScreenOrientation? screenOrientation = screen.orientation;
if (screenOrientation != null) {
if (orientations.isEmpty) {
screenOrientation.unlock();
return Future<bool>.value(true);
} else {
final String? lockType =
_deviceOrientationToLockType(orientations.first as String?);
if (lockType != null) {
final Completer<bool> completer = Completer<bool>();
try {
screenOrientation.lock(lockType).then((dynamic _) {
completer.complete(true);
}).catchError((dynamic error) {
// On Chrome desktop an error with 'not supported on this device
// error' is fired.
completer.complete(false);
});
} catch (_) {
return Future<bool>.value(false);
}
return completer.future;
}
}
}
}
// API is not supported on this browser return false.
return Future<bool>.value(false);
}

// Converts device orientation to w3c OrientationLockType enum.
//
// See also: https://developer.mozilla.org/en-US/docs/Web/API/ScreenOrientation/lock
static String? _deviceOrientationToLockType(String? deviceOrientation) {
switch (deviceOrientation) {
case 'DeviceOrientation.portraitUp':
return orientationLockTypePortraitPrimary;
case 'DeviceOrientation.portraitDown':
return orientationLockTypePortraitSecondary;
case 'DeviceOrientation.landscapeLeft':
return orientationLockTypeLandscapePrimary;
case 'DeviceOrientation.landscapeRight':
return orientationLockTypeLandscapeSecondary;
default:
return null;
}
}

/// Add an element as a global resource to be referenced by CSS.
///
/// This call create a global resource host element on demand and either
Expand Down
2 changes: 1 addition & 1 deletion lib/web_ui/lib/src/engine/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
return;
case 'SystemChrome.setPreferredOrientations':
final List<dynamic> arguments = decoded.arguments as List<dynamic>;
flutterViewEmbedder.setPreferredOrientation(arguments).then((bool success) {
ScreenOrientation.instance.setPreferredOrientation(arguments).then((bool success) {
replyToPlatformMessage(
callback, codec.encodeSuccessEnvelope(success));
});
Expand Down
10 changes: 5 additions & 5 deletions lib/web_ui/test/engine/window_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -358,25 +358,25 @@ Future<void> testMain() async {
unlockCount = 0;

expect(await sendSetPreferredOrientations(<dynamic>['DeviceOrientation.portraitUp']), isTrue);
expect(lockCalls, <String>[FlutterViewEmbedder.orientationLockTypePortraitPrimary]);
expect(lockCalls, <String>[ScreenOrientation.lockTypePortraitPrimary]);
expect(unlockCount, 0);
lockCalls.clear();
unlockCount = 0;

expect(await sendSetPreferredOrientations(<dynamic>['DeviceOrientation.portraitDown']), isTrue);
expect(lockCalls, <String>[FlutterViewEmbedder.orientationLockTypePortraitSecondary]);
expect(lockCalls, <String>[ScreenOrientation.lockTypePortraitSecondary]);
expect(unlockCount, 0);
lockCalls.clear();
unlockCount = 0;

expect(await sendSetPreferredOrientations(<dynamic>['DeviceOrientation.landscapeLeft']), isTrue);
expect(lockCalls, <String>[FlutterViewEmbedder.orientationLockTypeLandscapePrimary]);
expect(lockCalls, <String>[ScreenOrientation.lockTypeLandscapePrimary]);
expect(unlockCount, 0);
lockCalls.clear();
unlockCount = 0;

expect(await sendSetPreferredOrientations(<dynamic>['DeviceOrientation.landscapeRight']), isTrue);
expect(lockCalls, <String>[FlutterViewEmbedder.orientationLockTypeLandscapeSecondary]);
expect(lockCalls, <String>[ScreenOrientation.lockTypeLandscapeSecondary]);
expect(unlockCount, 0);
lockCalls.clear();
unlockCount = 0;
Expand All @@ -389,7 +389,7 @@ Future<void> testMain() async {

simulateError = true;
expect(await sendSetPreferredOrientations(<dynamic>['DeviceOrientation.portraitDown']), isFalse);
expect(lockCalls, <String>[FlutterViewEmbedder.orientationLockTypePortraitSecondary]);
expect(lockCalls, <String>[ScreenOrientation.lockTypePortraitSecondary]);
expect(unlockCount, 0);

js_util.setProperty(domWindow, 'screen', original);
Expand Down

0 comments on commit 7a5ed32

Please sign in to comment.