1. 新增报告计算进度圆环、科目卡片、完成弹窗组件 2. 重构report_computation_page,替换旧的自定义组件为新封装组件 3. 新增student_info_page的主题资源和UI重构,适配Flutter屏幕适配 4. 新增临时调试入口用于开发测试
172 lines
5.6 KiB
Dart
172 lines
5.6 KiB
Dart
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<ComputationFinishedPopup> createState() =>
|
||
_ComputationFinishedPopupState();
|
||
}
|
||
|
||
class _ComputationFinishedPopupState extends State<ComputationFinishedPopup>
|
||
with SingleTickerProviderStateMixin {
|
||
late final AnimationController _controller = AnimationController(
|
||
vsync: this,
|
||
duration: const Duration(milliseconds: 500),
|
||
)..forward();
|
||
|
||
late final Animation<double> _opacity = CurvedAnimation(
|
||
parent: _controller,
|
||
curve: Curves.easeOut,
|
||
);
|
||
late final Animation<Offset> _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,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|