Har*_*shi 2 flutter flutter-dialog
我一直在从事一个项目,该项目要求我根据移动设备的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();
});
}
}
Run Code Online (Sandbox Code Playgroud)
我找不到任何可行的解决方案。有一些解决方案需要监听方向变化并根据新方向推送/弹出小部件。
但它有点太多了,需要在纵向和横向小部件中添加相同类型的代码。如果我需要处理其他方向,例如反向纵向和反向横向,这也是不可扩展的。
可行的解决方案之一是弹出所有小部件直到根小部件,然后根据方向推送新小部件。这有效但会带来副作用。
例如,如果我从纵向推送一些新的小部件(假设是登录页面)。
然后,如果我将设备旋转到横向,它应该以横向模式膨胀登录页面 UI,但根据弹出所有小部件直到根的代码。
我将看到的是横向小部件,而不是横向模式下的登录页面。
为了清楚起见:
我正在寻找一个答案,以在其父小部件之前关闭所有打开的对话框/弹出窗口disposed。该解决方案不应依赖于从导航器中弹出整个小部件。
我从小部件树表示中发现,显示为弹出窗口(弹出菜单或对话框)的小部件直接位于小部件的不同分支MaterialApp。
查看这些屏幕截图:
横向小部件中可见的弹出菜单:
横向小部件中的可见对话框:
肖像小部件中可见的弹出菜单:
纵向小部件中的可见对话框:
因此,基本上我正在寻找一种方法来查找并弹出所有这些类型的小部件,我们可以在处理父小部件之前弹出这些小部件。我想这应该适用于所有小部件屏幕,并且不应更改当前小部件上方的现有小部件树。
我得到的答案成功删除了对话框和弹出窗口,但它有一个副作用,即在删除所有对话框/弹出窗口的同时,它还会删除添加在显示弹出窗口的根小部件顶部的任何小部件。
例如:在此示例中,考虑我从小portrait部件访问的详细信息页面。因此,当portrait小部件重建时,详细信息页面将被处理,并且仅portrait再次显示,即使我没有打开任何对话框/弹出窗口。
根据给出的代码,我更改了长按显示对话框代码及其子代码的手势检测器,如下所示:
// alert dialog code
...
builder: (context) => AlertDialog(
content:
MediaQuery.of(context).orientation == Orientation.portrait
? Text('Portrait')
: Text('Landscape'),
),
...
// gesture detector child code
...
Center(
child: MediaQuery.of(context).orientation == Orientation.portrait
? Text('Portrait')
: Text('Landscape'),
),
...
Run Code Online (Sandbox Code Playgroud)
输出:

结论: MediaQuery.of(context).orientation 可以自行处理。
更新:
如果您在使用此代码改变方向时使用生命周期,则只会调用构建方法而不是处置方法。您可以在构建方法调用时删除所有弹出窗口。
查看下面的代码。
长按时,我打开了 3 个弹出窗口用于演示目的(可以有任意数量的弹出窗口或菜单)...当方向更改时,
Navigator.of(context).popUntil((route) => route.isFirst);将首先调用并首先弹出所有弹出窗口和菜单。
// 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> {
// this method will not be called when orientation changes
// @override
// void dispose() {
// super.dispose();
// Navigator.pop(context);
// print("Portrait dispose");
// }
@override
Widget build(BuildContext context) {
// the below line will pop all the popups
Navigator.of(context).popUntil((route) => route.isFirst);
// code to check this method is called when orientation is changed
print("Portrait build");
return Scaffold(
appBar: AppBar(
title: buildTitle(),
actions: <Widget>[_buildOptionMenu(context)],
),
body: GestureDetector(
onLongPress: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
content: Center(
child:
MediaQuery.of(context).orientation == Orientation.portrait
? Text('Portrait')
: Text('Landscape'),
),
),
);
showDialog(
context: context,
builder: (context) => AlertDialog(
content: Center(
child:
MediaQuery.of(context).orientation == Orientation.portrait
? Text('Portrait')
: Text('Landscape'),
),
),
);
showDialog(
context: context,
builder: (context) => AlertDialog(
content: Center(
child:
MediaQuery.of(context).orientation == Orientation.portrait
? Text('Portrait')
: Text('Landscape'),
),
),
);
},
child: Container(
color: Colors.blue.withOpacity(0.4),
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Center(
child: Center(
child:
MediaQuery.of(context).orientation == Orientation.portrait
? Text('Portrait')
: Text('Landscape'),
),
),
],
),
),
),
);
}
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> {
// this method will not be called when orientation changes
// @override
// void dispose() {
// super.dispose();
// Navigator.pop(context);
// print("Landscape dispose");
// }
@override
Widget build(BuildContext context) {
// the below line will pop all the popups
Navigator.of(context).popUntil((route) => route.isFirst);
// code to check this method is called when orientation is changed
print("Landscape build");
return Scaffold(
appBar: AppBar(
title: buildTitle(),
actions: <Widget>[_buildOptionMenu(context)],
),
body: GestureDetector(
onLongPress: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
content: Center(
child:
MediaQuery.of(context).orientation == Orientation.portrait
? Text('Portrait')
: Text('Landscape'),
),
),
);
showDialog(
context: context,
builder: (context) => AlertDialog(
content: Center(
child:
MediaQuery.of(context).orientation == Orientation.portrait
? Text('Portrait')
: Text('Landscape'),
),
),
);
showDialog(
context: context,
builder: (context) => AlertDialog(
content: Center(
child:
MediaQuery.of(context).orientation == Orientation.portrait
? Text('Portrait')
: Text('Landscape'),
),
),
);
},
child: Container(
color: Colors.orange.withOpacity(0.3),
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Center(
child: Center(
child:
MediaQuery.of(context).orientation == Orientation.portrait
? Text('Portrait')
: Text('Landscape'),
),
),
],
),
),
),
);
}
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();
});
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4726 次 |
| 最近记录: |