import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import '../../../core/theme/app_theme.dart'; /// 顶部轻提示(扣积分/操作失败等),自动消失,不依赖第三方 Toast 库 void showTopToast(BuildContext context, String message) { final overlay = Overlay.of(context); final entry = OverlayEntry( builder: (_) => Positioned( top: MediaQuery.of(context).padding.top + 16, left: 48, right: 48, child: Material( color: Colors.transparent, child: Center( child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: Colors.black.withValues(alpha: 0.75), borderRadius: BorderRadius.circular(20), ), child: Text( message, style: const TextStyle(color: Colors.white, fontSize: 14), ), ), ), ), ), ); overlay.insert(entry); Future.delayed(const Duration(seconds: 2), entry.remove); } /// 游客今日免费次数已用完:登录引导(2002) void showGuestLimitDialog(BuildContext context) { showDialog( context: context, barrierDismissible: false, builder: (ctx) => AlertDialog( backgroundColor: AppColors.surface, title: const Text('今日免费次数已用完'), content: const Text('注册解锁更多视频,今日已看 10/10'), actions: [ TextButton( onPressed: () => Navigator.of(ctx).pop(), child: const Text('再逛逛', style: TextStyle(color: AppColors.textSecondary)), ), ElevatedButton( onPressed: () { Navigator.of(ctx).pop(); context.go('/login'); }, child: const Text('去登录/注册'), ), ], ), ); } /// 积分不足:引导充值 / 开通会员(2001) void showPointsInsufficientDialog(BuildContext context, int points) { showDialog( context: context, builder: (ctx) => AlertDialog( backgroundColor: AppColors.surface, title: const Text('积分不足'), content: Text('当前积分 $points,请充值或开通会员后继续观看'), actions: [ TextButton( onPressed: () { Navigator.of(ctx).pop(); context.push('/membership'); }, child: const Text('开通会员', style: TextStyle(color: AppColors.gold)), ), ElevatedButton( onPressed: () { Navigator.of(ctx).pop(); context.push('/recharge'); }, child: const Text('去充值'), ), ], ), ); } /// 底部免费剩余次数提示条:游客/非会员且剩余 ≤3 时显示(《01-开发文档》5.2.1) class FreeQuotaBanner extends StatelessWidget { const FreeQuotaBanner({super.key, required this.remaining}); /// 今日免费剩余次数;null(未上报过)或 >3 不显示 final int? remaining; @override Widget build(BuildContext context) { final r = remaining; if (r == null || r > 3) return const SizedBox.shrink(); return SafeArea( child: Container( width: double.infinity, color: Colors.black.withValues(alpha: 0.6), padding: const EdgeInsets.symmetric(vertical: 6), child: Text( r > 0 ? '今日免费观看剩余 $r 次' : '今日免费次数已用完,登录后继续观看', textAlign: TextAlign.center, style: const TextStyle(color: AppColors.gold, fontSize: 12), ), ), ); } } /// 双击点赞的大爱心飘出动画 class LikeBurst extends StatefulWidget { const LikeBurst({super.key, required this.trigger}); /// 每次双击递增触发动画(0 不显示) final int trigger; @override State createState() => LikeBurstState(); } class LikeBurstState extends State with SingleTickerProviderStateMixin { late final AnimationController _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 700), ); late final Animation _scale = TweenSequence([ TweenSequenceItem(tween: Tween(begin: 0.0, end: 1.2), weight: 30), TweenSequenceItem(tween: Tween(begin: 1.2, end: 1.0), weight: 20), TweenSequenceItem(tween: Tween(begin: 1.0, end: 1.0), weight: 50), ]).animate(_controller); late final Animation _opacity = TweenSequence([ TweenSequenceItem(tween: Tween(begin: 1.0, end: 1.0), weight: 70), TweenSequenceItem(tween: Tween(begin: 1.0, end: 0.0), weight: 30), ]).animate(_controller); @override void didUpdateWidget(LikeBurst oldWidget) { super.didUpdateWidget(oldWidget); if (widget.trigger != oldWidget.trigger && widget.trigger > 0) { _controller.forward(from: 0); } } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { if (widget.trigger <= 0) return const SizedBox.shrink(); return Center( child: AnimatedBuilder( animation: _controller, builder: (_, __) => Opacity( opacity: _opacity.value, child: Transform.scale( scale: _scale.value, child: const Icon(Icons.favorite, size: 96, color: AppColors.primary), ), ), ), ); } }