初始化
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hgdj/assets_tool/images.dart';
|
||||
import 'package:hgdj/hj_model/splash/domain_source_model.dart';
|
||||
import 'package:hgdj/hj_page/home/activity_float/home_activity_float.dart';
|
||||
import 'package:hgdj/hj_page/main_page/provider/msg_provider.dart';
|
||||
import 'package:hgdj/hj_utils/screen.dart';
|
||||
import 'package:hgdj/tools_base/widget/fly_to_overlay.dart';
|
||||
import 'package:hgdj/tools_base/widget/net_image_widget.dart';
|
||||
|
||||
/// 支付分层首页弹窗:展示后端 homePage 图,点击跳会员页并选中 config.vipCard。
|
||||
/// 倒计时读 config.lastDiscountTime,秒级刷新由 MineMsgProvider().tick 驱动,过期自动停
|
||||
class GuideHomeDialog extends StatefulWidget {
|
||||
final PayTierConfig config;
|
||||
|
||||
const GuideHomeDialog({super.key, required this.config});
|
||||
|
||||
@override
|
||||
State<GuideHomeDialog> createState() => _GuideHomeDialogState();
|
||||
}
|
||||
|
||||
class _GuideHomeDialogState extends State<GuideHomeDialog> {
|
||||
final _posterKey = GlobalKey(); // 关闭动画的起飞位置
|
||||
|
||||
/// 关闭中:① 防连点,pop 过渡期弹窗还在树上,再点会把首页也 pop 掉
|
||||
/// ② 藏掉原海报,否则它随弹窗淡出、和飞行副本重影
|
||||
bool _closing = false;
|
||||
|
||||
PayTierConfig get config => widget.config;
|
||||
|
||||
/// 关闭:海报缩小飞进右下角浮窗,告诉用户同款入口收在哪
|
||||
void _close() {
|
||||
if (_closing) return;
|
||||
_closing = true;
|
||||
final target = _floatRect();
|
||||
if (target != null) {
|
||||
FlyToOverlay.play(
|
||||
context: context,
|
||||
sourceKey: _posterKey,
|
||||
target: target,
|
||||
// 副本只放海报,倒计时胶囊不跟着飞(缩到 60 宽也看不清)
|
||||
// 占位给透明:没命中内存缓存时别飞一个灰块过去
|
||||
child: NetworkImageLoader(
|
||||
imageUrl: config.homePage ?? "",
|
||||
fit: BoxFit.fill,
|
||||
borderRadius: 0,
|
||||
placeHolderWidget: const SizedBox(),
|
||||
),
|
||||
);
|
||||
}
|
||||
setState(() {}); // 原海报立刻消失,只留副本在飞
|
||||
Get.back();
|
||||
}
|
||||
|
||||
/// 落点:容器右下角一块浮窗大小的区域(浮窗都 bottomRight 对齐)。
|
||||
/// 认容器不认轮播子项——子项会轮到屏幕外,容器位置才稳定。
|
||||
/// 浮窗没展示时容器不存在,返回 null → 直接关不飞
|
||||
Rect? _floatRect() {
|
||||
final box = homeFloatKey.currentContext?.findRenderObject() as RenderBox?;
|
||||
if (box == null || !box.hasSize) return null;
|
||||
final origin = box.localToGlobal(Offset.zero);
|
||||
const w = 60.0, h = 66.0; // 与 _LayeredFloatView 的尺寸一致
|
||||
return Rect.fromLTWH(
|
||||
origin.dx + box.size.width - w, origin.dy + box.size.height - h, w, h);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// 点蒙层、返回键都走 _close,保证任何关闭方式都有飞入动画
|
||||
// (canPop:false 只拦 maybePop,_close 里的 Get.back() 是强制 pop,不会被自己拦住)
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
onPopInvokedWithResult: (didPop, _) {
|
||||
if (!didPop) _close();
|
||||
},
|
||||
child: Center(
|
||||
// 关闭中内容交给飞行副本。必须同时 IgnorePointer:
|
||||
// Opacity(0) 照样吃手势,退出动画那百来毫秒点到海报会再 pop 一次并跳会员页
|
||||
child: IgnorePointer(
|
||||
ignoring: _closing,
|
||||
child: Opacity(
|
||||
opacity: _closing ? 0 : 1,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 30),
|
||||
child: GestureDetector(
|
||||
// 只回传"去开通",跳会员页交给调用方(_showPayDialog)——它要 await 到从会员页
|
||||
// 返回才继续弹窗链,否则后面的首页吸底引导会弹在会员页上
|
||||
onTap: () {
|
||||
if (_closing) return; // 防连点:pop 过渡期弹窗还在树上,再点会把首页也 pop 掉
|
||||
_closing = true;
|
||||
Get.back(result: true);
|
||||
},
|
||||
child: AspectRatio(
|
||||
aspectRatio: 3 / 4,
|
||||
child: Stack(
|
||||
key: _posterKey,
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
NetworkImageLoader(
|
||||
imageUrl: config.homePage ?? "",
|
||||
fit: BoxFit.fill,
|
||||
borderRadius: 0,
|
||||
placeHolderWidget:
|
||||
Container(color: const Color(0x00000000)),
|
||||
),
|
||||
// 倒计时胶囊:海报右上角,听 tick 每秒刷新,过期自动收起
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: 0,
|
||||
child: ValueListenableBuilder<int>(
|
||||
valueListenable: MineMsgProvider().tick,
|
||||
builder: (_, __, ___) =>
|
||||
config.hasDiscountCountdown
|
||||
? _buildCountdownPill()
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
20.sizeBoxH,
|
||||
GestureDetector(
|
||||
onTap: _close,
|
||||
child: Image.asset("close_button.png".commonImgPath,
|
||||
width: 32),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 倒计时胶囊:红渐变 + 奶白边,HH:MM:SS
|
||||
Widget _buildCountdownPill() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
gradient: const LinearGradient(
|
||||
colors: [Color(0xffE03017), Color(0xffF27952)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: const Color(0xffFFFAEE), width: 0.5),
|
||||
),
|
||||
child: Text(
|
||||
'${config.discountHour}:${config.discountMin}:${config.discountSec}',
|
||||
style: const TextStyle(
|
||||
color: Color(0xffFFF0D1),
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user