【译】Flutter控制特定的屏幕方向
· 阅读需 3 分钟
原文《Controlling screen orientation in Flutter apps on a per-screen basis》,作者是Roman Petrov
设置屏幕方向
作者首先介绍了一个SystemChrome
全局类,通过调用下面的类方法可以设置整个应用的屏幕方向:
setPreferredOrientations(List<DeviceOrientation> orientations) → Future<void>
然后封装了一个方法来简化操作:
enum ScreenOrientation {
portraitOnly,
landscapeOnly,
rotating,
}
void _setOrientation(ScreenOrientation orientation) {
List<DeviceOrientation> orientations;
switch (orientation) {
case ScreenOrientation.portraitOnly:
orientations = [
DeviceOrientation.portraitUp,
];
break;
case ScreenOrientation.landscapeOnly:
orientations = [
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
];
break;
case ScreenOrientation.rotating:
orientations = [
DeviceOrientation.portraitUp,
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
];
break;
}
SystemChrome.setPreferredOrientations(orientations);
}
监听导航事件
但重点是要在Navigator
进行push
时设置屏幕方向,在pop
的时候重置为上一个界面的屏幕方向。可以通过自定义一个NavigatorObserver,实现didPop
和 didPush
两个方法来达到目的。
class NavigatorObserverWithOrientation extends NavigatorObserver {
void didPop(Route route, Route previousRoute) {
if (previousRoute.settings.arguments is ScreenOrientation) {
_setOrientation(previousRoute.settings.arguments);
} else {
// Portrait-only is the default option
_setOrientation(ScreenOrientation.portraitOnly);
}
}
void didPush(Route route, Route previousRoute) {
if (route.settings.arguments is ScreenOrientation) {
_setOrientation(route.settings.arguments);
} else {
_setOrientation(ScreenOrientation.portraitOnly);
}
}
}
注意:作者使用了RouteSettings 的
arguments
字段来存储屏幕方向,如果arguments
为null
或者不是ScreenOrientation
类型,比如自定义的值,那么只设置为portraitOnly
。
最后在生成路由的时候指定屏幕方向:
class AppRoutes {
static final home = "/";
static final portrait = "/portrait";
static final landscape = "/landscape";
static final rotating = "/rotating";
}
RouteSettings rotationSettings(RouteSettings settings, ScreenOrientation rotation) {
return RouteSettings(name: settings.name, arguments: rotation);
}
class MyApp extends StatelessWidget {
final _observer = NavigatorObserverWithOrientation();
Route<dynamic> _onGenerateRoute(RouteSettings settings) {
if (settings.name == AppRoutes.home) {
return MaterialPageRoute(builder: (context) => HomeScreen());
} else if (settings.name == AppRoutes.portrait) {
return MaterialPageRoute(builder: (context) => PortraitScreen());
} else if (settings.name == AppRoutes.landscape) {
return MaterialPageRoute(
builder: (context) => LandscapeScreen(),
settings: rotationSettings(settings, ScreenOrientation.landscapeOnly),
);
} else if (settings.name == AppRoutes.rotating) {
return MaterialPageRoute(
builder: (context) => RotatingScreen(),
settings: rotationSettings(settings, ScreenOrientation.rotating),
);
}
return null;
}
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Orientation Demo',
theme: ThemeData(primarySwatch: Colors.blue,),
onGenerateRoute: _onGenerateRoute,
navigatorObservers: [_observer],
);
}
}
总结
完整的工程在这里。作者最后提了一下,这个方案不是完美的,因为屏幕旋转时,路由转场动画也在同一时间发生,这可能导致一些意料之外的问题。但对作者来说,这已经 够了,符合他的 App 需求。