showModalDailog<T> static method
Future<T?>
showModalDailog<T>(
- BuildContext rootContext,
- Widget dialog, {
- bool barrierDismissible = true,
- bool useSafeArea = true,
- required String dialogKey,
- Duration transitionDuration = const Duration(milliseconds: 300),
- DialogAnimationType animationType = DialogAnimationType.scale,
- Color barrierColor = Colors.black54,
- bool isSlideDialog = false,
- SlideDialogDirection slideDirection = SlideDialogDirection.fromRight,
显示模态对话框
rootContext 上下文
dialog 对话框
barrierDismissible 是否可以点击背景关闭
useSafeArea 是否使用安全区域
useRootNavigator 是否使用根导航器
transitionDuration 动画时长
animationType 动画类型
isSlideDialog 是否是滑动对话框(竖屏从底部滑入,横屏从侧边滑入)
slideDirection 滑动方向(仅在横屏且isSlideDialog=true时生效)
@return 返回对话框的唯一key
Implementation
static Future<T?> showModalDailog<T>(
BuildContext rootContext,
Widget dialog, {
bool barrierDismissible = true,
bool useSafeArea = true,
bool useRootNavigator = false,
required String dialogKey,
Duration transitionDuration = const Duration(milliseconds: 300),
DialogAnimationType animationType = DialogAnimationType.scale,
Color barrierColor = Colors.black54,
bool isSlideDialog = false,
SlideDialogDirection slideDirection = SlideDialogDirection.fromRight,
}) async {
TCICLog.info(
'showModalDailog dialog: ${dialog.toStringShort()}, key: $dialogKey, animation: $animationType',
actionModule: ActionModule.tools.name,
actionName: ActionName.showModalDailog.name,
);
// 使用 Completer 来处理异步返回值
final completer = Completer<T?>();
// 生成当前弹窗实例的唯一ID
final instanceId =
'${dialogKey}_${DateTime.now().millisecondsSinceEpoch}_${_currentMaxLevel}';
// 使用 addPostFrameCallback 确保在正确的时机执行
// runAfterInit(() async {
try {
// 只在第一个弹窗打开时隐藏floating
if (_currentMaxLevel == 0) {
hideAllFloating();
}
// 为当前弹窗分配层级
_currentMaxLevel++;
// 初始化列表(如果不存在)
_dialogContexts.putIfAbsent(dialogKey, () => []);
_dialogLevels.putIfAbsent(dialogKey, () => []);
_dialogInstanceIds.putIfAbsent(dialogKey, () => []);
// 添加当前实例ID到列表
_dialogInstanceIds[dialogKey]!.add(instanceId);
_dialogLevels[dialogKey]!.add(_currentMaxLevel);
// 提前获取屏幕尺寸,避免在动画过程中访问可能失效的context
final screenSize = MediaQuery.of(rootContext).size;
final isPortrait =
MediaQuery.of(rootContext).orientation == Orientation.portrait;
dynamic res;
if (isSlideDialog) {
if (isPortrait) {
// 竖屏模式:使用 showModalBottomSheet
res = await showModalBottomSheet<T>(
context: rootContext,
backgroundColor: Colors.transparent,
isScrollControlled: true,
builder: (context) {
// 将新的context添加到列表中
_dialogContexts[dialogKey]!.add(context);
return ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12),
),
child: Container(
width: screenSize.width,
height: screenSize.height * 0.8,
color: Theme.of(rootContext).colorScheme.secondary,
child: Theme(data: Theme.of(rootContext), child: dialog),
),
);
},
);
} else {
// 横屏模式:使用 showGeneralDialog 从侧边滑入
// 根据 slideDirection 确定对齐方式和圆角
final isFromRight = slideDirection == SlideDialogDirection.fromRight;
final alignment =
isFromRight ? Alignment.centerRight : Alignment.centerLeft;
final borderRadius =
isFromRight
? const BorderRadius.only(
topLeft: Radius.circular(12),
bottomLeft: Radius.circular(12),
)
: const BorderRadius.only(
topRight: Radius.circular(12),
bottomRight: Radius.circular(12),
);
res = await showGeneralDialog<T>(
context: rootContext,
barrierDismissible: false,
barrierLabel: "Dismiss",
barrierColor: Colors.black54,
transitionDuration: Duration(milliseconds: 300),
pageBuilder: (context, animation1, animation2) {
// 将新的context添加到列表中
_dialogContexts[dialogKey]!.add(context);
return PointerInterceptor(
child: GestureDetector(
onTap: () => Navigator.of(context).pop(),
behavior: HitTestBehavior.opaque,
child: Container(
color: Colors.transparent,
child: Align(
alignment: alignment,
child: GestureDetector(
onTap: () {},
behavior: HitTestBehavior.opaque,
child: Theme(
data: Theme.of(rootContext),
child: ClipRRect(
borderRadius: borderRadius,
child: Material(
color:
Theme.of(rootContext).colorScheme.secondary,
child: SizedBox(
width: screenSize.width * 0.5,
height: screenSize.height,
child: dialog,
),
),
),
),
),
),
),
),
);
},
transitionBuilder: (context, animation1, animation2, child) {
// 根据方向计算偏移量
final curvedAnimation = Curves.easeOut.transform(
animation1.value,
);
final offsetX =
isFromRight
? screenSize.width *
0.5 *
(1 - curvedAnimation) // 从右往左
: -screenSize.width * 0.5 * (1 - curvedAnimation); // 从左往右
return Transform.translate(
offset: Offset(offsetX, 0),
child: child,
);
},
);
}
// 对话框关闭后,从存储中移除对应的实例
_cleanupDialogInstance(dialogKey, instanceId);
// 只在所有弹窗都关闭时才显示floating
if (_currentMaxLevel == 0) {
showAllFloating();
}
completer.complete(res);
} else {
runAfterInit(() async {
res = await showGeneralDialog<T?>(
context: rootContext,
barrierDismissible: barrierDismissible,
barrierLabel: '',
barrierColor: barrierColor,
transitionDuration: transitionDuration,
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
// 将新的context添加到列表中
_dialogContexts[dialogKey]!.add(context);
return Theme(
data: TCICController.instance.getThemeObs().theme,
child: dialog,
);
},
transitionBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return _buildAnimation(animation, child, animationType);
},
// useRootNavigator: useRootNavigator,
);
// 对话框关闭后,从存储中移除对应的实例
_cleanupDialogInstance(dialogKey, instanceId);
// 只在所有弹窗都关闭时才显示floating
if (_currentMaxLevel == 0) {
showAllFloating();
}
completer.complete(res);
});
}
} catch (e) {
completer.completeError(e);
}
// });
return completer.future;
}