1. 程式人生 > 程式設計 >Flutter 控制螢幕旋轉的實現

Flutter 控制螢幕旋轉的實現

最近需要做個平板的專案,然後需要直接橫屏,有2種實現方式。

1,隨著螢幕旋轉,佈局自動調整。做橫豎屏適配

2,強制螢幕橫屏,不隨著螢幕去調整

第一種方式這裡就不做說明了。程式碼做適配就可以。 下面說一下第二種實現方式

Flutter 自帶方式

flutter 為我們提供了方法來控制系統的橫豎屏顯示

 SystemChrome.setPreferredOrientations([
   DeviceOrientation.landscapeLeft,DeviceOrientation.landscapeRight,DeviceOrientation.portraitUp,DeviceOrientation.portraitDown
  ]).then((_) {
  });

包含的方向型別。 !!!!但是但是但是這個方法只適用於android在iOS上沒有效果,上網查資料有大神封裝的 flutter外掛orientation,flutter外掛auto_orientation 在iOS上都起不到效果,所以打算自己寫一個原生檔案與flutter進行通訊,實現螢幕旋轉。也是筆者在查詢資料之後做的一個整合和解釋說明

iOS端實現螢幕旋轉

建立iOS原生檔案

建立iOS原生檔案來實現互動,首先要建立個原生檔案。命名為FlutterIOSDevicePlugin。 建立檔案

Flutter 控制螢幕旋轉的實現

建立一個命名為FlutterIOSDevicePlugin.h 和命名為FlutterIOSDevicePlugin.m檔案,說明一下: FlutterIOSDevicePlugin.h檔案選擇圖片裡面的header File模組建立即可。

建立之後,一起看一下FlutterIOSDevicePlugin.h裡面的程式碼

#ifndef FlutterIOSDevicePlugin_h
#define 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
#endif /* FlutterIOSDevicePlugin_h */

這個不需要過多說明了看一下FlutterIOSDevicePlugin.m的程式碼

#import <Foundation/Foundation.h>
#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];
    NSNumber *index = [NSNumber numberWithInt: [call.arguments[0] intValue]];
    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"];
//    [[NSNotificationCenter defaultCenter] postNotificationName:@"FlutterIOSDevicePlugin" object:nil userInfo:@{@"orientationMask": index}];
//    [UIViewController attemptRotationToDeviceOrientation];

    result(nil);
  } else {
    result(FlutterMethodNotImplemented);
  }
}
@end

以上是全部的程式碼,其中嘗試了用通知的方式去做螢幕旋轉,後來發現其實沒有那麼麻煩,直接採用的是

[[UIDevice currentDevice] setValue:@(iOSOrientation) forKey:@"orientation"];

方式做的。其中遇到的問題:

問題1:iphone手機上可以正常旋轉,ipad上不行

這個問題主要原因是info。plist檔案裡面的iphone和ipad不一致,且勾選Requires full screen 即可

Flutter 控制螢幕旋轉的實現

回到正軌哈

註冊原生外掛檔案

iOS 工程種有個GeneratedPluginRegistrant.m檔案,直接註冊就可以了。 如果你專案引入了其他第三方外掛,也是統一在這個地方註冊的

#import "GeneratedPluginRegistrant.h"

#if __has_include(<auto_orientation/AutoOrientationPlugin.h>)
#import <auto_orientation/AutoOrientationPlugin.h>
#else
@import auto_orientation;
#endif

#if __has_include(<orientation/OrientationPlugin.h>)
#import <orientation/OrientationPlugin.h>
#else
@import orientation;
#endif
#import "FlutterIOSDevicePlugin.h"

@implementation GeneratedPluginRegistrant

+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
 [AutoOrientationPlugin registerWithRegistrar:[registry registrarForPlugin:@"AutoOrientationPlugin"]];
 [OrientationPlugin registerWithRegistrar:[registry registrarForPlugin:@"OrientationPlugin"]];
//  [registry registrarForPlugin:@"FlutterIOSDevicePlugin"];
  

  [FlutterIOSDevicePlugin registerWithRegistrar: [registry registrarForPlugin:@"FlutterIOSDevicePlugin"]];
}

@end

程式碼中的這個片段是自己寫的外掛註冊的方法,其他的AutoOrientationPlugin,OrientationPlugin是引用第三方外掛自動生成的程式碼。

[FlutterIOSDevicePlugin registerWithRegistrar: [registry registrarForPlugin:@"FlutterIOSDevicePlugin"]];

AppDelegate檔案中的設定

import UIKit
import Flutter



@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
 var orientationMask: UIInterfaceOrientationMask = .all;
  
 override func application(
  _ application: UIApplication,didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
 ) -> Bool {
  NotificationCenter.default.addObserver(self,selector: #selector(changeLandscape(center:)),name:NSNotification.Name(rawValue: "FlutterIOSDevicePlugin"),object: nil)
  GeneratedPluginRegistrant.register(with: self);
  return super.application(application,didFinishLaunchingWithOptions: launchOptions)
 }
  override func application(_ application: UIApplication,supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
    return orientationMask;
  }
  
  @objc func changeLandscape(center: Notification){
    let index: NSNumber = (center.userInfo?["orientationMask"] ?? 5) as! NSNumber
    var mask : UIInterfaceOrientationMask = .all
    switch index {
    case 0:
      mask = .portrait
      break
    case 1:
      mask = .landscapeLeft
      break
    case 2:
      mask = .landscapeRight
      break
    case 3:
      mask = .portraitUpsideDown
      break
    case 4:
      mask = .landscape
      break
    case 5:
      mask = .all
      break
    case 6:
      mask = .allButUpsideDown
      break
    
    default:
      mask = .all
      break
    }
    orientationMask = mask;
    _ = application(UIApplication.shared,supportedInterfaceOrientationsFor: UIApplication.shared.keyWindow)
  }
}

其中changeLandscape方法是控制監聽的事件的方法,目前沒什麼用。 主要程式碼是這一段,這個是在發出螢幕旋轉的時候回撥的方法,這裡面設定的是全部的方向都可以。

  override func application(_ application: UIApplication,supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
    return orientationMask;
  }

問題2:程式不走該方法

具體原因是因為info.plist檔案問題。參考上面設定就沒問題

flutter 原生程式碼使用

這個地方是針對於iOS平臺去做的區別。

MethodChannel _channel = const MethodChannel('flutter_ios_device');

@override
 void initState() {
  SystemChrome.setPreferredOrientations([
   DeviceOrientation.landscapeLeft,DeviceOrientation.portraitDown
  ]).then((_) {
   if (Platform.isIOS) {
    this.changeScreenOrientation(DeviceOrientation.landscapeLeft);
   }
  });
  super.initState();
 }
 
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]);
 }

程式碼不用過多說明了, 有不瞭解的可以留言

問題3: 啟動程式自動旋轉

SystemChrome.setPreferredOrientations需要設定全部內容。不然會預設旋轉。有點小坑,

以上基本上可以實現螢幕旋轉的問題,如果有小夥伴還是不行,可以試一下用iOS原生通知的方式去完成。這個我做的是平板的專案,暫時沒有問題,更多相關Flutter 控制螢幕旋轉內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!