precode_sendemail_view.dart 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_screenutil/flutter_screenutil.dart';
  3. import 'package:get/get.dart';
  4. import 'package:nomo/app/base/base_view.dart';
  5. import 'package:nomo/app/widgets/click_opacity.dart';
  6. import 'package:nomo/config/theme/theme_extensions/theme_extension.dart';
  7. import '../controllers/precode_sendemail_controller.dart';
  8. class PrecodeSendemailView extends BaseView<PrecodeSendemailController> {
  9. const PrecodeSendemailView({super.key});
  10. @override
  11. Widget buildContent(BuildContext context) {
  12. return Padding(
  13. padding: EdgeInsets.symmetric(horizontal: 14.w),
  14. child: Column(
  15. children: [
  16. // 自定义标题栏
  17. _buildAppBar(),
  18. 40.verticalSpaceFromWidth,
  19. // 邮箱输入框
  20. _buildEmailInput(),
  21. 20.verticalSpaceFromWidth,
  22. // 发送按钮
  23. _buildSendButton(),
  24. 12.verticalSpaceFromWidth,
  25. // 说明文字
  26. Text(
  27. 'Your code will be backed up to this email.',
  28. textAlign: TextAlign.center,
  29. style: TextStyle(
  30. fontSize: 14.sp,
  31. color: Get.reactiveTheme.hintColor.withOpacity(0.6),
  32. ),
  33. ),
  34. 40.verticalSpaceFromWidth,
  35. // 信息卡片列表
  36. _buildInfoCardList(),
  37. ],
  38. ),
  39. );
  40. }
  41. /// 自定义标题栏
  42. Widget _buildAppBar() {
  43. return Container(
  44. height: 64.h,
  45. alignment: Alignment.center,
  46. child: Stack(
  47. children: [
  48. // 返回按钮
  49. Positioned(
  50. left: 0,
  51. top: 0,
  52. bottom: 0,
  53. child: ClickOpacity(
  54. onTap: () => Get.back(),
  55. child: Container(
  56. width: 48.w,
  57. height: 48.w,
  58. alignment: Alignment.center,
  59. child: Icon(
  60. Icons.arrow_back,
  61. color: Get.reactiveTheme.textTheme.bodyLarge!.color,
  62. size: 24.w,
  63. ),
  64. ),
  65. ),
  66. ),
  67. // 标题
  68. Center(
  69. child: Text(
  70. 'Send Pre Code to Email',
  71. style: TextStyle(
  72. fontSize: 20.sp,
  73. color: Get.reactiveTheme.textTheme.bodyLarge!.color,
  74. fontWeight: FontWeight.w600,
  75. ),
  76. ),
  77. ),
  78. ],
  79. ),
  80. );
  81. }
  82. /// 邮箱输入框
  83. Widget _buildEmailInput() {
  84. return Obx(
  85. () => Container(
  86. height: 56.h,
  87. decoration: BoxDecoration(
  88. color: Get.reactiveTheme.highlightColor,
  89. borderRadius: BorderRadius.circular(16.r),
  90. border: Border.all(
  91. color: controller.isFocused.value
  92. ? const Color(0xFF0A84FF)
  93. : Colors.transparent,
  94. width: 2,
  95. ),
  96. ),
  97. child: TextField(
  98. controller: controller.emailController,
  99. focusNode: controller.emailFocusNode,
  100. keyboardType: TextInputType.emailAddress,
  101. style: TextStyle(
  102. fontSize: 16.sp,
  103. color: Get.reactiveTheme.textTheme.bodyLarge!.color,
  104. ),
  105. decoration: InputDecoration(
  106. hintText: 'Enter your email',
  107. hintStyle: TextStyle(
  108. fontSize: 16.sp,
  109. color: Get.reactiveTheme.hintColor.withOpacity(0.5),
  110. ),
  111. border: InputBorder.none,
  112. contentPadding: EdgeInsets.symmetric(horizontal: 20.w),
  113. ),
  114. ),
  115. ),
  116. );
  117. }
  118. /// 发送按钮
  119. Widget _buildSendButton() {
  120. return Obx(() {
  121. final canSend = controller.canSend;
  122. final isSending = controller.isSending.value;
  123. return ClickOpacity(
  124. onTap: canSend ? controller.sendEmail : null,
  125. child: Container(
  126. height: 56.h,
  127. decoration: BoxDecoration(
  128. color: canSend
  129. ? const Color(0xFF0A84FF)
  130. : Get.reactiveTheme.highlightColor,
  131. borderRadius: BorderRadius.circular(16.r),
  132. ),
  133. child: Row(
  134. mainAxisAlignment: MainAxisAlignment.center,
  135. children: [
  136. if (isSending)
  137. SizedBox(
  138. width: 20.w,
  139. height: 20.w,
  140. child: const CircularProgressIndicator(
  141. strokeWidth: 2,
  142. valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
  143. ),
  144. )
  145. else
  146. Icon(
  147. Icons.email_outlined,
  148. color: canSend
  149. ? Colors.white
  150. : Get.reactiveTheme.hintColor.withOpacity(0.5),
  151. size: 24.w,
  152. ),
  153. 12.horizontalSpace,
  154. Text(
  155. isSending ? 'Sending...' : 'Send your Email',
  156. style: TextStyle(
  157. fontSize: 16.sp,
  158. color: canSend
  159. ? Colors.white
  160. : Get.reactiveTheme.hintColor.withOpacity(0.5),
  161. fontWeight: FontWeight.w500,
  162. ),
  163. ),
  164. ],
  165. ),
  166. ),
  167. );
  168. });
  169. }
  170. /// 信息卡片列表(带连接线)
  171. Widget _buildInfoCardList() {
  172. final cards = [
  173. {
  174. 'icon': Icons.workspace_premium_outlined,
  175. 'title': 'Your Pre Credential',
  176. 'description':
  177. 'This is your VIP credential. Please store it securely and do not share it with anyone.',
  178. },
  179. {
  180. 'icon': Icons.mark_email_read_outlined,
  181. 'title': 'Secure Email Backup',
  182. 'description':
  183. 'We will send an email containing this credential to your specified email address for safekeeping.',
  184. },
  185. {
  186. 'icon': Icons.check_circle_outline,
  187. 'title': 'Send and Save',
  188. 'description':
  189. 'After the email is sent, we recommend you also save this credential to a secure location on your device.',
  190. },
  191. ];
  192. return Column(
  193. children: List.generate(cards.length, (index) {
  194. final card = cards[index];
  195. final isLast = index == cards.length - 1;
  196. return Row(
  197. crossAxisAlignment: CrossAxisAlignment.start,
  198. children: [
  199. // 左侧图标和连接线
  200. Column(
  201. children: [
  202. // 图标
  203. Container(
  204. width: 48.w,
  205. height: 48.w,
  206. decoration: BoxDecoration(
  207. color: const Color(0xFF0A84FF).withOpacity(0.15),
  208. borderRadius: BorderRadius.circular(12.r),
  209. ),
  210. child: Icon(
  211. card['icon'] as IconData,
  212. color: const Color(0xFF0A84FF),
  213. size: 28.w,
  214. ),
  215. ),
  216. // 连接线(最后一个不显示)
  217. if (!isLast)
  218. Container(
  219. width: 2.w,
  220. height: 60.h,
  221. margin: EdgeInsets.symmetric(vertical: 8.h),
  222. decoration: BoxDecoration(
  223. color: Get.reactiveTheme.hintColor.withOpacity(0.2),
  224. borderRadius: BorderRadius.circular(1.r),
  225. ),
  226. ),
  227. ],
  228. ),
  229. 16.horizontalSpace,
  230. // 右侧文字内容
  231. Expanded(
  232. child: Padding(
  233. padding: EdgeInsets.only(top: 4.h, bottom: isLast ? 0 : 24.h),
  234. child: Column(
  235. crossAxisAlignment: CrossAxisAlignment.start,
  236. children: [
  237. Text(
  238. card['title'] as String,
  239. style: TextStyle(
  240. fontSize: 18.sp,
  241. color: Get.reactiveTheme.textTheme.bodyLarge!.color,
  242. fontWeight: FontWeight.w600,
  243. ),
  244. ),
  245. 8.verticalSpaceFromWidth,
  246. Text(
  247. card['description'] as String,
  248. style: TextStyle(
  249. fontSize: 14.sp,
  250. color: Get.reactiveTheme.hintColor.withOpacity(0.6),
  251. height: 1.5,
  252. ),
  253. ),
  254. ],
  255. ),
  256. ),
  257. ),
  258. ],
  259. );
  260. }),
  261. );
  262. }
  263. }