Files
please-pay-me/mobile/lib/ui/feedback/app_toast.dart
T
2026-09-21 04:06:43 +03:00

78 lines
2.3 KiB
Dart

import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:please_pay_me/theme/tokens.dart';
import 'package:please_pay_me/ui/atoms/app_text.dart';
/// iOS has no SnackBar — the platform idiom is a floating blurred capsule
/// (AirPods / Silent-mode style). Kept dismiss-free and auto-hiding.
class AppToast extends StatelessWidget {
const AppToast({
super.key,
required this.message,
this.icon = CupertinoIcons.check_mark_circled_solid,
this.tint = AppColors.accent,
});
final String message;
final IconData? icon;
final Color tint;
@override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.circular(AppRadii.capsule),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 20, sigmaY: 20),
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.s4,
vertical: AppSpacing.s3,
),
decoration: BoxDecoration(
color: AppColors.of(context, AppColors.groupedSurface).withValues(alpha: 0.82),
borderRadius: BorderRadius.circular(AppRadii.capsule),
boxShadow: const [
BoxShadow(color: Color(0x1F000000), blurRadius: 24, offset: Offset(0, 8)),
],
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (icon != null) ...[
Icon(icon, size: 20, color: AppColors.of(context, tint)),
const SizedBox(width: AppSpacing.s2),
],
Flexible(child: AppText.subhead(message, color: AppColors.label)),
],
),
),
),
);
}
}
Future<void> showAppToast(
BuildContext context, {
required String message,
IconData? icon = CupertinoIcons.check_mark_circled_solid,
Duration duration = const Duration(seconds: 2),
}) async {
final overlay = Overlay.of(context, rootOverlay: true);
final entry = OverlayEntry(
builder: (ctx) => Positioned(
left: AppSpacing.s5,
right: AppSpacing.s5,
bottom: MediaQuery.of(ctx).padding.bottom + AppSpacing.s7,
child: SafeArea(
top: false,
child: Center(child: AppToast(message: message, icon: icon)),
),
),
);
overlay.insert(entry);
await Future<void>.delayed(duration);
entry.remove();
}