我一直在做一个项目,该项目需要我根据移动设备的orientation
.
例如,在portrait
方向的情况下,我需要显示一个小部件(比如说纵向小部件),而在landscape
方向的情况下,我需要显示另一个小部件(我们称之为横向小部件)。
我用过OrientationBuilder
实际问题:在方向更改时,所有Dialogs
和OptionMenu
或任何其他 Popup 类型的 Widget 都没有关闭。如何在方向更改时关闭它们?
重现问题的步骤:
- 运行下面给出的应用程序代码 [重现问题的示例应用程序]
- 长按应用程序主体以查看对话框或单击
OptionMenu
左上角 - 更改设备方向,您会看到小部件主体已根据方向更改,但
Popuped
小部件仍然可见。
注意:请注意,我需要针对此问题的全局解决方案。仅针对此代码提供解决方案对我没有任何用处。这是我为更好地理解问题而提供的示例代码,我根本不使用此代码。
示例应用代码:
// main.dart
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return OrientationBuilder(builder: (context, orientation) {
bool isLandscape = orientation == Orientation.landscape;
return isLandscape ? Landscape() : Portrait();
});
}
}
class Portrait extends StatefulWidget {
@override
_PortraitState createState() => _PortraitState();
}
class _PortraitState extends State<Portrait> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: buildTitle(),
actions: <Widget>[_buildOptionMenu(context)],
),
body: GestureDetector(
onLongPress: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
content: buildTitle(),
),
);
},
child: Container(
color: Colors.blue.withOpacity(0.4),
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Center(
child: buildTitle(),
),
],
),
),
),
);
}
Widget _buildOptionMenu(BuildContext context) {
return PopupMenuButton(itemBuilder: (context) {
var list = <String>['Portrait-Item-1', 'Portrait-Item-2'];
return list
.map<PopupMenuEntry<String>>(
(e) => PopupMenuItem<String>(
child: Text(e),
),
)
.toList();
});
}
Text buildTitle() => Text('Portrait');
}
class Landscape extends StatefulWidget {
@override
_LandscapeState createState() => _LandscapeState();
}
class _LandscapeState extends State<Landscape> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: buildTitle(),
actions: <Widget>[_buildOptionMenu(context)],
),
body: GestureDetector(
onLongPress: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
content: buildTitle(),
),
);
},
child: Container(
color: Colors.orange.withOpacity(0.3),
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Center(
child: buildTitle(),
),
],
),
),
),
);
}
Text buildTitle() => Text('Landscape');
Widget _buildOptionMenu(BuildContext context) {
return PopupMenuButton(itemBuilder: (context) {
var list = <String>[
'Landscape-Item-1',
'Landscape-Item-2',
'Landscape-Item-3',
];
return list
.map<PopupMenuEntry<String>>(
(e) => PopupMenuItem<String>(
child: Text(e),
),
)
.toList();
});
}
}
我找不到任何可行的解决方案。有一些解决方案需要监听方向变化和基于新方向的推送/弹出小部件。
但它有点太多了,需要在纵向和横向小部件中添加相同类型的代码。如果我需要处理反向纵向和反向横向等附加方向,这也是不可扩展的。
更新
可行的解决方案之一是弹出所有小部件直到根小部件,然后根据方向推送新的小部件。这有效,但有副作用。
例如,如果我从纵向推送一些新小部件(假设是登录页面)。
然后,如果我将我的设备旋转到横向,它应该在横向模式下膨胀登录页面 UI,但根据弹出所有小部件直到根目录的代码。
我将看到的是横向小部件,而不是横向模式下的登录页面。
为了清楚起见:
我正在寻找一个答案来关闭所有打开的对话框/弹出窗口,然后它的父小部件是disposed
. 该解决方案不应依赖于从导航器中弹出整个小部件。
我从小部件树表示中发现的是,显示为弹出窗口(弹出菜单或对话框)的小部件与MaterialApp
.
查看这些屏幕截图:
横向小部件中的可见弹出菜单:
Landscape小部件中的可见对话框:
肖像小部件中的可见弹出菜单:
Portrait 小部件中的可见对话框:
所以,基本上我正在寻找一种方法来查找和弹出所有这些类型的小部件,我们可以在处理父小部件之前弹出这些小部件。我想这应该适用于所有小部件屏幕,并且不应该改变当前小部件上方的现有小部件树。
更新 2
我得到了一个成功删除对话框和弹出窗口的答案,但它有一个副作用,即在删除所有对话框/弹出窗口的同时,它还删除了添加在显示弹出窗口的根小部件顶部的任何小部件。
例如:在此示例中,考虑我从小portrait
部件访问的详细信息页面。因此,当小部件重建时,即使我没有打开任何对话框/弹出窗口,也会portrait
处理详细信息页面并且仅再次显示。portrait