my_tab.dart 11.1 KB
import 'package:cached_network_image/cached_network_image.dart';
import 'package:doublefeel_flutter/app/models/input_dialog_meta_data.dart';
import 'package:doublefeel_flutter/app/modules/home/controllers/my_controller.dart';
import 'package:doublefeel_flutter/app/modules/home/widgets/my/account_setting_view.dart';
import 'package:doublefeel_flutter/app/routes/app_pages.dart';
import 'package:doublefeel_flutter/app/utils/dialog_utils.dart';
import 'package:doublefeel_flutter/core/theme/app_theme.dart';
import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart';
import 'package:doublefeel_flutter/data/models/local/user_preferences.dart';
import 'package:doublefeel_flutter/data/models/user/user_models.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';

class MyTab extends GetView<MyController> {
  const MyTab({super.key});

  static const _bgColor = Color(0xFFF5F2FF);

  @override
  Widget build(BuildContext context) {
    final userPrefs = Get.find<UserPreferencesStorage>();

    return Container(
      color: _bgColor,
      child: SafeArea(
        bottom: false,
        child: Obx(() {
          final preferences = userPrefs.preferences.value;
          final me = preferences.meUserInfo;
          final vipInfo = preferences.vipInfo;

          return ListView(
            physics: const ClampingScrollPhysics(),
            padding: EdgeInsets.fromLTRB(
              16,
              59,
              16,
              150 + MediaQuery.paddingOf(context).bottom,
            ),
            children: [
              Padding(
                padding: const EdgeInsets.only(left: 8),
                child: _ProfileHeader(user: me),
              ),
              const SizedBox(height: 28),
              _ProCard(vipInfo: vipInfo),
              const SizedBox(height: 12),
              const _WatchThemeCard(),
              const SizedBox(height: 12),
              _SettingsRow(
                title: '账号信息',
                onTap: () => Get.to(() => const AccountSettingView()),
              ),
              const SizedBox(height: 12),
              _SettingsRow(
                title: '帮助',
                onTap: () => Get.toNamed(Routes.HELP),
              ),
              if (kDebugMode) ...[
                const SizedBox(height: 12),
                _SettingsRow(
                  title: 'Route List',
                  onTap: () {
                    Get.toNamed(Routes.ROUTE_LIST);
                  },
                ),
              ],
            ],
          );
        }),
      ),
    );
  }
}

class _ProfileHeader extends StatelessWidget {
  const _ProfileHeader({required this.user});

  final UserInfoResponse? user;

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 80,
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _Avatar(avatarUrl: user?.avatar ?? ''),
          const SizedBox(width: 12),
          Expanded(
            child: Padding(
              padding: const EdgeInsets.only(top: 11),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Row(
                    children: [
                      Flexible(
                        child: Text(
                          user?.nickname ?? '',
                          maxLines: 1,
                          overflow: TextOverflow.ellipsis,
                          style: TextStyle(
                            color: context.colors.textPrimary,
                            fontSize: 20,
                            fontWeight: FontWeight.w500,
                            height: 1.4,
                          ),
                        ),
                      ),
                      const SizedBox(width: 8),
                      GestureDetector(
                        behavior: HitTestBehavior.opaque,
                        onTap: () async {
                          await DialogUtils.showInputDialog(
                            InputDialogMetaData(
                              title: '修改昵称',
                              initialValue: user?.nickname ?? '',
                              hintText: '请输入昵称',
                              confirmText: '保存',
                              maxLength: 6,
                            ),
                          );
                        },
                        child: Image.asset(
                          'assets/images/common/ic_edit_outlined.webp',
                          width: 16,
                          height: 16,
                        ),
                      ),
                    ],
                  ),
                  const SizedBox(height: 1),
                  Text(
                    'ID:${user?.id ?? 0}',
                    maxLines: 1,
                    overflow: TextOverflow.ellipsis,
                    style: TextStyle(
                      color: context.colors.textSecondary,
                      fontSize: 14,
                      fontWeight: FontWeight.w400,
                      height: 1.4,
                    ),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class _Avatar extends StatelessWidget {
  const _Avatar({required this.avatarUrl});

  final String avatarUrl;

  @override
  Widget build(BuildContext context) {
    return Stack(
      clipBehavior: Clip.none,
      children: [
        ClipOval(
          child: SizedBox(
            width: 80,
            height: 80,
            child: CachedNetworkImage(
              imageUrl: avatarUrl,
              fit: BoxFit.cover,
            ),
          ),
        ),
        Positioned(
          right: 0,
          bottom: 0,
          child: GestureDetector(
            behavior: HitTestBehavior.opaque,
            onTap: () {},
            child: Container(
              width: 24,
              height: 24,
              decoration: const BoxDecoration(
                color: Color(0xFFEBE5FF),
                shape: BoxShape.circle,
              ),
              padding: const EdgeInsets.all(4),
              child: Image.asset(
                'assets/images/common/ic_camera_outlined.webp',
                width: 16,
                height: 16,
              ),
            ),
          ),
        ),
      ],
    );
  }
}

class _ProCard extends StatelessWidget {
  const _ProCard({required this.vipInfo});

  final UserPreferencesVipInfo? vipInfo;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      behavior: HitTestBehavior.opaque,
      onTap: () => Get.toNamed(Routes.PURCHASE),
      child: Container(
        height: 80,
        padding: const EdgeInsets.fromLTRB(20, 14, 20, 14),
        decoration: BoxDecoration(
          gradient: const LinearGradient(
            begin: Alignment.centerLeft,
            end: Alignment.centerRight,
            colors: [
              Color(0xFF916DF5),
              Color(0xFF8D64F4),
            ],
          ),
          borderRadius: BorderRadius.circular(16),
        ),
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Row(
                    children: [
                      const Flexible(
                        child: Text(
                          'DoubleFeel Pro',
                          maxLines: 1,
                          overflow: TextOverflow.ellipsis,
                          style: TextStyle(
                            color: Colors.white,
                            fontSize: 18,
                            fontWeight: FontWeight.w600,
                            height: 1.4,
                          ),
                        ),
                      ),
                      const SizedBox(width: 6),
                      Image.asset(
                        'assets/images/common/ic_pro_badge.webp',
                        width: 43,
                        height: 16,
                      ),
                    ],
                  ),
                  const SizedBox(height: 2),
                  Text(
                    _vipSubtitle(vipInfo),
                    maxLines: 1,
                    overflow: TextOverflow.ellipsis,
                    style: const TextStyle(
                      color: Color(0xFFC6B3FF),
                      fontSize: 12,
                      fontWeight: FontWeight.w500,
                      height: 1.4,
                    ),
                  ),
                ],
              ),
            ),
            const SizedBox(width: 12),
            Padding(
              padding: const EdgeInsets.only(top: 5),
              child: Image.asset(
                'assets/images/common/ic_more_gray.png',
                width: 16,
                height: 16,
                color: Colors.white,
              ),
            ),
          ],
        ),
      ),
    );
  }

  String _vipSubtitle(UserPreferencesVipInfo? vipInfo) {
    final endDate = vipInfo?.vipEndDate ?? 0;
    if (vipInfo?.isVip != true || endDate <= 0) {
      return '立即解锁';
    }

    if (vipInfo?.isForeverVip ?? false) {
      return '终身会员';
    }

    final milliseconds = endDate > 1000000000000 ? endDate : endDate * 1000;
    final date = DateTime.fromMillisecondsSinceEpoch(milliseconds);
    final month = date.month.toString().padLeft(2, '0');
    final day = date.day.toString().padLeft(2, '0');
    return '有效期至 ${date.year}-$month-$day';
  }
}

class _WatchThemeCard extends StatelessWidget {
  const _WatchThemeCard();

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 145,
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(16),
      ),
      child: Image.asset('assets/images/my/ic_my_watch_theme.png'),
    );
  }
}

class _SettingsRow extends StatelessWidget {
  const _SettingsRow({required this.title, required this.onTap});

  final String title;
  final VoidCallback onTap;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      behavior: HitTestBehavior.opaque,
      onTap: onTap,
      child: Container(
        height: 56,
        padding: const EdgeInsets.symmetric(horizontal: 20),
        decoration: BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.circular(16),
        ),
        child: Row(
          children: [
            Expanded(
              child: Text(
                title,
                maxLines: 1,
                overflow: TextOverflow.ellipsis,
                style: TextStyle(
                  color: context.colors.textPrimary,
                  fontSize: 14,
                  fontWeight: FontWeight.w400,
                  height: 1.4,
                ),
              ),
            ),
            Image.asset(
              'assets/images/common/ic_more_gray.png',
              width: 16,
              height: 16,
            ),
          ],
        ),
      ),
    );
  }
}