Files
record-tool/lib/shared/widgets/app_toast.dart
2026-06-04 10:50:24 +08:00

88 lines
2.3 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:recording_tool/app/router/app_navigator.dart';
class AppToast {
AppToast._();
static OverlayEntry? _entry;
static Timer? _timer;
static String messageOf(Object? error) {
if (error == null) return '未知错误';
if (error is FormatException) return error.message;
if (error is TimeoutException) return error.message ?? '请求超时';
var text = error.toString().trim();
const prefix = 'Exception: ';
while (text.startsWith(prefix)) {
text = text.substring(prefix.length).trimLeft();
}
return text.isEmpty ? '未知错误' : text;
}
static void show(
String message, {
Duration duration = const Duration(seconds: 2),
}) {
final context = AppNavigator.context;
if (context == null) return;
_dismiss();
final overlay = Overlay.maybeOf(context, rootOverlay: true);
if (overlay == null) return;
_entry = OverlayEntry(builder: (_) => _ToastWidget(message: message));
overlay.insert(_entry!);
_timer = Timer(duration, _dismiss);
}
static void showError(Object error) => show(messageOf(error));
static void _dismiss() {
_timer?.cancel();
_timer = null;
try {
_entry?.remove();
} catch (_) {
// OverlayEntry may already be removed.
}
_entry = null;
}
}
class _ToastWidget extends StatelessWidget {
const _ToastWidget({required this.message});
final String message;
@override
Widget build(BuildContext context) {
return Positioned.fill(
child: IgnorePointer(
child: Center(
child: Material(
color: Colors.transparent,
child: Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.sizeOf(context).width * 0.8,
),
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 11),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.82),
borderRadius: BorderRadius.circular(8),
),
child: Text(
message,
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.white, fontSize: 14),
),
),
),
),
),
);
}
}