|
|
|
import 'package:doublefeel_flutter/app/modules/watch_theme/models/watch_theme_models.dart';
|
|
|
|
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/core/theme/app_colors.dart';
|
|
|
|
import 'package:doublefeel_flutter/core/util/size_extensions.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:doublefeel_flutter/r.dart';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:get/get.dart';
|
|
|
|
|
|
|
|
class MyTab extends StatelessWidget {
|
|
|
|
class MyTab extends GetView<MyController> {
|
|
|
|
const MyTab({super.key});
|
|
|
|
|
|
|
|
static const _background = Color(0xFFF5F2FF);
|
|
|
|
static const _proPurple = Color(0xFF916DF5);
|
|
|
|
static const _proGold = Color(0xFFFFDF51);
|
|
|
|
static const _bgColor = Color(0xFFF5F2FF);
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
final userPrefs = Get.find<UserPreferencesStorage>();
|
|
|
|
|
|
|
|
return Container(
|
|
|
|
color: _background,
|
|
|
|
color: _bgColor,
|
|
|
|
child: SafeArea(
|
|
|
|
bottom: false,
|
|
|
|
child: Obx(() {
|
|
|
|
final user = userPrefs.preferences.value.meUserInfo;
|
|
|
|
final preferences = userPrefs.preferences.value;
|
|
|
|
final me = preferences.meUserInfo;
|
|
|
|
final vipInfo = preferences.vipInfo;
|
|
|
|
|
|
|
|
return ListView(
|
|
|
|
physics: const ClampingScrollPhysics(),
|
|
|
|
padding: EdgeInsets.fromLTRB(16.dp, 59.dp, 16.dp, 112.dp),
|
|
|
|
padding: EdgeInsets.fromLTRB(
|
|
|
|
16,
|
|
|
|
59,
|
|
|
|
16,
|
|
|
|
150 + MediaQuery.paddingOf(context).bottom,
|
|
|
|
),
|
|
|
|
children: [
|
|
|
|
_ProfileHeader(
|
|
|
|
user: user,
|
|
|
|
onTap: () => Get.toNamed(Routes.ACCOUNT_SETTINGS),
|
|
|
|
Padding(
|
|
|
|
padding: const EdgeInsets.only(left: 8),
|
|
|
|
child: _ProfileHeader(user: me),
|
|
|
|
),
|
|
|
|
SizedBox(height: 28.dp),
|
|
|
|
const _PremiumCard(),
|
|
|
|
SizedBox(height: 12.dp),
|
|
|
|
_WatchThemeCard(
|
|
|
|
onTap: () => Get.toNamed(
|
|
|
|
Routes.WATCH_THEME,
|
|
|
|
arguments: {'isPremium': false, 'hasCustomThemes': false},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
SizedBox(height: 12.dp),
|
|
|
|
_MenuTile(
|
|
|
|
const SizedBox(height: 28),
|
|
|
|
_ProCard(vipInfo: vipInfo),
|
|
|
|
const SizedBox(height: 12),
|
|
|
|
const _WatchThemeCard(),
|
|
|
|
const SizedBox(height: 12),
|
|
|
|
_SettingsRow(
|
|
|
|
title: '账号信息',
|
|
|
|
onTap: () => Get.toNamed(Routes.ACCOUNT_SETTINGS),
|
|
|
|
onTap: () => Get.to(() => const AccountSettingView()),
|
|
|
|
),
|
|
|
|
const SizedBox(height: 12),
|
|
|
|
_SettingsRow(
|
|
|
|
title: '帮助',
|
|
|
|
onTap: () => Get.toNamed(Routes.HELP),
|
|
|
|
),
|
|
|
|
SizedBox(height: 12.dp),
|
|
|
|
_MenuTile(title: '帮助', onTap: () {}),
|
|
|
|
if (kDebugMode) ...[
|
|
|
|
const SizedBox(height: 12),
|
|
|
|
_SettingsRow(
|
|
|
|
title: 'Route List',
|
|
|
|
onTap: () {
|
|
|
|
Get.toNamed(Routes.ROUTE_LIST);
|
|
|
|
},
|
|
|
|
),
|
|
|
|
],
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}),
|
|
...
|
...
|
@@ -57,135 +75,135 @@ class MyTab extends StatelessWidget { |
|
|
|
}
|
|
|
|
|
|
|
|
class _ProfileHeader extends StatelessWidget {
|
|
|
|
const _ProfileHeader({
|
|
|
|
required this.user,
|
|
|
|
required this.onTap,
|
|
|
|
});
|
|
|
|
const _ProfileHeader({required this.user});
|
|
|
|
|
|
|
|
final UserInfoResponse? user;
|
|
|
|
final VoidCallback onTap;
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return GestureDetector(
|
|
|
|
behavior: HitTestBehavior.opaque,
|
|
|
|
onTap: onTap,
|
|
|
|
child: SizedBox(
|
|
|
|
height: 80.dp,
|
|
|
|
child: Row(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
|
children: [
|
|
|
|
_Avatar(avatarUrl: user?.avatar),
|
|
|
|
SizedBox(width: 12.dp),
|
|
|
|
Expanded(
|
|
|
|
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(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
Row(
|
|
|
|
children: [
|
|
|
|
Flexible(
|
|
|
|
child: Text(
|
|
|
|
_displayName(user),
|
|
|
|
user?.nickname ?? '',
|
|
|
|
maxLines: 1,
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
style: TextStyle(
|
|
|
|
color: AppColors.textPrimary,
|
|
|
|
fontSize: 20.dp,
|
|
|
|
color: context.colors.textPrimary,
|
|
|
|
fontSize: 20,
|
|
|
|
fontWeight: FontWeight.w500,
|
|
|
|
height: 1.25,
|
|
|
|
height: 1.4,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
SizedBox(width: 8.dp),
|
|
|
|
Icon(
|
|
|
|
Icons.edit_outlined,
|
|
|
|
color: const Color(0xFFA084EF),
|
|
|
|
size: 16.dp,
|
|
|
|
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,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
SizedBox(height: 6.dp),
|
|
|
|
const SizedBox(height: 1),
|
|
|
|
Text(
|
|
|
|
'ID:${user?.id ?? 738293}',
|
|
|
|
'ID:${user?.id ?? 0}',
|
|
|
|
maxLines: 1,
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
style: TextStyle(
|
|
|
|
color: AppColors.textSecondary,
|
|
|
|
fontSize: 14.dp,
|
|
|
|
color: context.colors.textSecondary,
|
|
|
|
fontSize: 14,
|
|
|
|
fontWeight: FontWeight.w400,
|
|
|
|
height: 1.25,
|
|
|
|
height: 1.4,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
String _displayName(UserInfoResponse? user) {
|
|
|
|
final name = user?.nickname?.trim();
|
|
|
|
if (name != null && name.isNotEmpty) {
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
return '不爱吃热干面';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class _Avatar extends StatelessWidget {
|
|
|
|
const _Avatar({this.avatarUrl});
|
|
|
|
const _Avatar({required this.avatarUrl});
|
|
|
|
|
|
|
|
final String? avatarUrl;
|
|
|
|
final String avatarUrl;
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
final url = avatarUrl?.trim();
|
|
|
|
final imageProvider = url == null || url.isEmpty
|
|
|
|
? AssetImage(R.assetsImagesMyAvatarDefault) as ImageProvider
|
|
|
|
: NetworkImage(url);
|
|
|
|
|
|
|
|
return SizedBox(
|
|
|
|
width: 80.dp,
|
|
|
|
height: 80.dp,
|
|
|
|
child: Stack(
|
|
|
|
children: [
|
|
|
|
ClipOval(
|
|
|
|
child: Image(
|
|
|
|
image: imageProvider,
|
|
|
|
width: 80.dp,
|
|
|
|
height: 80.dp,
|
|
|
|
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,
|
|
|
|
),
|
|
|
|
Positioned(
|
|
|
|
right: 0,
|
|
|
|
bottom: 0,
|
|
|
|
child: GestureDetector(
|
|
|
|
behavior: HitTestBehavior.opaque,
|
|
|
|
onTap: () {},
|
|
|
|
child: Container(
|
|
|
|
width: 24.dp,
|
|
|
|
height: 24.dp,
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
color: const Color(0xFFE8E0FF),
|
|
|
|
width: 24,
|
|
|
|
height: 24,
|
|
|
|
decoration: const BoxDecoration(
|
|
|
|
color: Color(0xFFEBE5FF),
|
|
|
|
shape: BoxShape.circle,
|
|
|
|
border: Border.all(color: MyTab._background, width: 2.dp),
|
|
|
|
),
|
|
|
|
child: Icon(
|
|
|
|
Icons.photo_camera_outlined,
|
|
|
|
color: const Color(0xFFA084EF),
|
|
|
|
size: 14.dp,
|
|
|
|
padding: const EdgeInsets.all(4),
|
|
|
|
child: Image.asset(
|
|
|
|
'assets/images/common/ic_camera_outlined.webp',
|
|
|
|
width: 16,
|
|
|
|
height: 16,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class _PremiumCard extends StatelessWidget {
|
|
|
|
const _PremiumCard();
|
|
|
|
class _ProCard extends StatelessWidget {
|
|
|
|
const _ProCard({required this.vipInfo});
|
|
|
|
|
|
|
|
final UserPreferencesVipInfo? vipInfo;
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
...
|
...
|
@@ -193,240 +211,116 @@ class _PremiumCard extends StatelessWidget { |
|
|
|
behavior: HitTestBehavior.opaque,
|
|
|
|
onTap: () => Get.toNamed(Routes.PURCHASE),
|
|
|
|
child: Container(
|
|
|
|
height: 128.dp,
|
|
|
|
height: 80,
|
|
|
|
padding: const EdgeInsets.fromLTRB(20, 14, 20, 14),
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
color: MyTab._proPurple,
|
|
|
|
borderRadius: BorderRadius.circular(16.dp),
|
|
|
|
gradient: const LinearGradient(
|
|
|
|
begin: Alignment.centerLeft,
|
|
|
|
end: Alignment.centerRight,
|
|
|
|
colors: [
|
|
|
|
Color(0xFF916DF5),
|
|
|
|
Color(0xFF8D64F4),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
borderRadius: BorderRadius.circular(16),
|
|
|
|
),
|
|
|
|
clipBehavior: Clip.antiAlias,
|
|
|
|
child: Stack(
|
|
|
|
child: Row(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
Positioned(
|
|
|
|
right: 0,
|
|
|
|
bottom: 0,
|
|
|
|
child: SizedBox(
|
|
|
|
width: 151.dp,
|
|
|
|
height: 114.dp,
|
|
|
|
child: Stack(
|
|
|
|
alignment: Alignment.bottomRight,
|
|
|
|
children: [
|
|
|
|
Positioned(
|
|
|
|
left: 0,
|
|
|
|
bottom: -14.dp,
|
|
|
|
child: Container(
|
|
|
|
width: 86.dp,
|
|
|
|
height: 110.dp,
|
|
|
|
decoration: const BoxDecoration(
|
|
|
|
color: Color(0xFFD8CFF7),
|
|
|
|
borderRadius: BorderRadius.only(
|
|
|
|
topLeft: Radius.circular(12),
|
|
|
|
topRight: Radius.circular(12),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Container(
|
|
|
|
width: 80.dp,
|
|
|
|
height: 120.dp,
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
color: Colors.white,
|
|
|
|
borderRadius: BorderRadius.only(
|
|
|
|
topLeft: Radius.circular(8.dp),
|
|
|
|
topRight: Radius.circular(8.dp),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Padding(
|
|
|
|
padding: EdgeInsets.fromLTRB(20.dp, 20.dp, 20.dp, 20.dp),
|
|
|
|
Expanded(
|
|
|
|
child: Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
ShaderMask(
|
|
|
|
blendMode: BlendMode.srcIn,
|
|
|
|
shaderCallback: (bounds) => const LinearGradient(
|
|
|
|
begin: Alignment.topCenter,
|
|
|
|
end: Alignment.bottomCenter,
|
|
|
|
colors: [
|
|
|
|
Color(0xFFFFE8AE),
|
|
|
|
Color(0xFFFFFAED),
|
|
|
|
Colors.white,
|
|
|
|
],
|
|
|
|
).createShader(bounds),
|
|
|
|
child: Text(
|
|
|
|
'解锁专业版',
|
|
|
|
style: TextStyle(
|
|
|
|
fontSize: 16.dp,
|
|
|
|
fontWeight: FontWeight.w600,
|
|
|
|
height: 1.25,
|
|
|
|
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,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
SizedBox(height: 5.dp),
|
|
|
|
const SizedBox(height: 2),
|
|
|
|
Text(
|
|
|
|
'开启压力预警与健康陪伴之旅',
|
|
|
|
style: TextStyle(
|
|
|
|
color: const Color(0xFFC6B3FF),
|
|
|
|
fontSize: 12.dp,
|
|
|
|
_vipSubtitle(vipInfo),
|
|
|
|
maxLines: 1,
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
style: const TextStyle(
|
|
|
|
color: Color(0xFFC6B3FF),
|
|
|
|
fontSize: 12,
|
|
|
|
fontWeight: FontWeight.w500,
|
|
|
|
height: 1.25,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
const Spacer(),
|
|
|
|
Container(
|
|
|
|
height: 32.dp,
|
|
|
|
width: 129.dp,
|
|
|
|
alignment: Alignment.center,
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
color: MyTab._proGold,
|
|
|
|
borderRadius: BorderRadius.circular(24.dp),
|
|
|
|
),
|
|
|
|
child: Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
|
children: [
|
|
|
|
Icon(
|
|
|
|
Icons.workspace_premium,
|
|
|
|
color: AppColors.textPrimary,
|
|
|
|
size: 16.dp,
|
|
|
|
),
|
|
|
|
SizedBox(width: 4.dp),
|
|
|
|
Text(
|
|
|
|
'立即解锁',
|
|
|
|
style: TextStyle(
|
|
|
|
color: AppColors.textPrimary,
|
|
|
|
fontSize: 14.dp,
|
|
|
|
fontWeight: FontWeight.w500,
|
|
|
|
height: 1.25,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
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,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class _WatchThemeCard extends StatelessWidget {
|
|
|
|
const _WatchThemeCard({required this.onTap});
|
|
|
|
|
|
|
|
final VoidCallback onTap;
|
|
|
|
|
|
|
|
final double _itemSize = 64;
|
|
|
|
final double _overlap = 8;
|
|
|
|
String _vipSubtitle(UserPreferencesVipInfo? vipInfo) {
|
|
|
|
final endDate = vipInfo?.vipEndDate ?? 0;
|
|
|
|
if (vipInfo?.isVip != true || endDate <= 0) {
|
|
|
|
return '立即解锁';
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return GestureDetector(
|
|
|
|
behavior: HitTestBehavior.opaque,
|
|
|
|
onTap: onTap,
|
|
|
|
child: Container(
|
|
|
|
padding: EdgeInsets.fromLTRB(20.dp, 20.dp, 20.dp, 18.dp),
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
color: Colors.white,
|
|
|
|
borderRadius: BorderRadius.circular(16.dp),
|
|
|
|
),
|
|
|
|
child: Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
Row(
|
|
|
|
children: [
|
|
|
|
Text(
|
|
|
|
'Watch主题',
|
|
|
|
style: TextStyle(
|
|
|
|
color: AppColors.textPrimary,
|
|
|
|
fontSize: 16.dp,
|
|
|
|
fontWeight: FontWeight.w600,
|
|
|
|
height: 1.25,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
const Spacer(),
|
|
|
|
Icon(
|
|
|
|
Icons.chevron_right,
|
|
|
|
color: AppColors.textTertiary,
|
|
|
|
size: 20.dp,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
SizedBox(height: 17.dp),
|
|
|
|
SizedBox(
|
|
|
|
height: _itemSize,
|
|
|
|
child: ListView.builder(
|
|
|
|
scrollDirection: Axis.horizontal,
|
|
|
|
clipBehavior: Clip.none,
|
|
|
|
itemCount: officialThemes.length,
|
|
|
|
itemBuilder: (context, index) {
|
|
|
|
return Transform.translate(
|
|
|
|
offset: Offset(index == 0 ? 0 : -_overlap * index, 0),
|
|
|
|
child: _ThemeBubble(
|
|
|
|
size: _itemSize,
|
|
|
|
assetPath: officialThemes[index]
|
|
|
|
.infoList
|
|
|
|
.firstOrNull
|
|
|
|
?.assetPath ??
|
|
|
|
'',
|
|
|
|
));
|
|
|
|
},
|
|
|
|
if (vipInfo?.isForeverVip ?? false) {
|
|
|
|
return '终身会员';
|
|
|
|
}
|
|
|
|
|
|
|
|
),
|
|
|
|
),
|
|
|
|
SizedBox(height: 10.dp),
|
|
|
|
Text(
|
|
|
|
'支持自定义创作主题哦~',
|
|
|
|
style: TextStyle(
|
|
|
|
color: AppColors.primary,
|
|
|
|
fontSize: 12.dp,
|
|
|
|
fontWeight: FontWeight.w500,
|
|
|
|
height: 1.25,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
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 _ThemeBubble extends StatelessWidget {
|
|
|
|
const _ThemeBubble({required this.size, required this.assetPath});
|
|
|
|
|
|
|
|
final double size;
|
|
|
|
final String assetPath;
|
|
|
|
class _WatchThemeCard extends StatelessWidget {
|
|
|
|
const _WatchThemeCard();
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return Container(
|
|
|
|
width: size,
|
|
|
|
height: size,
|
|
|
|
height: 145,
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
color: const Color(0xFFEDE8FF),
|
|
|
|
shape: BoxShape.circle,
|
|
|
|
border: Border.all(color: Colors.white, width: 4),
|
|
|
|
),
|
|
|
|
alignment: Alignment.center,
|
|
|
|
child: Image.asset(
|
|
|
|
assetPath,
|
|
|
|
width: 36,
|
|
|
|
height: 36,
|
|
|
|
fit: BoxFit.fill,
|
|
|
|
color: Colors.white,
|
|
|
|
borderRadius: BorderRadius.circular(16),
|
|
|
|
),
|
|
|
|
child: Image.asset('assets/images/my/ic_my_watch_theme.png'),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class _MenuTile extends StatelessWidget {
|
|
|
|
const _MenuTile({
|
|
|
|
required this.title,
|
|
|
|
required this.onTap,
|
|
|
|
});
|
|
|
|
class _SettingsRow extends StatelessWidget {
|
|
|
|
const _SettingsRow({required this.title, required this.onTap});
|
|
|
|
|
|
|
|
final String title;
|
|
|
|
final VoidCallback onTap;
|
|
...
|
...
|
@@ -437,28 +331,31 @@ class _MenuTile extends StatelessWidget { |
|
|
|
behavior: HitTestBehavior.opaque,
|
|
|
|
onTap: onTap,
|
|
|
|
child: Container(
|
|
|
|
height: 56.dp,
|
|
|
|
padding: EdgeInsets.symmetric(horizontal: 20.dp),
|
|
|
|
height: 56,
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
color: Colors.white,
|
|
|
|
borderRadius: BorderRadius.circular(16.dp),
|
|
|
|
borderRadius: BorderRadius.circular(16),
|
|
|
|
),
|
|
|
|
child: Row(
|
|
|
|
children: [
|
|
|
|
Text(
|
|
|
|
title,
|
|
|
|
style: TextStyle(
|
|
|
|
color: AppColors.textPrimary,
|
|
|
|
fontSize: 14.dp,
|
|
|
|
fontWeight: FontWeight.w400,
|
|
|
|
height: 1.25,
|
|
|
|
Expanded(
|
|
|
|
child: Text(
|
|
|
|
title,
|
|
|
|
maxLines: 1,
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
style: TextStyle(
|
|
|
|
color: context.colors.textPrimary,
|
|
|
|
fontSize: 14,
|
|
|
|
fontWeight: FontWeight.w400,
|
|
|
|
height: 1.4,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
const Spacer(),
|
|
|
|
Icon(
|
|
|
|
Icons.chevron_right,
|
|
|
|
color: AppColors.textTertiary,
|
|
|
|
size: 20.dp,
|
|
|
|
Image.asset(
|
|
|
|
'assets/images/common/ic_more_gray.png',
|
|
|
|
width: 16,
|
|
|
|
height: 16,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
...
|
...
|
|