import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:new_learn/core/utils/img_url.dart'; import 'package:new_learn/core/utils/screen_adapt_template.dart'; /// ============================================================ /// 【ComputationFinishedPopup】报告生成完成弹窗 /// —— 对标 Vue finishedPopup.vue /// /// 黑色 60% 遮罩 + 顶部 62% 屏高的 top-light 光效图 + /// 640x570rpx content-bg 内容图,底部「立即开启学习蝶变」渐变按钮, /// 入场 0.5s 淡入上移(popup-fade-up)。 /// ============================================================ class ComputationFinishedPopup extends StatefulWidget { const ComputationFinishedPopup({super.key, required this.onStart}); final VoidCallback onStart; static const String topLightImg = '/StaticResource/2026-06-11/top-light1781173654372.png'; static const String topLightPadImg = '/StaticResource/2026-06-24/top-light_pad1782270110001.png'; static const String contentBgImg = '/StaticResource/2026-06-11/content-bg1781173794633.png'; static const String starImg = '/StaticResource/2026-06-10/star1781089096898.png'; @override State createState() => _ComputationFinishedPopupState(); } class _ComputationFinishedPopupState extends State with SingleTickerProviderStateMixin { late final AnimationController _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 500), )..forward(); late final Animation _opacity = CurvedAnimation( parent: _controller, curve: Curves.easeOut, ); late final Animation _offset = Tween(begin: const Offset(0, 0.08), end: Offset.zero).animate( CurvedAnimation(parent: _controller, curve: Curves.easeOut), ); @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final screenHeight = MediaQuery.sizeOf(context).height; final isPad = MediaQuery.sizeOf(context).width >= AppAdapt.tabletBreakpoint; return Stack( children: [ // popup-mask const Positioned.fill(child: ColoredBox(color: Color(0x99000000))), // top-light:宽铺满,高 62%,居顶 Positioned( top: 0, left: 0, right: 0, height: screenHeight * 0.62, child: CachedNetworkImage( imageUrl: ImgUrl.build( isPad ? ComputationFinishedPopup.topLightPadImg : ComputationFinishedPopup.topLightImg, ), fit: BoxFit.fill, errorWidget: (_, _, _) => const SizedBox.shrink(), ), ), // popup-content:640x570rpx Center( child: FadeTransition( opacity: _opacity, child: SlideTransition( position: _offset, child: SizedBox( width: 320.w, height: 310.h, child: Stack( clipBehavior: Clip.none, children: [ Positioned.fill( child: CachedNetworkImage( imageUrl: ImgUrl.build(ComputationFinishedPopup.contentBgImg), fit: BoxFit.fill, errorWidget: (_, _, _) => DecoratedBox( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8.r), ), ), ), ), // gogo-button:90% 宽,距底 50rpx Positioned( left: 16.w, right: 16.w, bottom: 25.h, child: _buildStartButton(), ), ], ), ), ), ), ), ], ); } Widget _buildStartButton() { return GestureDetector( behavior: HitTestBehavior.opaque, onTap: widget.onStart, child: Container( height: 50.h, // 100rpx decoration: BoxDecoration( borderRadius: BorderRadius.circular(999), // start-button-bg 多层 radial-gradient 用线性渐变近似 gradient: const LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Color(0xFF6AA2FF), Color(0xFF3D7DF5)], ), border: Border.all(color: const Color(0x4DFFFFFF), width: 1.w), boxShadow: const [ BoxShadow( color: Color(0x3D3D7DF5), blurRadius: 20, offset: Offset(0, 10), ), ], ), alignment: Alignment.center, child: Row( mainAxisSize: MainAxisSize.min, children: [ CachedNetworkImage( imageUrl: ImgUrl.build(ComputationFinishedPopup.starImg), width: 24.w, height: 24.w, errorWidget: (_, _, _) => SizedBox(width: 24.w, height: 24.w), ), SizedBox(width: 6.w), Text( '立即开启学习蝶变', style: TextStyle( fontSize: 16.spMin, fontWeight: FontWeight.w600, color: Colors.white, ), ), ], ), ), ); } }