Flutter 用画面仿对话框(Dialog)点击画面外不关闭
出发点
由于 Flutter 的 AlertDialog 有时候会有局限性,所以用画面仿了一下对话框。
技术点
主要是使用 Stack 将 ModalBarrier 和 Material 叠加。
-
ModalBarrier 遮罩组件
dismissible 属性设置为 false 时,点击不会弹出页面,即不会关闭; 设置为 true 时,点击就会弹出页面,即关闭。
-
主体使用 Material
使用 Scaffold 的话,对话框画面不太方便设置圆角。
不使用 Material 的话,是无法放置 TextFormField 这样的组件的。 -
需要给 Material 设置内边距
这里是用设置画面的高宽减去对话框画面的高宽后除以 2 得到。
这里对话框画面的高宽都设置成了 400 。final width = MediaQuery.of(context).size.width; final height = MediaQuery.of(context).size.height; final double tb = (height - 400 - 24) / 2; final double lr = (width - 400) / 2; -
使用 showDialog 打开
showDialog( context: context, builder: (BuildContext context) => const DialogPage(), ); -
在对话框画面点击关闭按钮关闭
Navigator.of(context).pop();
画面效果
代码
dialog_page.dart
import 'package:flutter/material.dart';
class DialogPage extends StatefulWidget {
const DialogPage({super.key});
@override
State<DialogPage> createState() => _DialogPageState();
}
class _DialogPageState extends State<DialogPage> {
@override
Widget build(BuildContext context) {
final width = MediaQuery.of(context).size.width;
final height = MediaQuery.of(context).size.height;
final double tb = (height - 400 - 24) / 2;
final double lr = (width - 400) / 2;
return Stack(
children: [
const ModalBarrier(
dismissible: false, // 点击不关闭
),
Center(
child: Padding(
padding: EdgeInsets.only(left: lr, right: lr, top: tb, bottom: tb),
child: Material(
borderRadius: BorderRadius.circular(8),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
TextButton(
child: const Text('关闭'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
decoration: const InputDecoration(
label: Text('Hello'),
),
),
),
),
],
),
],
),
),
),
),
],
);
}
}
main.dart
import 'package:flutter/material.dart';
import 'dialog_page.dart';
void main() {
runApp(const App());
}
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
void showDialogPage() {
showDialog(
context: context,
builder: (BuildContext context) => const DialogPage(),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('子画面'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextButton(
onPressed: showDialogPage,
child: const Text('打开对话框画面'),
),
],
),
),
);
}
}