Flutter:如何按需设置和锁定屏幕方向

177
在我的 Flutter 页面中,我需要将屏幕设置为横屏模式并锁定,使其无法旋转成竖屏模式,但仅限于该页面。因此需要一种方法来实时启用此功能。有人知道如何做到这一点吗?
我希望它能够旋转成左横屏或右横屏,但不要旋转成竖屏模式。
14个回答

313

首先导入服务包:

import 'package:flutter/services.dart';

这将使您可以访问SystemChrome类,它"控制操作系统图形界面的特定方面以及它如何与应用程序交互。"

当您加载小部件时,请执行以下操作:

@override
void initState(){
  super.initState();
  SystemChrome.setPreferredOrientations([
      DeviceOrientation.landscapeRight,
      DeviceOrientation.landscapeLeft,
  ]);
}

然后当我离开这个页面时,将它恢复为正常状态,像这样:

@override
dispose(){
  SystemChrome.setPreferredOrientations([
    DeviceOrientation.landscapeRight,
    DeviceOrientation.landscapeLeft,
    DeviceOrientation.portraitUp,
    DeviceOrientation.portraitDown,
  ]);
  super.dispose();
}

2
但似乎只在Android上工作 https://github.com/flutter/flutter/issues/20124 - Luigino De Togni
哦,我没意识到。这很有可能,我只在安卓上测试过。 - Jus10
没关系,这个问题可以通过平台通道来解决 https://github.com/flutter/flutter/issues/13238#issuecomment-435958221 ,我认为它很快就会被修复。 - Luigino De Togni
45
已解决的问题是数月前修复的,现在此解决方案也可在 iOS 上使用。 - Pablo Insua
4
从我看到的情况来看,在dispose方法中设置首选方向不是一个好主意。如果您导航到另一个路由并打算返回,则不会调用dispose方法。 - Rowan Gontier
显示剩余5条评论

45

我会使用一个简单的mixin锁定手机为竖屏模式。以下解决方案可以将整个应用程序锁定为竖屏模式,或者将特定的屏幕设置为竖屏模式,同时保持其他地方的旋转。

import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';

/// Forces portrait-only mode application-wide
/// Use this Mixin on the main app widget i.e. app.dart
/// Flutter's 'App' has to extend Stateless widget.
///
/// Call `super.build(context)` in the main build() method
/// to enable portrait only mode
mixin PortraitModeMixin on StatelessWidget {
  @override
  Widget build(BuildContext context) {
    _portraitModeOnly();
    return null;
  }
}

/// Forces portrait-only mode on a specific screen
/// Use this Mixin in the specific screen you want to
/// block to portrait only mode.
///
/// Call `super.build(context)` in the State's build() method
/// and `super.dispose();` in the State's dispose() method
mixin PortraitStatefulModeMixin<T extends StatefulWidget> on State<T> {
  @override
  Widget build(BuildContext context) {
    _portraitModeOnly();
    return null;
  }

  @override
  void dispose() {
    _enableRotation();
    super.dispose();
  }
}

/// blocks rotation; sets orientation to: portrait
void _portraitModeOnly() {
  SystemChrome.setPreferredOrientations([
    DeviceOrientation.portraitUp,
    DeviceOrientation.portraitDown,
  ]);
}

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

为了在整个应用程序中阻止旋转,请在主要的App小部件中实现PortraitModeMixin。请记住在Widget build(BuildContext context)方法中调用super.build(context)
/// Main App widget
class App extends StatelessWidget with PortraitModeMixin {
  const App();

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return CupertinoApp(
      title: 'Flutter Demo',
      theme: CupertinoThemeData(),
      home: Text("Block screen rotation example"),
    );
  }
}

要在特定屏幕中阻止旋转,请在该屏幕的状态中实现“PortraitStatefulModeMixin”。请记住,在状态的“build()”方法中调用“super.build(context)”并在“dispose()”方法中调用“super.dispose()”。如果您的屏幕是一个“StatelessWidget”,则只需重复应用程序的解决方案(前面的示例),即使用“PortraitModeMixin”。保留HTML标签。
/// Specific screen
class SampleScreen extends StatefulWidget {
  SampleScreen() : super();

  @override
  State<StatefulWidget> createState() => _SampleScreenState();
}

class _SampleScreenState extends State<SampleScreen>
    with PortraitStatefulModeMixin<SampleScreen> {
  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Text("Flutter - Block screen rotation example");
  }

  @override
  void dispose() {
     super.dispose();
  }
}

使用这种语法的混合类在Dart 2.1中可用。

2
我已经尝试了你的mixin类 - 无状态版本运行良好,但是有状态版本在我的有状态类调用其disposed方法时会抛出异常。请注意,我在我的类中调用了super.dispose()。以下是错误信息: I/flutter (29686): 在构建小部件树的最终阶段,发生了以下断言错误: I/flutter (29686): _MultiPlayerAcceptPageState.dispose failed to call super.dispose. I/flutter (29686): dispose()实现必须始终调用它们的超类dispose()方法,以确保小部件使用的所有资源都被完全释放。 - Angel Todorov
1
状态版本也出现了同样的异常。你解决了这个问题吗? - cwhisperer
1
解决了!如果你使用的是Android Studio,这个问题很容易被发现。它会提示dispose()被注释为@mustCallSuper。这意味着你需要在mixin函数本身中添加super.dispose() - Chirag Arora
在mixin中使用return null;会抛出以下错误:无法从具有返回类型'Widget'的方法'build'返回类型为'Null'的值。 - cwhisperer
1
这是一个非常棒的答案,尽管现在有点过时了。在Flutter的新版本中,将return null替换为return super.build(context);,它就可以正常工作了。 - its_broke_again
有没有办法在运行在iOS或Android上的应用程序中实现这个功能? 我猜"SystemChrome"只适用于网页? - Janaka

40

首先,将整个应用程序的方向锁定为纵向模式。

//Do this in main.dart
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
  .then((_) {
runApp(MyApp());
});

其次,进入您想要更改方向的特定屏幕。

@override
void initState() {
super.initState();

SystemChrome.setPreferredOrientations([
  DeviceOrientation.portraitUp,
  DeviceOrientation.landscapeRight,
  DeviceOrientation.landscapeLeft
]);

}

@override
void dispose() {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
super.dispose();
}

要使用SystemChrome,您需要添加'package:flutter/services.dart'


1
无状态小部件的情况下该怎么办? - p2kr
2
你需要一个有状态的小部件才能访问生命周期方法。 - Anthony O
我的应用程序通常在竖屏上运行。当我从主屏幕切换到横屏屏幕时,我已经设置为横屏模式。当我按返回键时,它会首先在横屏屏幕模式下加载主屏幕,然后再构建为竖屏。这发生在不到一秒的时间内。 - Nifal Nizar
没有在iOS 16上的任何影响 - 有什么想法吗? - iOS Flow

21
void main() {
  WidgetsFlutterBinding.ensureInitialized();
  SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown])
    .then((_) {
      runApp(new MyApp());
    });
}

5
虽然这段代码可能提供了解决问题的方法,但最好添加说明其为何/如何有效的上下文信息。这能够帮助未来的用户学习,并将该知识应用于自己的代码中。当代码得到解释时,你也很可能会收到用户的积极反馈,表现为点赞。 - borchvm

7
导入 services.dart 包并添加以下代码以将设备方向锁定为 portraitUp 模式:
 import 'package:flutter/services.dart';

 main() {
     WidgetsFlutterBinding.ensureInitialized();
     SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
     runApp(MyHomePage());
 }

1
WidgetsFlutterBinding.ensureInitialized(); 这个方法让我的解决方案起作用了。很好的发现! - AWP

5
有时由于方向的空信息,它可能无法工作。 您可以像这样简单使用:
import services.dart


void main() {
    SystemChrome.setPreferredOrientations(
    [DeviceOrientation.portraitUp]
     )
        .then((_) {
          runApp(new MyApp());
        });
    }

// 等待应用程序初始化后设置屏幕方向,然后锁定方向


5

iOS重要提示

  • 在info.plist文件中启用方向。例如:enter image description here

步骤

  • 在main.dart文件中设置方向。在我的情况下,我的应用程序仅支持纵向,除了一个屏幕需要横向模式。例如:
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp,DeviceOrientation.portraitDown,]);
  • 在需要旋转的屏幕中添加以下代码。
  void initState() {
    super.initState();
    SystemChrome.setPreferredOrientations([
      DeviceOrientation.landscapeRight,
      DeviceOrientation.landscapeLeft,
    ]);
  }
  @override
  dispose(){
     SystemChrome.setPreferredOrientations([
        DeviceOrientation.portraitUp,
        DeviceOrientation.portraitDown,
      ]);
    super.dispose();
  }

4

简单的方法来锁定整个应用程序的屏幕方向

  • main.dart文件开头添加import 'package:flutter/services.dart';

  • MyApp类的Widget构建区域中,在return部分之前创建SystemChrome.setPreferredOrientations();方法来禁止屏幕旋转。

  • 使用[DeviceOrientation.<orientation-type>] 将方向指定为方法参数。

<orientation-type>位置上使用以下之一:

  1. portraitUp
  2. portraitDown
  3. landscapeLeft
  4. landscapeRight

示例代码:

import 'package:flutter/material.dart';
 
import 'package:flutter/services.dart' ;
 
void main() => runApp(MyApp());
 
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
 
      SystemChrome.setPreferredOrientations([
        DeviceOrientation.portraitUp,
      ]);
 
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
        title: Text("Screen Orientation"),
        ),
        body: Container(
        ),
      ),
    );
  }
}

3

导入services.dart,您的void main函数应该是这样的:

void main(){

    WidgetsFlutterBinding.ensureInitialized();
    SystemChrome.setPreferredOrientations(
       [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown])
       .then((_){
           runApp(MyApp());
       }
    );
}

2

在主文件夹中的AndroidManifest.xml文件中,找到activity标签并设置android:screenOrientation="portrait"。

<activity android:windowSoftInputMode="adjustResize" android:screenOrientation = "portrait">

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接