轉載請標明出處: juejin.im/post/5c68da…
本文出自:Wos的主頁
我此刻的Flutter版本:
Flutter 1.2.0 • channel dev • github.com/flutter/flu…
Framework • revision 06b979c4d5 (3 weeks ago) • 2019-01-25 14:27:35 -0500
Engine • revision 36acd02c94
Tools • Dart 2.1.1 (build 2.1.1-dev.3.2 f4afaee422)
特定頁面旋轉螢幕很簡單:
SystemChrome.setPreferredOrientations([
...
]);
複製程式碼
陣列中是您要支援的螢幕方向.
如果想在特定頁面固定橫屏, 您可以這樣寫:
@override
void initState() {
super.initState();
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeRight,
DeviceOrientation.landscapeRight,
]);
}
複製程式碼
並且在dispose
時更改回豎屏
@override
void dispose() {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
super.dispose();
}
複製程式碼
但是!!! 不要走開 本文重點在下面
在Android裝置上, 呼叫此方法可以強制改變螢幕方向. 但在iOS上卻不是這樣
對於iOS, 這個方法表示設定應用支援的螢幕方向, 只有在物理方向改變時才會改變螢幕方向
現在看起來, 這應該是一個Flutter的一個Bug. 有待官方解決
您可關注 issue #13238 追蹤Flutter官方的最新更新
強制改變佈局方向
2019-03-15 更新:
發現已經有大佬將下面的方法封裝成了package (點此跳轉到orientation).
我沒有嘗試過, 大家可以優先嚐試一下這個庫.
既然 Flutter 提供的方法不能強制改變螢幕方向, 那麼我們可以通過外掛的形式, 橋接到iOS原生程式碼中, 通過原生方式改變螢幕方向.
設定應用支援的佈局方向
通過Xcode開啟Flutter專案中的iOS工程, 根據下圖找到Device Orientation
這一項
勾選需要支援的佈局方向, 通過這一步, 預設你現在的應用已經會根據裝置的方向轉變佈局了
編寫外掛
展開 Runner
/Runner
資料夾 右鍵->New File 新增兩個新的OC檔案
FlutterIOSDevicePlugin.m
和 FlutterIOSDevicePlugin.h
(叫什麼都沒關係) 建立方式看下圖:
FlutterIOSDevicePlugin.h
的內容:
#import <Flutter/Flutter.h>
@interface FlutterIOSDevicePlugin : NSObject<FlutterPlugin>
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar flutterViewController:(FlutterViewController*) controller;
- (instancetype)newInstance:(NSObject<FlutterPluginRegistrar>*)registrar flutterViewController:(FlutterViewController*) controller;
@end
複製程式碼
FlutterIOSDevicePlugin.m
的內容:
#import "FlutterIOSDevicePlugin.h"
@interface FlutterIOSDevicePlugin () {
NSObject<FlutterPluginRegistrar> *_registrar;
FlutterViewController *_controller;
}
@end
static NSString* const CHANNEL_NAME = @"flutter_ios_device";
static NSString* const METHOD_CHANGE_ORIENTATION = @"change_screen_orientation";
static NSString* const ORIENTATION_PORTRAIT_UP = @"portraitUp";
static NSString* const ORIENTATION_PORTRAIT_DOWN = @"portraitDown";
static NSString* const ORIENTATION_LANDSCAPE_LEFT = @"landscapeLeft";
static NSString* const ORIENTATION_LANDSCAPE_RIGHT = @"landscapeRight";
@implementation FlutterIOSDevicePlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
FlutterMethodChannel* channel = [FlutterMethodChannel
methodChannelWithName:CHANNEL_NAME
binaryMessenger:[registrar messenger]];
FlutterIOSDevicePlugin* instance = [[FlutterIOSDevicePlugin alloc] newInstance:registrar flutterViewController:nil];
[registrar addMethodCallDelegate:instance channel:channel];
}
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar flutterViewController:(FlutterViewController*) controller {
FlutterMethodChannel* channel = [FlutterMethodChannel
methodChannelWithName:CHANNEL_NAME
binaryMessenger:[registrar messenger]];
FlutterIOSDevicePlugin* instance = [[FlutterIOSDevicePlugin alloc] newInstance:registrar flutterViewController:controller];
[registrar addMethodCallDelegate:instance channel:channel];
}
- (instancetype)newInstance:(NSObject<FlutterPluginRegistrar>*)registrar flutterViewController:(FlutterViewController*) controller{
_registrar = registrar;
_controller = controller;
return self;
}
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
if ([METHOD_CHANGE_ORIENTATION isEqualToString:call.method]) {
NSArray *arguments = call.arguments;
NSString *orientation = arguments[0];
NSInteger iOSOrientation;
if ([orientation isEqualToString:ORIENTATION_LANDSCAPE_LEFT]){
iOSOrientation = UIDeviceOrientationLandscapeLeft;
}else if([orientation isEqualToString:ORIENTATION_LANDSCAPE_RIGHT]){
iOSOrientation = UIDeviceOrientationLandscapeRight;
}else if ([orientation isEqualToString:ORIENTATION_PORTRAIT_DOWN]){
iOSOrientation = UIDeviceOrientationPortraitUpsideDown;
}else{
iOSOrientation = UIDeviceOrientationPortrait;
}
[[UIDevice currentDevice] setValue:@(iOSOrientation) forKey:@"orientation"];
result(nil);
} else {
result(FlutterMethodNotImplemented);
}
}
@end
複製程式碼
註冊外掛
開啟AppDelegate.m
在 didFinishLaunchingWithOptions
方法中註冊外掛
#include "FlutterIOSDevicePlugin.h"
...
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
...
// flutter: Device Plugin
[FlutterIOSDevicePlugin registerWithRegistrar:[self registrarForPlugin:@"FlutterIOSDevicePlugin"] flutterViewController:controller];
}
複製程式碼
使用外掛
import 'package:flutter/services.dart';
MethodChannel _channel = const MethodChannel('flutter_ios_device');
@override
void initState() {
super.initState();
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
]).then((_) {
if (Platform.isIOS) {
iOSDevicePlugin.changeScreenOrientation(DeviceOrientation.landscapeLeft);
}
});
}
@override
void dispose() {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]).then((_) {
if (Platform.isIOS) {
iOSDevicePlugin.changeScreenOrientation(DeviceOrientation.portraitUp);
}
});
super.dispose();
}
Future<void> changeScreenOrientation(DeviceOrientation orientation) {
String o;
switch (orientation) {
case DeviceOrientation.portraitUp:
o = 'portraitUp';
break;
case DeviceOrientation.portraitDown:
o = 'portraitDown';
break;
case DeviceOrientation.landscapeLeft:
o = 'landscapeLeft';
break;
case DeviceOrientation.landscapeRight:
o = 'landscapeRight';
break;
}
return _channel.invokeMethod('change_screen_orientation', [o]);
}
複製程式碼
到此, 我們的工作基本完成. 可以強制某些特定頁面改變佈局方向.
還沒有結束
在實踐中, 我發現上面這樣的做法會導致一個問題.
如果只想讓特定的頁面可以改變方向(橫屏), 其它頁面一直保持豎屏該怎麼辦?
"圖一" 中, 我們設定了 iOS 的 Device Orientation 只要裝置方向改變了, 佈局就會改變.
現在, 根據圖一的步驟將 Device Orientation 改為 僅 Portrait
修改 AppDelegate.h
, 加入 isLandscape
這個屬性
@interface AppDelegate : FlutterAppDelegate
@property (nonatomic,assign)BOOL isLandscape;
@end
複製程式碼
在 AppDelegate.m
中加入下列方法
// 是否允許橫屏
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(nullable UIWindow *)window{
if (self.isLandscape) {
return UIInterfaceOrientationMaskAllButUpsideDown;
}
return UIInterfaceOrientationMaskPortrait;
}
複製程式碼
修改 FlutterIOSDevicePlugin.m
#import "AppDelegate.h"
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
...
if ([orientation isEqualToString:ORIENTATION_LANDSCAPE_LEFT]){
iOSOrientation = UIDeviceOrientationLandscapeLeft;
((AppDelegate *)[UIApplication sharedApplication].delegate).isLandscape = YES;
}else if([orientation isEqualToString:ORIENTATION_LANDSCAPE_RIGHT]){
iOSOrientation = UIDeviceOrientationLandscapeRight;
((AppDelegate *)[UIApplication sharedApplication].delegate).isLandscape = YES;
}else if ([orientation isEqualToString:ORIENTATION_PORTRAIT_DOWN]){
iOSOrientation = UIDeviceOrientationPortraitUpsideDown;
((AppDelegate *)[UIApplication sharedApplication].delegate).isLandscape = NO;
}else{
iOSOrientation = UIDeviceOrientationPortrait;
((AppDelegate *)[UIApplication sharedApplication].delegate).isLandscape = NO;
}
...
}
複製程式碼
完成
參考:
SystemChrome.setPreferredOrientations does not force the device to the given orientations until the device is physically rotated #13238
iOS關於橫屏的有關問題
ios啟動頁強制豎屏(進入App後允許橫屏與豎屏)
我對iOS知之甚少, 以上解決方案全憑網上的資料彙總, 暫時沒有能力提供 flutter plugin 供大家快速接入. 如有高手寫了相關庫請告知我, 我會將它放到這篇文章中. 感謝