// pangolin_toast.dart — 统一轻提示(toast)。真相源规格见 design/CONTRACT.md §2「Toast/SnackBar」。 // // 用 Overlay 实现(非 Material SnackBar),以完全控制布局: // · 只在主显示区内(避开左侧栏:desktop 204 / tablet 232 / mobile 0)。 // · 宽度自适应内容,上限为主显示区的 80%;超长则文本多行、居中。 // · 跟随明暗主题:surface 底 + fg1 字 + border 描边 + md 圆角 + 柔和暖阴影,淡入轻起。 import 'dart:async'; import 'package:flutter/material.dart'; import '../core/responsive/form_factor.dart'; import '../pangolin_theme.dart'; /// 在主显示区底部居中弹一条 toast(默认 3 秒)。需要一个能拿到 Overlay/主题的 context。 void showPangolinToast(BuildContext context, String message, {Duration duration = const Duration(seconds: 3)}) { final overlay = Overlay.maybeOf(context, rootOverlay: true); if (overlay == null) return; late final OverlayEntry entry; entry = OverlayEntry(builder: (_) => _PangolinToast(message: message)); overlay.insert(entry); Timer(duration, () { if (entry.mounted) entry.remove(); }); } class _PangolinToast extends StatelessWidget { const _PangolinToast({required this.message}); final String message; @override Widget build(BuildContext context) { final c = context.pangolin; // 侧栏宽度(避开它):取自各 shell 的 NavSidebar 宽度。 final sidebar = switch (context.formFactor) { FormFactor.desktop => 204.0, FormFactor.tablet => 232.0, FormFactor.mobile => 0.0, }; final w = MediaQuery.sizeOf(context).width; final contentW = (w - sidebar).clamp(0.0, w); return Positioned( left: sidebar, right: 0, bottom: 28, child: IgnorePointer( child: TweenAnimationBuilder( tween: Tween(begin: 0, end: 1), duration: const Duration(milliseconds: 240), curve: Curves.easeOut, builder: (_, t, child) => Opacity(opacity: t, child: Transform.translate(offset: Offset(0, (1 - t) * 8), child: child)), child: Center( child: ConstrainedBox( constraints: BoxConstraints(maxWidth: contentW * 0.8), child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( color: c.surface, borderRadius: BorderRadius.circular(PangolinRadius.md), border: Border.all(color: c.border), boxShadow: PangolinShadow.sm, ), child: Text( message, textAlign: TextAlign.center, style: PangolinText.sm.copyWith(color: c.fg1, fontWeight: FontWeight.w600), ), ), ), ), ), ), ); } }