diff --git a/lib/app_upgrade_simple.dart b/lib/app_upgrade_simple.dart index 816888a..cdc905f 100644 --- a/lib/app_upgrade_simple.dart +++ b/lib/app_upgrade_simple.dart @@ -518,51 +518,101 @@ class AppUpgradeSimple { ); } + /// 使用 Overlay 显示 Toast(不依赖 Scaffold) + void _showOverlayToast(BuildContext context, String message, UpgradeConfig config) { + if (!context.mounted) { + debugPrint('Toast消息(context已卸载): $message'); + return; + } + + try { + final overlay = Overlay.of(context); + + // 创建 OverlayEntry + final overlayEntry = OverlayEntry( + builder: (context) => _ToastWidget(message: message), + ); + + // 插入到 Overlay + overlay.insert(overlayEntry); + + // 2秒后自动移除 + Future.delayed(const Duration(seconds: 2), () { + try { + overlayEntry.remove(); + } catch (e) { + // 忽略移除错误 + if (config.enableDebugLog) { + debugPrint('移除Toast失败: $e'); + } + } + }); + } catch (e) { + debugPrint('显示Toast失败: $e'); + debugPrint('Toast消息: $message'); + } + } + + /// 尝试使用 ScaffoldMessenger 显示 SnackBar(如果可用) + void _tryShowSnackBar(BuildContext context, String message, UpgradeConfig config) { + if (!context.mounted) { + _showOverlayToast(context, message, config); + return; + } + + // 尝试获取 ScaffoldMessenger + final scaffoldMessenger = ScaffoldMessenger.maybeOf(context); + if (scaffoldMessenger == null) { + // 如果没有 ScaffoldMessenger,使用 Overlay Toast + _showOverlayToast(context, message, config); + return; + } + + // 尝试使用根 Navigator 的 context(更安全) + BuildContext? rootContext; + try { + final navigator = Navigator.maybeOf(context, rootNavigator: true); + if (navigator != null) { + rootContext = navigator.context; + } + } catch (e) { + // 忽略错误,继续使用原 context + } + + // 优先使用根 context 的 ScaffoldMessenger + final messenger = + rootContext != null && rootContext.mounted ? ScaffoldMessenger.maybeOf(rootContext) : scaffoldMessenger; + + if (messenger == null) { + _showOverlayToast(context, message, config); + return; + } + + // 尝试显示 SnackBar,如果失败则使用 Overlay Toast + try { + messenger.showSnackBar( + SnackBar( + content: Text(message), + duration: Duration(seconds: 2), + ), + ); + } catch (e) { + // 如果 SnackBar 失败,使用 Overlay Toast + if (config.enableDebugLog) { + debugPrint('SnackBar显示失败,使用Overlay Toast: $e'); + } + _showOverlayToast(context, message, config); + } + } + /// 显示Toast提示 void _showToast(String message, BuildContext context, [UpgradeConfig? config]) { final effectiveConfig = config ?? _config; if (effectiveConfig.customToast != null) { effectiveConfig.customToast!(message); } else { - // 检查 context 是否有效 - if (!context.mounted) { - debugPrint('Toast消息(context已卸载): $message'); - return; - } - - // 检查是否有 Scaffold 在 widget 树中 - final scaffold = Scaffold.maybeOf(context); - if (scaffold == null) { - // 如果没有 Scaffold,使用 debugPrint 作为后备方案 - debugPrint('Toast消息(无Scaffold): $message'); - return; - } - - // 使用 maybeOf 安全地获取 ScaffoldMessenger - final scaffoldMessenger = ScaffoldMessenger.maybeOf(context); - if (scaffoldMessenger == null) { - // 如果没有 ScaffoldMessenger,使用 debugPrint 作为后备方案 - debugPrint('Toast消息(无ScaffoldMessenger): $message'); - return; - } - - // 即使有 Scaffold 和 ScaffoldMessenger,也可能出现其他问题 - // 使用 try-catch 捕获所有可能的错误 - try { - scaffoldMessenger.showSnackBar( - SnackBar( - content: Text(message), - duration: Duration(seconds: 2), - ), - ); - } catch (e) { - // 捕获所有错误,包括断言错误 - // 在调试模式下,断言错误可能仍然会显示,但不会导致应用崩溃 - debugPrint('显示Toast失败(已使用后备方案): $message'); - if (effectiveConfig.enableDebugLog) { - debugPrint('错误详情: $e'); - } - } + // 优先尝试使用 SnackBar,如果失败则使用 Overlay Toast + _tryShowSnackBar(context, message, effectiveConfig); } } @@ -2053,3 +2103,68 @@ class _ForceUpgradeDialogState extends State<_ForceUpgradeDialog> with _UpgradeD ); } } + +/// 使用 Overlay 显示的 Toast Widget(不依赖 Scaffold) +class _ToastWidget extends StatelessWidget { + final String message; + + const _ToastWidget({required this.message}); + + @override + Widget build(BuildContext context) { + return Positioned( + top: MediaQuery.of(context).padding.top + 16, + left: 16, + right: 16, + child: Material( + color: Colors.transparent, + child: SafeArea( + child: IgnorePointer( + child: TweenAnimationBuilder( + duration: const Duration(milliseconds: 300), + tween: Tween(begin: 0.0, end: 1.0), + builder: (context, value, child) { + return Opacity( + opacity: value, + child: Transform.translate( + offset: Offset(0, -20 * (1 - value)), + child: child, + ), + ); + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + decoration: BoxDecoration( + color: Colors.black87, + borderRadius: BorderRadius.circular(8), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.2), + blurRadius: 8, + offset: const Offset(0, 4), + ), + ], + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: Text( + message, + style: const TextStyle( + color: Colors.white, + fontSize: 14, + ), + textAlign: TextAlign.center, + ), + ), + ], + ), + ), + ), + ), + ), + ), + ); + } +}