转载请标明出处: 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官方的最新更新
强制改变布局方向
既然 Flutter 提供的方法不能强制改变屏幕方向, 那么我们可以通过插件的形式, 桥接到iOS原生代码中, 通过原生方式改变屏幕方向.
设置应用支持的布局方向
通过Xcode打开Flutter项目中的iOS工程, 根据下图找到Device Orientation
这一项 勾选需要支持的布局方向, 通过这一步, 默认你现在的应用已经会根据设备的方向转变布局了
编写插件
展开 Runner
/Runner
文件夹 右键->New File 添加两个新的OC文件 FlutterIOSDevicePlugin.m
和 FlutterIOSDevicePlugin.h
(叫什么都没关系) 创建方式看下图:
FlutterIOSDevicePlugin.h
的内容:
#import
@interface FlutterIOSDevicePlugin : NSObject
+ (void)registerWithRegistrar:(NSObject*)registrar flutterViewController:(FlutterViewController*) controller;
- (instancetype)newInstance:(NSObject*)registrar flutterViewController:(FlutterViewController*) controller;
@end
复制代码
FlutterIOSDevicePlugin.m
的内容:
#import "FlutterIOSDevicePlugin.h"
@interface FlutterIOSDevicePlugin () {
NSObject *_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*)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*)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*)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 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 供大家快速接入. 如有高手写了相关库请告知我, 我会将它放到这篇文章中. 感谢