purchase_view.dart 9.45 KB
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';

import '../controllers/purchase_controller.dart';
import '../widgets/purchase_benefit_list.dart';
import '../widgets/purchase_bottom_bar.dart';
import '../widgets/purchase_colors.dart';
import '../widgets/purchase_plan_card.dart';

class PurchaseView extends GetView<PurchaseController> {
  const PurchaseView({super.key});

  static const _benefits = [
    PurchaseBenefit(title: '实时压力监测', icon: Icons.monitor_heart_outlined),
    PurchaseBenefit(title: '每周/每月/每年压力趋势', icon: Icons.query_stats),
    PurchaseBenefit(
      title: '每周/每月/每年活动消耗趋势',
      icon: Icons.local_fire_department_outlined,
    ),
    PurchaseBenefit(title: '每周/每月/每年睡眠报告', icon: Icons.bed_outlined),
    PurchaseBenefit(title: '健康数据实时同步', icon: Icons.sync),
    PurchaseBenefit(title: '健康数据实时通知亲密联系人', icon: Icons.near_me_outlined),
    PurchaseBenefit(title: '会员专属自定义表盘', icon: Icons.watch_outlined),
    PurchaseBenefit(title: '睡眠分析', icon: Icons.nightlight_round),
    PurchaseBenefit(title: '未来更多高级权益免费获取', icon: Icons.layers_outlined),
  ];

  @override
  Widget build(BuildContext context) {
    return AnnotatedRegion<SystemUiOverlayStyle>(
      value: const SystemUiOverlayStyle(
        statusBarColor: Colors.transparent,
        statusBarIconBrightness: Brightness.dark,
        statusBarBrightness: Brightness.light,
        systemNavigationBarColor: PurchaseColors.background,
        systemNavigationBarIconBrightness: Brightness.dark,
      ),
      child: Scaffold(
        backgroundColor: PurchaseColors.background,
        body: Stack(
          children: [
            Positioned.fill(
              child: SingleChildScrollView(
                controller: controller.scrollController,
                physics: const ClampingScrollPhysics(),
                padding: const EdgeInsets.only(bottom: 170),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const _HeroHeader(),
                    const Padding(
                      padding: EdgeInsets.only(left: 18, top: 9),
                      child: Text(
                        '解锁专业版,关心更及时',
                        style: TextStyle(
                          color: Colors.black,
                          fontSize: 24,
                          fontWeight: FontWeight.w600,
                          height: 1.2,
                        ),
                      ),
                    ),
                    const SizedBox(height: 26),
                    _PlanScroller(controller: controller),
                    const SizedBox(height: 22),
                    const _SectionTitle('畅享全部高级权益'),
                    const SizedBox(height: 14),
                    const PurchaseBenefitList(benefits: _benefits),
                    const SizedBox(height: 24),
                    const _LegalNotes(),
                  ],
                ),
              ),
            ),
            Positioned(
              left: 0,
              right: 0,
              top: 0,
              child: _PurchaseNavigationBar(controller: controller),
            ),
            Positioned(
              left: 0,
              right: 0,
              bottom: 0,
              child: PurchaseBottomBar(
                label: '立即解锁',
                loading: false,
                onPressed: controller.unlock,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class _HeroHeader extends StatelessWidget {
  const _HeroHeader();

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 320,
      width: double.infinity,
      child: DecoratedBox(
        decoration: const BoxDecoration(
          gradient: LinearGradient(
            begin: Alignment.topCenter,
            end: Alignment.bottomCenter,
            colors: [
              Color(0xFFFF8898),
              Color(0xFFFF9BA8),
              PurchaseColors.background,
            ],
            stops: [0, 0.76, 1],
          ),
        ),
        child: Align(
          alignment: const Alignment(0.55, 0.18),
          child: Image.asset(
            'assets/images/today/premium_gift_illustration.png',
            width: 150,
            height: 164,
            fit: BoxFit.contain,
          ),
        ),
      ),
    );
  }
}

class _PlanScroller extends StatelessWidget {
  const _PlanScroller({required this.controller});

  final PurchaseController controller;

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 123,
      child: Obx(
        () {
          final selectedIndex = controller.selectedIndex.value;
          return ListView.separated(
            controller: controller.planScrollController,
            padding: const EdgeInsets.symmetric(horizontal: 18),
            scrollDirection: Axis.horizontal,
            physics: const ClampingScrollPhysics(),
            itemCount: controller.plans.length,
            separatorBuilder: (_, __) => const SizedBox(width: 12),
            itemBuilder: (context, index) {
              final plan = controller.plans[index];
              return PurchasePlanCard(
                plan: plan,
                selected: selectedIndex == index,
                onTap: () => controller.selectPlan(index),
              );
            },
          );
        },
      ),
    );
  }
}

class _SectionTitle extends StatelessWidget {
  const _SectionTitle(this.text);

  final String text;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16),
      child: Text(
        text,
        style: const TextStyle(
          color: Colors.black,
          fontSize: 18,
          fontWeight: FontWeight.w600,
          height: 1.2,
        ),
      ),
    );
  }
}

class _PurchaseNavigationBar extends StatelessWidget {
  const _PurchaseNavigationBar({required this.controller});

  final PurchaseController controller;

  @override
  Widget build(BuildContext context) {
    final topInset = MediaQuery.paddingOf(context).top;
    return Obx(
      () => AnimatedContainer(
        duration: const Duration(milliseconds: 160),
        height: topInset + 44,
        padding: EdgeInsets.only(top: topInset),
        color: controller.isScrolled.value ? Colors.white : Colors.transparent,
        child: Row(
          children: [
            const SizedBox(width: 4),
            IconButton(
              onPressed: Get.back,
              icon: Image.asset(
                'assets/images/common/ic_nav_back.webp',
                width: 24,
                height: 24,
              ),
            ),
            const Spacer(),
            TextButton(
              onPressed: controller.restorePurchase,
              style: TextButton.styleFrom(
                foregroundColor: PurchaseColors.title,
                padding: const EdgeInsets.symmetric(horizontal: 16),
                textStyle: const TextStyle(fontSize: 14, height: 1.2),
              ),
              child: const Text('恢复'),
            ),
          ],
        ),
      ),
    );
  }
}

class _LegalNotes extends StatelessWidget {
  const _LegalNotes();

  @override
  Widget build(BuildContext context) {
    return const Padding(
      padding: EdgeInsets.symmetric(horizontal: 16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            '说明',
            style: TextStyle(
              color: Colors.black,
              fontSize: 18,
              fontWeight: FontWeight.w600,
              height: 1.2,
            ),
          ),
          SizedBox(height: 24),
          _LegalText(
            prefix: '1. ',
            text:
                '确认购买并支付后,将通过您的iTunes账号自动续订。苹果iTunes账户会在到期前24小时内扣费,扣费成功后订阅周期顺延一个订阅周期。如需取消续订,请在当前订阅周期到期前24小时以前,手动在iTunes/ApplelD设置管理中关闭自动续费功能。本服务由您自主选择是否取消,若您选择不取消,将为您开通下个计费周期的续费服务。\n\nDoubleFeel Pro会员属于虚拟物品,购买后,除App Store渠道退款外,不支持其它形式退款,如果需要可以点击了解更多。',
          ),
          SizedBox(height: 12),
          _LegalText(prefix: '2. ', text: '如果购买后未生效,请点击恢复购买。'),
          SizedBox(height: 12),
          _LegalText(prefix: '3. ', text: '如果有更多其它问题请联系我们。'),
        ],
      ),
    );
  }
}

class _LegalText extends StatelessWidget {
  const _LegalText({required this.prefix, required this.text});

  final String prefix;
  final String text;

  @override
  Widget build(BuildContext context) {
    return Row(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          prefix,
          style: const TextStyle(
            color: PurchaseColors.subtitle,
            fontSize: 12,
            height: 1.4,
          ),
        ),
        Expanded(
          child: Text(
            text,
            style: const TextStyle(
              color: PurchaseColors.subtitle,
              fontSize: 12,
              height: 1.4,
            ),
          ),
        ),
      ],
    );
  }
}