|
@@ -8,7 +8,11 @@ import 'package:nomo/app/widgets/ix_app_bar.dart';
|
|
|
import 'package:nomo/config/theme/theme_extensions/theme_extension.dart';
|
|
import 'package:nomo/config/theme/theme_extensions/theme_extension.dart';
|
|
|
import 'package:pinput/pinput.dart';
|
|
import 'package:pinput/pinput.dart';
|
|
|
|
|
|
|
|
|
|
+import '../../../../config/theme/dark_theme_colors.dart';
|
|
|
|
|
+import '../../../constants/iconfont/iconfont.dart';
|
|
|
|
|
+import '../../../widgets/info_card.dart';
|
|
|
import '../controllers/deviceauth_controller.dart';
|
|
import '../controllers/deviceauth_controller.dart';
|
|
|
|
|
+import '../widgets/device_card.dart';
|
|
|
|
|
|
|
|
class DeviceauthView extends BaseView<DeviceauthController> {
|
|
class DeviceauthView extends BaseView<DeviceauthController> {
|
|
|
const DeviceauthView({super.key});
|
|
const DeviceauthView({super.key});
|
|
@@ -21,7 +25,7 @@ class DeviceauthView extends BaseView<DeviceauthController> {
|
|
|
return Obx(() {
|
|
return Obx(() {
|
|
|
final isPremium = controller.isPremium.value;
|
|
final isPremium = controller.isPremium.value;
|
|
|
return SingleChildScrollView(
|
|
return SingleChildScrollView(
|
|
|
- padding: EdgeInsets.symmetric(horizontal: 14.w, vertical: 20.h),
|
|
|
|
|
|
|
+ padding: EdgeInsets.symmetric(horizontal: 14.w, vertical: 20.w),
|
|
|
child: Column(
|
|
child: Column(
|
|
|
children: [
|
|
children: [
|
|
|
if (isPremium) ...[
|
|
if (isPremium) ...[
|
|
@@ -43,9 +47,6 @@ class DeviceauthView extends BaseView<DeviceauthController> {
|
|
|
children: [
|
|
children: [
|
|
|
// 授权码显示区域
|
|
// 授权码显示区域
|
|
|
_buildAuthCodeDisplay(),
|
|
_buildAuthCodeDisplay(),
|
|
|
-
|
|
|
|
|
- 30.verticalSpaceFromWidth,
|
|
|
|
|
-
|
|
|
|
|
// 说明卡片
|
|
// 说明卡片
|
|
|
_buildInstructionCards(),
|
|
_buildInstructionCards(),
|
|
|
],
|
|
],
|
|
@@ -61,11 +62,14 @@ class DeviceauthView extends BaseView<DeviceauthController> {
|
|
|
|
|
|
|
|
20.verticalSpaceFromWidth,
|
|
20.verticalSpaceFromWidth,
|
|
|
|
|
|
|
|
|
|
+ Padding(
|
|
|
|
|
+ padding: EdgeInsets.symmetric(horizontal: 8.w),
|
|
|
|
|
+ child: Divider(height: 1.w, color: Get.reactiveTheme.dividerColor),
|
|
|
|
|
+ ),
|
|
|
|
|
+
|
|
|
// 设备管理区域
|
|
// 设备管理区域
|
|
|
_buildDeviceManagementSection(),
|
|
_buildDeviceManagementSection(),
|
|
|
|
|
|
|
|
- 20.verticalSpaceFromWidth,
|
|
|
|
|
-
|
|
|
|
|
// 授权步骤说明
|
|
// 授权步骤说明
|
|
|
_buildAuthorizationSteps(),
|
|
_buildAuthorizationSteps(),
|
|
|
],
|
|
],
|
|
@@ -75,11 +79,7 @@ class DeviceauthView extends BaseView<DeviceauthController> {
|
|
|
/// 授权码显示区域
|
|
/// 授权码显示区域
|
|
|
Widget _buildAuthCodeDisplay() {
|
|
Widget _buildAuthCodeDisplay() {
|
|
|
return Container(
|
|
return Container(
|
|
|
- padding: EdgeInsets.all(24.w),
|
|
|
|
|
- decoration: BoxDecoration(
|
|
|
|
|
- color: Get.reactiveTheme.highlightColor,
|
|
|
|
|
- borderRadius: BorderRadius.circular(16.r),
|
|
|
|
|
- ),
|
|
|
|
|
|
|
+ padding: EdgeInsets.symmetric(horizontal: 14.w, vertical: 10.w),
|
|
|
child: Column(
|
|
child: Column(
|
|
|
children: [
|
|
children: [
|
|
|
// 6位授权码
|
|
// 6位授权码
|
|
@@ -87,42 +87,44 @@ class DeviceauthView extends BaseView<DeviceauthController> {
|
|
|
() => Text(
|
|
() => Text(
|
|
|
controller.authCode.value,
|
|
controller.authCode.value,
|
|
|
style: TextStyle(
|
|
style: TextStyle(
|
|
|
- fontSize: 48.sp,
|
|
|
|
|
- fontWeight: FontWeight.bold,
|
|
|
|
|
- color: Colors.white,
|
|
|
|
|
- letterSpacing: 4.w,
|
|
|
|
|
|
|
+ fontSize: 34.sp,
|
|
|
|
|
+ fontWeight: FontWeight.w500,
|
|
|
|
|
+ height: 1.2,
|
|
|
|
|
+ color: Get.reactiveTheme.textTheme.bodyLarge!.color,
|
|
|
|
|
+ letterSpacing: 10.w,
|
|
|
),
|
|
),
|
|
|
),
|
|
),
|
|
|
),
|
|
),
|
|
|
|
|
|
|
|
- 20.verticalSpaceFromWidth,
|
|
|
|
|
|
|
+ 14.verticalSpaceFromWidth,
|
|
|
|
|
|
|
|
// 倒计时和复制按钮
|
|
// 倒计时和复制按钮
|
|
|
Row(
|
|
Row(
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
children: [
|
|
children: [
|
|
|
Icon(
|
|
Icon(
|
|
|
- Icons.access_time,
|
|
|
|
|
- size: 16.w,
|
|
|
|
|
|
|
+ IconFont.icon48,
|
|
|
|
|
+ size: 20.w,
|
|
|
color: Get.reactiveTheme.hintColor,
|
|
color: Get.reactiveTheme.hintColor,
|
|
|
),
|
|
),
|
|
|
- SizedBox(width: 8.w),
|
|
|
|
|
|
|
+ 4.horizontalSpace,
|
|
|
Obx(
|
|
Obx(
|
|
|
() => Text(
|
|
() => Text(
|
|
|
controller.countdownText,
|
|
controller.countdownText,
|
|
|
style: TextStyle(
|
|
style: TextStyle(
|
|
|
- fontSize: 16.sp,
|
|
|
|
|
|
|
+ fontSize: 12.sp,
|
|
|
|
|
+ height: 1.7,
|
|
|
color: Get.reactiveTheme.hintColor,
|
|
color: Get.reactiveTheme.hintColor,
|
|
|
),
|
|
),
|
|
|
),
|
|
),
|
|
|
),
|
|
),
|
|
|
- SizedBox(width: 16.w),
|
|
|
|
|
|
|
+ 20.horizontalSpace,
|
|
|
Container(
|
|
Container(
|
|
|
- width: 1,
|
|
|
|
|
- height: 20.h,
|
|
|
|
|
- color: Get.reactiveTheme.hintColor.withOpacity(0.3),
|
|
|
|
|
|
|
+ width: 1.w,
|
|
|
|
|
+ height: 20.w,
|
|
|
|
|
+ color: Get.reactiveTheme.dividerColor,
|
|
|
),
|
|
),
|
|
|
- SizedBox(width: 16.w),
|
|
|
|
|
|
|
+ 20.horizontalSpace,
|
|
|
ClickOpacity(
|
|
ClickOpacity(
|
|
|
onTap: () {
|
|
onTap: () {
|
|
|
Clipboard.setData(
|
|
Clipboard.setData(
|
|
@@ -136,14 +138,15 @@ class DeviceauthView extends BaseView<DeviceauthController> {
|
|
|
Text(
|
|
Text(
|
|
|
'Copy',
|
|
'Copy',
|
|
|
style: TextStyle(
|
|
style: TextStyle(
|
|
|
- fontSize: 16.sp,
|
|
|
|
|
|
|
+ fontSize: 12.sp,
|
|
|
|
|
+ height: 1.7,
|
|
|
color: Get.reactiveTheme.hintColor,
|
|
color: Get.reactiveTheme.hintColor,
|
|
|
),
|
|
),
|
|
|
),
|
|
),
|
|
|
- SizedBox(width: 4.w),
|
|
|
|
|
|
|
+ 4.horizontalSpace,
|
|
|
Icon(
|
|
Icon(
|
|
|
- Icons.copy,
|
|
|
|
|
- size: 16.w,
|
|
|
|
|
|
|
+ IconFont.icon57,
|
|
|
|
|
+ size: 20.w,
|
|
|
color: Get.reactiveTheme.hintColor,
|
|
color: Get.reactiveTheme.hintColor,
|
|
|
),
|
|
),
|
|
|
],
|
|
],
|
|
@@ -158,9 +161,9 @@ class DeviceauthView extends BaseView<DeviceauthController> {
|
|
|
Text(
|
|
Text(
|
|
|
'Please keep this page open.',
|
|
'Please keep this page open.',
|
|
|
style: TextStyle(
|
|
style: TextStyle(
|
|
|
- fontSize: 16.sp,
|
|
|
|
|
- color: const Color(0xFFFFB800),
|
|
|
|
|
- fontWeight: FontWeight.w500,
|
|
|
|
|
|
|
+ fontSize: 13.sp,
|
|
|
|
|
+ color: DarkThemeColors.validTermColor,
|
|
|
|
|
+ height: 1.4,
|
|
|
),
|
|
),
|
|
|
),
|
|
),
|
|
|
],
|
|
],
|
|
@@ -170,113 +173,29 @@ class DeviceauthView extends BaseView<DeviceauthController> {
|
|
|
|
|
|
|
|
/// 说明卡片
|
|
/// 说明卡片
|
|
|
Widget _buildInstructionCards() {
|
|
Widget _buildInstructionCards() {
|
|
|
- return Container(
|
|
|
|
|
- padding: EdgeInsets.all(20.w),
|
|
|
|
|
- decoration: BoxDecoration(
|
|
|
|
|
- color: Get.reactiveTheme.hintColor.withOpacity(0.1),
|
|
|
|
|
- borderRadius: BorderRadius.circular(16.r),
|
|
|
|
|
- border: Border.all(
|
|
|
|
|
- color: const Color(0xFF00A8E8).withOpacity(0.3),
|
|
|
|
|
- width: 1,
|
|
|
|
|
|
|
+ return InfoCard(
|
|
|
|
|
+ title: '',
|
|
|
|
|
+ items: [
|
|
|
|
|
+ InfoItem(
|
|
|
|
|
+ icon: IconFont.icon11,
|
|
|
|
|
+ title: 'Authorization Code',
|
|
|
|
|
+ description:
|
|
|
|
|
+ 'This 6-digit code allows a VIP user to link your device. It refreshes every 15 minutes.',
|
|
|
|
|
+ iconColor: DarkThemeColors.shadowColor,
|
|
|
),
|
|
),
|
|
|
- ),
|
|
|
|
|
- child: Column(
|
|
|
|
|
- children: [
|
|
|
|
|
- _buildInstructionItem(
|
|
|
|
|
- icon: Icons.vpn_key,
|
|
|
|
|
- title: 'Authorization Code',
|
|
|
|
|
- description:
|
|
|
|
|
- 'This 6-digit code allows a VIP user to link your device. It refreshes every 15 minutes.',
|
|
|
|
|
- ),
|
|
|
|
|
-
|
|
|
|
|
- 20.verticalSpaceFromWidth,
|
|
|
|
|
-
|
|
|
|
|
- _buildInstructionItem(
|
|
|
|
|
- icon: Icons.workspace_premium,
|
|
|
|
|
- title: 'Share with Pre User',
|
|
|
|
|
- description:
|
|
|
|
|
- 'Tell the VIP user this code so they can enter it on their device to authorize you.',
|
|
|
|
|
- ),
|
|
|
|
|
-
|
|
|
|
|
- 20.verticalSpaceFromWidth,
|
|
|
|
|
-
|
|
|
|
|
- _buildInstructionItem(
|
|
|
|
|
- icon: Icons.access_time,
|
|
|
|
|
- title: 'Waiting for Authorization',
|
|
|
|
|
- description:
|
|
|
|
|
- 'Please keep this page open.\nOnce approved, your account will automatically upgrade and reconnect.',
|
|
|
|
|
- highlightText: 'Please keep this page open.',
|
|
|
|
|
- ),
|
|
|
|
|
- ],
|
|
|
|
|
- ),
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// 说明条目
|
|
|
|
|
- Widget _buildInstructionItem({
|
|
|
|
|
- required IconData icon,
|
|
|
|
|
- required String title,
|
|
|
|
|
- required String description,
|
|
|
|
|
- String? highlightText,
|
|
|
|
|
- }) {
|
|
|
|
|
- return Row(
|
|
|
|
|
- crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
- children: [
|
|
|
|
|
- Container(
|
|
|
|
|
- width: 40.w,
|
|
|
|
|
- height: 40.w,
|
|
|
|
|
- decoration: BoxDecoration(
|
|
|
|
|
- color: const Color(0xFF00A8E8).withOpacity(0.2),
|
|
|
|
|
- borderRadius: BorderRadius.circular(8.r),
|
|
|
|
|
- ),
|
|
|
|
|
- child: Icon(icon, size: 20.w, color: const Color(0xFF00A8E8)),
|
|
|
|
|
|
|
+ InfoItem(
|
|
|
|
|
+ icon: IconFont.icon23,
|
|
|
|
|
+ title: 'Share with Pre User',
|
|
|
|
|
+ description:
|
|
|
|
|
+ 'Tell the VIP user this code so they can enter it on their device to authorize you.',
|
|
|
|
|
+ iconColor: DarkThemeColors.shadowColor,
|
|
|
),
|
|
),
|
|
|
- SizedBox(width: 16.w),
|
|
|
|
|
- Expanded(
|
|
|
|
|
- child: Column(
|
|
|
|
|
- crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
- children: [
|
|
|
|
|
- Text(
|
|
|
|
|
- title,
|
|
|
|
|
- style: TextStyle(
|
|
|
|
|
- fontSize: 16.sp,
|
|
|
|
|
- fontWeight: FontWeight.bold,
|
|
|
|
|
- color: Colors.white,
|
|
|
|
|
- ),
|
|
|
|
|
- ),
|
|
|
|
|
- SizedBox(height: 8.h),
|
|
|
|
|
- highlightText != null
|
|
|
|
|
- ? RichText(
|
|
|
|
|
- text: TextSpan(
|
|
|
|
|
- children: [
|
|
|
|
|
- TextSpan(
|
|
|
|
|
- text: highlightText,
|
|
|
|
|
- style: TextStyle(
|
|
|
|
|
- fontSize: 14.sp,
|
|
|
|
|
- color: const Color(0xFF00A8E8),
|
|
|
|
|
- ),
|
|
|
|
|
- ),
|
|
|
|
|
- TextSpan(
|
|
|
|
|
- text:
|
|
|
|
|
- '\n${description.substring(highlightText.length + 1)}',
|
|
|
|
|
- style: TextStyle(
|
|
|
|
|
- fontSize: 14.sp,
|
|
|
|
|
- color: Get.reactiveTheme.hintColor,
|
|
|
|
|
- ),
|
|
|
|
|
- ),
|
|
|
|
|
- ],
|
|
|
|
|
- ),
|
|
|
|
|
- )
|
|
|
|
|
- : Text(
|
|
|
|
|
- description,
|
|
|
|
|
- style: TextStyle(
|
|
|
|
|
- fontSize: 14.sp,
|
|
|
|
|
- color: Get.reactiveTheme.hintColor,
|
|
|
|
|
- height: 1.4,
|
|
|
|
|
- ),
|
|
|
|
|
- ),
|
|
|
|
|
- ],
|
|
|
|
|
- ),
|
|
|
|
|
|
|
+ InfoItem(
|
|
|
|
|
+ icon: IconFont.icon49,
|
|
|
|
|
+ title: 'Waiting for Authorization',
|
|
|
|
|
+ description:
|
|
|
|
|
+ 'Please keep this page open.\nOnce approved, your account will automatically upgrade and reconnect.',
|
|
|
|
|
+ iconColor: DarkThemeColors.shadowColor,
|
|
|
),
|
|
),
|
|
|
],
|
|
],
|
|
|
);
|
|
);
|
|
@@ -284,430 +203,136 @@ class DeviceauthView extends BaseView<DeviceauthController> {
|
|
|
|
|
|
|
|
/// 顶部6个输入框
|
|
/// 顶部6个输入框
|
|
|
Widget _buildTopCodeInput() {
|
|
Widget _buildTopCodeInput() {
|
|
|
- return Container(
|
|
|
|
|
- padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 16.h),
|
|
|
|
|
- decoration: BoxDecoration(
|
|
|
|
|
- color: Get.reactiveTheme.highlightColor,
|
|
|
|
|
- borderRadius: BorderRadius.circular(16.r),
|
|
|
|
|
- ),
|
|
|
|
|
- child: Obx(
|
|
|
|
|
- () => Pinput(
|
|
|
|
|
- length: 6,
|
|
|
|
|
- defaultPinTheme: PinTheme(
|
|
|
|
|
- width: 40.w,
|
|
|
|
|
- height: 50.h,
|
|
|
|
|
- textStyle: TextStyle(
|
|
|
|
|
- fontSize: 24.sp,
|
|
|
|
|
- fontWeight: FontWeight.bold,
|
|
|
|
|
- color: Colors.white,
|
|
|
|
|
- ),
|
|
|
|
|
- decoration: BoxDecoration(
|
|
|
|
|
- color: Get.reactiveTheme.hintColor.withOpacity(0.1),
|
|
|
|
|
- borderRadius: BorderRadius.circular(8.r),
|
|
|
|
|
- border: Border.all(color: Colors.transparent, width: 2),
|
|
|
|
|
- ),
|
|
|
|
|
- ),
|
|
|
|
|
- focusedPinTheme: PinTheme(
|
|
|
|
|
- width: 40.w,
|
|
|
|
|
- height: 50.h,
|
|
|
|
|
- textStyle: TextStyle(
|
|
|
|
|
- fontSize: 24.sp,
|
|
|
|
|
- fontWeight: FontWeight.bold,
|
|
|
|
|
- color: Colors.white,
|
|
|
|
|
- ),
|
|
|
|
|
- decoration: BoxDecoration(
|
|
|
|
|
- color: const Color(0xFF00A8E8).withOpacity(0.2),
|
|
|
|
|
- borderRadius: BorderRadius.circular(8.r),
|
|
|
|
|
- border: Border.all(color: const Color(0xFF00A8E8), width: 2),
|
|
|
|
|
- ),
|
|
|
|
|
|
|
+ return Obx(
|
|
|
|
|
+ () => Pinput(
|
|
|
|
|
+ length: 6,
|
|
|
|
|
+ defaultPinTheme: PinTheme(
|
|
|
|
|
+ width: 50.w,
|
|
|
|
|
+ height: 50.w,
|
|
|
|
|
+ textStyle: TextStyle(
|
|
|
|
|
+ fontSize: 27.sp,
|
|
|
|
|
+ fontWeight: FontWeight.w600,
|
|
|
|
|
+ color: Get.reactiveTheme.primaryColor,
|
|
|
),
|
|
),
|
|
|
- submittedPinTheme: PinTheme(
|
|
|
|
|
- width: 40.w,
|
|
|
|
|
- height: 50.h,
|
|
|
|
|
- textStyle: TextStyle(
|
|
|
|
|
- fontSize: 24.sp,
|
|
|
|
|
- fontWeight: FontWeight.bold,
|
|
|
|
|
- color: Colors.white,
|
|
|
|
|
- ),
|
|
|
|
|
- decoration: BoxDecoration(
|
|
|
|
|
- color: const Color(0xFF00A8E8).withOpacity(0.2),
|
|
|
|
|
- borderRadius: BorderRadius.circular(8.r),
|
|
|
|
|
- border: Border.all(color: Colors.transparent, width: 2),
|
|
|
|
|
|
|
+ decoration: BoxDecoration(
|
|
|
|
|
+ color: Get.reactiveTheme.highlightColor,
|
|
|
|
|
+ borderRadius: BorderRadius.circular(5.r),
|
|
|
|
|
+ border: Border.all(
|
|
|
|
|
+ color: Get.reactiveTheme.dividerColor,
|
|
|
|
|
+ width: 1.5.w,
|
|
|
),
|
|
),
|
|
|
),
|
|
),
|
|
|
- onChanged: (value) {
|
|
|
|
|
- controller.setInputCode(value);
|
|
|
|
|
- },
|
|
|
|
|
- onCompleted: (pin) {
|
|
|
|
|
- controller.submitAuthCode();
|
|
|
|
|
- },
|
|
|
|
|
- inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
|
|
|
|
- keyboardType: TextInputType.number,
|
|
|
|
|
- showCursor: true,
|
|
|
|
|
- cursor: Container(
|
|
|
|
|
- width: 2,
|
|
|
|
|
- height: 20.h,
|
|
|
|
|
- color: const Color(0xFF00A8E8),
|
|
|
|
|
- ),
|
|
|
|
|
),
|
|
),
|
|
|
- ),
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// 设备管理区域
|
|
|
|
|
- Widget _buildDeviceManagementSection() {
|
|
|
|
|
- return Container(
|
|
|
|
|
- padding: EdgeInsets.all(20.w),
|
|
|
|
|
- decoration: BoxDecoration(
|
|
|
|
|
- color: Get.reactiveTheme.highlightColor,
|
|
|
|
|
- borderRadius: BorderRadius.circular(16.r),
|
|
|
|
|
- ),
|
|
|
|
|
- child: Column(
|
|
|
|
|
- crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
- children: [
|
|
|
|
|
- // 标题
|
|
|
|
|
- Row(
|
|
|
|
|
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
|
- children: [
|
|
|
|
|
- Text(
|
|
|
|
|
- 'Manage Devices',
|
|
|
|
|
- style: TextStyle(
|
|
|
|
|
- fontSize: 18.sp,
|
|
|
|
|
- fontWeight: FontWeight.bold,
|
|
|
|
|
- color: Colors.white,
|
|
|
|
|
- ),
|
|
|
|
|
- ),
|
|
|
|
|
- Obx(
|
|
|
|
|
- () => Text(
|
|
|
|
|
- controller.deviceCountText,
|
|
|
|
|
- style: TextStyle(
|
|
|
|
|
- fontSize: 16.sp,
|
|
|
|
|
- color: Get.reactiveTheme.hintColor,
|
|
|
|
|
- ),
|
|
|
|
|
- ),
|
|
|
|
|
- ),
|
|
|
|
|
- ],
|
|
|
|
|
- ),
|
|
|
|
|
-
|
|
|
|
|
- 20.verticalSpaceFromWidth,
|
|
|
|
|
-
|
|
|
|
|
- // 设备列表
|
|
|
|
|
- Obx(
|
|
|
|
|
- () => Column(
|
|
|
|
|
- children: [
|
|
|
|
|
- ...controller.currentDevices.map((device) {
|
|
|
|
|
- return _buildDeviceCard(device);
|
|
|
|
|
- }).toList(),
|
|
|
|
|
- // 等待激活的设备
|
|
|
|
|
- if (controller.inputCode.value.isEmpty)
|
|
|
|
|
- _buildAwaitingDeviceCard(),
|
|
|
|
|
- ],
|
|
|
|
|
- ),
|
|
|
|
|
|
|
+ focusedPinTheme: PinTheme(
|
|
|
|
|
+ width: 50.w,
|
|
|
|
|
+ height: 50.w,
|
|
|
|
|
+ textStyle: TextStyle(
|
|
|
|
|
+ fontSize: 27.sp,
|
|
|
|
|
+ fontWeight: FontWeight.w600,
|
|
|
|
|
+ color: Get.reactiveTheme.primaryColor,
|
|
|
),
|
|
),
|
|
|
-
|
|
|
|
|
- 20.verticalSpaceFromWidth,
|
|
|
|
|
-
|
|
|
|
|
- // 授权限制说明
|
|
|
|
|
- Text(
|
|
|
|
|
- 'Authorize up to 4 devices as Premium (${controller.deviceCountText})',
|
|
|
|
|
- style: TextStyle(
|
|
|
|
|
- fontSize: 14.sp,
|
|
|
|
|
- color: Get.reactiveTheme.hintColor,
|
|
|
|
|
- ),
|
|
|
|
|
- ),
|
|
|
|
|
- ],
|
|
|
|
|
- ),
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// 等待激活的设备卡片
|
|
|
|
|
- Widget _buildAwaitingDeviceCard() {
|
|
|
|
|
- return Container(
|
|
|
|
|
- margin: EdgeInsets.only(bottom: 12.h),
|
|
|
|
|
- padding: EdgeInsets.all(16.w),
|
|
|
|
|
- decoration: BoxDecoration(
|
|
|
|
|
- color: Get.reactiveTheme.hintColor.withOpacity(0.1),
|
|
|
|
|
- borderRadius: BorderRadius.circular(12.r),
|
|
|
|
|
- ),
|
|
|
|
|
- child: Row(
|
|
|
|
|
- children: [
|
|
|
|
|
- // 设备图标
|
|
|
|
|
- Container(
|
|
|
|
|
- width: 40.w,
|
|
|
|
|
- height: 40.w,
|
|
|
|
|
- decoration: BoxDecoration(
|
|
|
|
|
- color: Get.reactiveTheme.hintColor.withOpacity(0.2),
|
|
|
|
|
- borderRadius: BorderRadius.circular(8.r),
|
|
|
|
|
- ),
|
|
|
|
|
- child: Icon(
|
|
|
|
|
- Icons.phone_iphone,
|
|
|
|
|
- size: 20.w,
|
|
|
|
|
- color: Get.reactiveTheme.hintColor,
|
|
|
|
|
- ),
|
|
|
|
|
- ),
|
|
|
|
|
-
|
|
|
|
|
- SizedBox(width: 12.w),
|
|
|
|
|
-
|
|
|
|
|
- // 设备信息
|
|
|
|
|
- Expanded(
|
|
|
|
|
- child: Text(
|
|
|
|
|
- 'Awaiting Activation',
|
|
|
|
|
- style: TextStyle(
|
|
|
|
|
- fontSize: 16.sp,
|
|
|
|
|
- fontWeight: FontWeight.w500,
|
|
|
|
|
- color: Colors.white,
|
|
|
|
|
- ),
|
|
|
|
|
|
|
+ decoration: BoxDecoration(
|
|
|
|
|
+ color: Get.reactiveTheme.cardColor,
|
|
|
|
|
+ borderRadius: BorderRadius.circular(5.r),
|
|
|
|
|
+ border: Border.all(
|
|
|
|
|
+ color: Get.reactiveTheme.primaryColor,
|
|
|
|
|
+ width: 1.5.w,
|
|
|
),
|
|
),
|
|
|
),
|
|
),
|
|
|
- ],
|
|
|
|
|
- ),
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// 设备卡片
|
|
|
|
|
- Widget _buildDeviceCard(DeviceInfo device) {
|
|
|
|
|
- return Container(
|
|
|
|
|
- margin: EdgeInsets.only(bottom: 12.h),
|
|
|
|
|
- padding: EdgeInsets.all(16.w),
|
|
|
|
|
- decoration: BoxDecoration(
|
|
|
|
|
- color: Get.reactiveTheme.hintColor.withOpacity(0.1),
|
|
|
|
|
- borderRadius: BorderRadius.circular(12.r),
|
|
|
|
|
- ),
|
|
|
|
|
- child: Row(
|
|
|
|
|
- children: [
|
|
|
|
|
- // 设备图标
|
|
|
|
|
- Container(
|
|
|
|
|
- width: 40.w,
|
|
|
|
|
- height: 40.w,
|
|
|
|
|
- decoration: BoxDecoration(
|
|
|
|
|
- color: Get.reactiveTheme.hintColor.withOpacity(0.2),
|
|
|
|
|
- borderRadius: BorderRadius.circular(8.r),
|
|
|
|
|
- ),
|
|
|
|
|
- child: Icon(
|
|
|
|
|
- _getDeviceIcon(device.type),
|
|
|
|
|
- size: 20.w,
|
|
|
|
|
- color: Get.reactiveTheme.hintColor,
|
|
|
|
|
- ),
|
|
|
|
|
|
|
+ ),
|
|
|
|
|
+ submittedPinTheme: PinTheme(
|
|
|
|
|
+ width: 50.w,
|
|
|
|
|
+ height: 50.w,
|
|
|
|
|
+ textStyle: TextStyle(
|
|
|
|
|
+ fontSize: 27.sp,
|
|
|
|
|
+ fontWeight: FontWeight.w600,
|
|
|
|
|
+ color: Get.reactiveTheme.primaryColor,
|
|
|
),
|
|
),
|
|
|
-
|
|
|
|
|
- SizedBox(width: 12.w),
|
|
|
|
|
-
|
|
|
|
|
- // 设备信息
|
|
|
|
|
- Expanded(
|
|
|
|
|
- child: Column(
|
|
|
|
|
- crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
- children: [
|
|
|
|
|
- Text(
|
|
|
|
|
- device.name,
|
|
|
|
|
- style: TextStyle(
|
|
|
|
|
- fontSize: 16.sp,
|
|
|
|
|
- fontWeight: FontWeight.w500,
|
|
|
|
|
- color: Colors.white,
|
|
|
|
|
- ),
|
|
|
|
|
- ),
|
|
|
|
|
- if (device.uid != null) ...[
|
|
|
|
|
- SizedBox(height: 4.h),
|
|
|
|
|
- Text(
|
|
|
|
|
- device.uid!,
|
|
|
|
|
- style: TextStyle(
|
|
|
|
|
- fontSize: 14.sp,
|
|
|
|
|
- color: Get.reactiveTheme.hintColor,
|
|
|
|
|
- ),
|
|
|
|
|
- ),
|
|
|
|
|
- ],
|
|
|
|
|
- if (device.date != null) ...[
|
|
|
|
|
- SizedBox(height: 4.h),
|
|
|
|
|
- Text(
|
|
|
|
|
- device.date!,
|
|
|
|
|
- style: TextStyle(
|
|
|
|
|
- fontSize: 12.sp,
|
|
|
|
|
- color: Get.reactiveTheme.hintColor,
|
|
|
|
|
- ),
|
|
|
|
|
- ),
|
|
|
|
|
- ],
|
|
|
|
|
- ],
|
|
|
|
|
|
|
+ decoration: BoxDecoration(
|
|
|
|
|
+ color: Get.reactiveTheme.cardColor,
|
|
|
|
|
+ borderRadius: BorderRadius.circular(5.r),
|
|
|
|
|
+ border: Border.all(
|
|
|
|
|
+ color: Get.reactiveTheme.dividerColor,
|
|
|
|
|
+ width: 1.5.w,
|
|
|
),
|
|
),
|
|
|
),
|
|
),
|
|
|
-
|
|
|
|
|
- // 操作按钮
|
|
|
|
|
- if (device.isCurrent)
|
|
|
|
|
- ClickOpacity(
|
|
|
|
|
- onTap: () => controller.removeDevice(device.id),
|
|
|
|
|
- child: Container(
|
|
|
|
|
- padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
|
|
|
|
|
- decoration: BoxDecoration(
|
|
|
|
|
- color: Get.reactiveTheme.hintColor.withOpacity(0.2),
|
|
|
|
|
- borderRadius: BorderRadius.circular(6.r),
|
|
|
|
|
- ),
|
|
|
|
|
- child: Row(
|
|
|
|
|
- mainAxisSize: MainAxisSize.min,
|
|
|
|
|
- children: [
|
|
|
|
|
- Icon(
|
|
|
|
|
- Icons.delete,
|
|
|
|
|
- size: 16.w,
|
|
|
|
|
- color: Get.reactiveTheme.hintColor,
|
|
|
|
|
- ),
|
|
|
|
|
- SizedBox(width: 4.w),
|
|
|
|
|
- Text(
|
|
|
|
|
- 'Relieve',
|
|
|
|
|
- style: TextStyle(
|
|
|
|
|
- fontSize: 12.sp,
|
|
|
|
|
- color: Get.reactiveTheme.hintColor,
|
|
|
|
|
- ),
|
|
|
|
|
- ),
|
|
|
|
|
- ],
|
|
|
|
|
- ),
|
|
|
|
|
- ),
|
|
|
|
|
- )
|
|
|
|
|
- else
|
|
|
|
|
- ClickOpacity(
|
|
|
|
|
- onTap: () => controller.removeDevice(device.id),
|
|
|
|
|
- child: Container(
|
|
|
|
|
- padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
|
|
|
|
|
- decoration: BoxDecoration(
|
|
|
|
|
- color: const Color(0xFF00A8E8),
|
|
|
|
|
- borderRadius: BorderRadius.circular(6.r),
|
|
|
|
|
- ),
|
|
|
|
|
- child: Text(
|
|
|
|
|
- 'Relieve',
|
|
|
|
|
- style: TextStyle(
|
|
|
|
|
- fontSize: 12.sp,
|
|
|
|
|
- color: Colors.white,
|
|
|
|
|
- fontWeight: FontWeight.w500,
|
|
|
|
|
- ),
|
|
|
|
|
- ),
|
|
|
|
|
- ),
|
|
|
|
|
- ),
|
|
|
|
|
- ],
|
|
|
|
|
|
|
+ ),
|
|
|
|
|
+ onChanged: (value) {
|
|
|
|
|
+ controller.setInputCode(value);
|
|
|
|
|
+ },
|
|
|
|
|
+ onCompleted: (pin) {
|
|
|
|
|
+ controller.submitAuthCode();
|
|
|
|
|
+ },
|
|
|
|
|
+ inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
|
|
|
|
+ keyboardType: TextInputType.number,
|
|
|
|
|
+ showCursor: true,
|
|
|
|
|
+ cursor: Container(
|
|
|
|
|
+ width: 1.w,
|
|
|
|
|
+ height: 30.w,
|
|
|
|
|
+ color: Get.reactiveTheme.textTheme.bodyLarge!.color,
|
|
|
|
|
+ ),
|
|
|
),
|
|
),
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /// 获取设备图标
|
|
|
|
|
- IconData _getDeviceIcon(DeviceTypeEnum type) {
|
|
|
|
|
- switch (type) {
|
|
|
|
|
- case DeviceTypeEnum.ios:
|
|
|
|
|
- return Icons.phone_iphone;
|
|
|
|
|
- case DeviceTypeEnum.android:
|
|
|
|
|
- return Icons.android;
|
|
|
|
|
- case DeviceTypeEnum.windows:
|
|
|
|
|
- return Icons.laptop_windows;
|
|
|
|
|
- case DeviceTypeEnum.mac:
|
|
|
|
|
- return Icons.laptop_mac;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ /// 设备管理区域
|
|
|
|
|
+ Widget _buildDeviceManagementSection() {
|
|
|
|
|
+ return Obx(() {
|
|
|
|
|
+ final authStatus = controller.authorizationStatus.value;
|
|
|
|
|
+ final isConfiguring = authStatus == AuthorizationStatus.configuring;
|
|
|
|
|
+
|
|
|
|
|
+ return Container(
|
|
|
|
|
+ margin: EdgeInsets.only(top: 20.w),
|
|
|
|
|
+ decoration: BoxDecoration(
|
|
|
|
|
+ color: Get.reactiveTheme.highlightColor,
|
|
|
|
|
+ borderRadius: BorderRadius.circular(12.r),
|
|
|
|
|
+ ),
|
|
|
|
|
+ child: Column(
|
|
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
+ children: [
|
|
|
|
|
+ // 设备列表
|
|
|
|
|
+ ...controller.currentDevices.map((device) {
|
|
|
|
|
+ return DeviceCard(
|
|
|
|
|
+ device: device,
|
|
|
|
|
+ onRelieve: () => controller.removeDevice(device.id),
|
|
|
|
|
+ showRelieveButton: true,
|
|
|
|
|
+ );
|
|
|
|
|
+ }),
|
|
|
|
|
+
|
|
|
|
|
+ // 加载中状态或等待激活的设备
|
|
|
|
|
+ if (isConfiguring || controller.inputCode.value.isEmpty)
|
|
|
|
|
+ const AwaitingActivationCard(),
|
|
|
|
|
+ ],
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// 授权步骤说明
|
|
/// 授权步骤说明
|
|
|
Widget _buildAuthorizationSteps() {
|
|
Widget _buildAuthorizationSteps() {
|
|
|
- return Container(
|
|
|
|
|
- padding: EdgeInsets.all(20.w),
|
|
|
|
|
- decoration: BoxDecoration(
|
|
|
|
|
- color: Get.reactiveTheme.highlightColor,
|
|
|
|
|
- borderRadius: BorderRadius.circular(16.r),
|
|
|
|
|
- ),
|
|
|
|
|
- child: Column(
|
|
|
|
|
- crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
- children: [
|
|
|
|
|
- Text(
|
|
|
|
|
- 'Authorization Steps',
|
|
|
|
|
- style: TextStyle(
|
|
|
|
|
- fontSize: 18.sp,
|
|
|
|
|
- fontWeight: FontWeight.bold,
|
|
|
|
|
- color: Colors.white,
|
|
|
|
|
- ),
|
|
|
|
|
- ),
|
|
|
|
|
-
|
|
|
|
|
- 20.verticalSpaceFromWidth,
|
|
|
|
|
-
|
|
|
|
|
- _buildStepItem(
|
|
|
|
|
- stepNumber: 1,
|
|
|
|
|
- title: 'Enter Code',
|
|
|
|
|
- description:
|
|
|
|
|
- 'Input the 6-digit code shown on the other device (free user). This code refreshes every 15 minutes.',
|
|
|
|
|
- icon: Icons.input,
|
|
|
|
|
- isCompleted: controller.inputCode.value.isNotEmpty,
|
|
|
|
|
- ),
|
|
|
|
|
-
|
|
|
|
|
- 16.verticalSpaceFromWidth,
|
|
|
|
|
-
|
|
|
|
|
- _buildStepItem(
|
|
|
|
|
- stepNumber: 2,
|
|
|
|
|
- title: 'Verify Device',
|
|
|
|
|
- description:
|
|
|
|
|
- 'We\'ll check if the entered code matches an active device waiting for authorization.',
|
|
|
|
|
- icon: Icons.verified_user,
|
|
|
|
|
- isCompleted: controller.isCodeComplete.value,
|
|
|
|
|
- ),
|
|
|
|
|
-
|
|
|
|
|
- 16.verticalSpaceFromWidth,
|
|
|
|
|
-
|
|
|
|
|
- _buildStepItem(
|
|
|
|
|
- stepNumber: 3,
|
|
|
|
|
- title: 'Authorization Successful',
|
|
|
|
|
- description:
|
|
|
|
|
- 'Once confirmed, the device will automatically upgrade and link to your account.',
|
|
|
|
|
- icon: Icons.check_circle,
|
|
|
|
|
- isCompleted:
|
|
|
|
|
- controller.authorizationStatus.value ==
|
|
|
|
|
- AuthorizationStatus.success,
|
|
|
|
|
- ),
|
|
|
|
|
- ],
|
|
|
|
|
- ),
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// 步骤条目
|
|
|
|
|
- Widget _buildStepItem({
|
|
|
|
|
- required int stepNumber,
|
|
|
|
|
- required String title,
|
|
|
|
|
- required String description,
|
|
|
|
|
- required IconData icon,
|
|
|
|
|
- required bool isCompleted,
|
|
|
|
|
- }) {
|
|
|
|
|
- return Row(
|
|
|
|
|
- crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
- children: [
|
|
|
|
|
- Container(
|
|
|
|
|
- width: 32.w,
|
|
|
|
|
- height: 32.w,
|
|
|
|
|
- decoration: BoxDecoration(
|
|
|
|
|
- color: isCompleted
|
|
|
|
|
- ? const Color(0xFF00D9A3)
|
|
|
|
|
- : Get.reactiveTheme.hintColor.withOpacity(0.2),
|
|
|
|
|
- borderRadius: BorderRadius.circular(8.r),
|
|
|
|
|
- ),
|
|
|
|
|
- child: Center(
|
|
|
|
|
- child: isCompleted
|
|
|
|
|
- ? Icon(Icons.check, size: 16.w, color: Colors.white)
|
|
|
|
|
- : Icon(icon, size: 16.w, color: Get.reactiveTheme.hintColor),
|
|
|
|
|
- ),
|
|
|
|
|
|
|
+ return InfoCard(
|
|
|
|
|
+ title: '',
|
|
|
|
|
+ items: [
|
|
|
|
|
+ InfoItem(
|
|
|
|
|
+ icon: IconFont.icon46,
|
|
|
|
|
+ title: 'Enter Code',
|
|
|
|
|
+ description:
|
|
|
|
|
+ 'Input the 6-digit code shown on the other device (free user).This code refreshes every 15 minutes.',
|
|
|
|
|
+ iconColor: DarkThemeColors.shadowColor,
|
|
|
),
|
|
),
|
|
|
-
|
|
|
|
|
- SizedBox(width: 12.w),
|
|
|
|
|
-
|
|
|
|
|
- Expanded(
|
|
|
|
|
- child: Column(
|
|
|
|
|
- crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
- children: [
|
|
|
|
|
- Text(
|
|
|
|
|
- title,
|
|
|
|
|
- style: TextStyle(
|
|
|
|
|
- fontSize: 16.sp,
|
|
|
|
|
- fontWeight: FontWeight.w500,
|
|
|
|
|
- color: Colors.white,
|
|
|
|
|
- ),
|
|
|
|
|
- ),
|
|
|
|
|
- SizedBox(height: 4.h),
|
|
|
|
|
- Text(
|
|
|
|
|
- description,
|
|
|
|
|
- style: TextStyle(
|
|
|
|
|
- fontSize: 14.sp,
|
|
|
|
|
- color: Get.reactiveTheme.hintColor,
|
|
|
|
|
- height: 1.4,
|
|
|
|
|
- ),
|
|
|
|
|
- ),
|
|
|
|
|
- ],
|
|
|
|
|
- ),
|
|
|
|
|
|
|
+ InfoItem(
|
|
|
|
|
+ icon: IconFont.icon16,
|
|
|
|
|
+ title: 'Verify Device',
|
|
|
|
|
+ description:
|
|
|
|
|
+ 'We’ll check if the entered code matches an active device waiting for authorization.',
|
|
|
|
|
+ iconColor: DarkThemeColors.shadowColor,
|
|
|
|
|
+ ),
|
|
|
|
|
+ InfoItem(
|
|
|
|
|
+ icon: IconFont.icon43,
|
|
|
|
|
+ title: 'Authorization Successful',
|
|
|
|
|
+ description:
|
|
|
|
|
+ 'Once confirmed, the device will automatically upgrade and link to your account.',
|
|
|
|
|
+ iconColor: DarkThemeColors.shadowColor,
|
|
|
),
|
|
),
|
|
|
],
|
|
],
|
|
|
);
|
|
);
|