mock_page.dart 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. import 'package:flutter/material.dart';
  2. import 'package:intl/intl.dart';
  3. import 'package:japp_flutter/core/constants/options.dart';
  4. import 'package:japp_flutter/features/widgets/app_dropdown.dart';
  5. class MockPage extends StatelessWidget {
  6. const MockPage({super.key});
  7. @override
  8. Widget build(BuildContext context) {
  9. return MaterialApp(
  10. title: 'Flutter 表单示例',
  11. theme: ThemeData(
  12. primarySwatch: Colors.blue,
  13. ),
  14. home: const MyFormPage(),
  15. );
  16. }
  17. }
  18. // 表单数据模型
  19. class FormData {
  20. String name;
  21. String email;
  22. String phone;
  23. String password;
  24. bool agreeTerms;
  25. DateTime? startDate;
  26. DateTime? endDate;
  27. int? difficult;
  28. FormData({
  29. this.name = '',
  30. this.email = 'fin@qq.com',
  31. this.phone = '13770656091',
  32. this.password = '',
  33. this.agreeTerms = false,
  34. this.startDate,
  35. this.endDate,
  36. this.difficult = 2,
  37. });
  38. // 转换为Map方便打印
  39. Map<String, dynamic> toMap() {
  40. return {
  41. 'name': name,
  42. 'email': email,
  43. 'phone': phone,
  44. 'password': password.replaceAll(RegExp(r'.'), '*'), // 密码用*号代替
  45. 'agreeTerms': agreeTerms,
  46. 'startDate': startDate != null ? DateFormat('yyyy-MM-dd').format(startDate!) : null,
  47. 'endDate': endDate != null ? DateFormat('yyyy-MM-dd').format(endDate!) : null,
  48. 'difficult':difficult
  49. };
  50. }
  51. }
  52. // 难度等级模型
  53. class DifficultyLevel {
  54. final int value;
  55. final String label;
  56. final IconData? icon;
  57. const DifficultyLevel({
  58. required this.value,
  59. required this.label,
  60. this.icon,
  61. });
  62. }
  63. // 预定义的难度等级
  64. const List<DifficultyLevel> difficultyLevels = [
  65. DifficultyLevel(value: 1, label: '简单', icon: Icons.sentiment_very_satisfied),
  66. DifficultyLevel(value: 2, label: '中等', icon: Icons.sentiment_satisfied),
  67. DifficultyLevel(value: 3, label: '困难', icon: Icons.sentiment_dissatisfied),
  68. DifficultyLevel(value: 4, label: '专家', icon: Icons.sentiment_very_dissatisfied),
  69. ];
  70. class MyFormPage extends StatefulWidget {
  71. const MyFormPage({super.key});
  72. @override
  73. State<MyFormPage> createState() => _MyFormPageState();
  74. }
  75. class _MyFormPageState extends State<MyFormPage> {
  76. final _formKey = GlobalKey<FormState>();
  77. final FormData _formData = FormData(
  78. startDate: DateTime.now(), // 默认开始日期为今天
  79. endDate: DateTime.now().add(const Duration(days: 7)), // 默认结束日期为7天后
  80. );
  81. // 选择开始日期
  82. Future<void> _selectStartDate() async {
  83. final DateTime? picked = await showDatePicker(
  84. context: context,
  85. initialDate: _formData.startDate ?? DateTime.now(),
  86. firstDate: DateTime(2000),
  87. lastDate: DateTime(2100),
  88. );
  89. if (picked != null && picked != _formData.startDate) {
  90. setState(() {
  91. _formData.startDate = picked;
  92. // 如果结束日期早于开始日期,重置结束日期
  93. if (_formData.endDate != null && _formData.endDate!.isBefore(picked)) {
  94. _formData.endDate = null;
  95. }
  96. });
  97. }
  98. }
  99. // 选择结束日期
  100. Future<void> _selectEndDate() async {
  101. final DateTime? picked = await showDatePicker(
  102. context: context,
  103. initialDate: _formData.endDate ?? (_formData.startDate ?? DateTime.now()),
  104. firstDate: _formData.startDate ?? DateTime.now(),
  105. lastDate: DateTime(2100),
  106. );
  107. if (picked != null && picked != _formData.endDate) {
  108. setState(() {
  109. _formData.endDate = picked;
  110. });
  111. }
  112. }
  113. // 提交表单
  114. void _submitForm() {
  115. if (_formKey.currentState!.validate()) {
  116. _formKey.currentState!.save();
  117. // 获取完整的难度信息(不仅仅是value)
  118. final selectedDifficulty = difficultyLevels.firstWhere(
  119. (level) => level.value == _formData.difficult,
  120. orElse: () => const DifficultyLevel(value: 0, label: '未知'),
  121. );
  122. print('表单提交数据: ${_formData.toMap()}');
  123. // 显示提交成功的提示
  124. ScaffoldMessenger.of(context).showSnackBar(
  125. const SnackBar(content: Text('表单提交成功! 数据已打印到控制台')),
  126. );
  127. }
  128. }
  129. @override
  130. Widget build(BuildContext context) {
  131. return Scaffold(
  132. appBar: AppBar(
  133. title: const Text('Flutter 表单示例'),
  134. ),
  135. body: Padding(
  136. padding: const EdgeInsets.all(16.0),
  137. child: Form(
  138. key: _formKey,
  139. child: ListView(
  140. children: [
  141. TextFormField(
  142. decoration: const InputDecoration(
  143. labelText: '姓名',
  144. hintText: '请输入您的姓名',
  145. border: OutlineInputBorder(),
  146. ),
  147. validator: (value) {
  148. if (value == null || value.isEmpty) {
  149. return '请输入姓名';
  150. }
  151. return null;
  152. },
  153. onSaved: (value) => _formData.name = value!,
  154. ),
  155. const SizedBox(height: 16),
  156. TextFormField(
  157. initialValue: _formData.email,
  158. decoration: const InputDecoration(
  159. labelText: '电子邮件',
  160. hintText: '请输入您的电子邮件',
  161. border: OutlineInputBorder(),
  162. ),
  163. keyboardType: TextInputType.emailAddress,
  164. validator: (value) {
  165. if (value == null || value.isEmpty) {
  166. return '请输入电子邮件';
  167. }
  168. if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) {
  169. return '请输入有效的电子邮件地址';
  170. }
  171. return null;
  172. },
  173. onSaved: (value) => _formData.email = value!,
  174. ),
  175. const SizedBox(height: 16),
  176. TextFormField(
  177. initialValue: _formData.phone,
  178. decoration: const InputDecoration(
  179. labelText: '手机号码',
  180. hintText: '请输入您的手机号码',
  181. border: OutlineInputBorder(),
  182. ),
  183. keyboardType: TextInputType.phone,
  184. validator: (value) {
  185. if (value == null || value.isEmpty) {
  186. return '请输入手机号码';
  187. }
  188. if (!RegExp(r'^[0-9]{11}$').hasMatch(value)) {
  189. return '请输入有效的11位手机号码';
  190. }
  191. return null;
  192. },
  193. onSaved: (value) => _formData.phone = value!,
  194. ),
  195. const SizedBox(height: 16),
  196. TextFormField(
  197. decoration: const InputDecoration(
  198. labelText: '密码',
  199. hintText: '请输入您的密码',
  200. border: OutlineInputBorder(),
  201. ),
  202. obscureText: true,
  203. validator: (value) {
  204. if (value == null || value.isEmpty) {
  205. return '请输入密码';
  206. }
  207. if (value.length < 6) {
  208. return '密码长度不能少于6位';
  209. }
  210. return null;
  211. },
  212. onSaved: (value) => _formData.password = value!,
  213. ),
  214. const SizedBox(height: 16),
  215. // 开始日期选择
  216. InkWell(
  217. onTap: _selectStartDate,
  218. child: InputDecorator(
  219. decoration: const InputDecoration(
  220. labelText: '开始日期',
  221. border: OutlineInputBorder(),
  222. ),
  223. child: Row(
  224. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  225. children: [
  226. Text(
  227. _formData.startDate != null
  228. ? DateFormat('yyyy-MM-dd').format(_formData.startDate!)
  229. : '请选择开始日期',
  230. ),
  231. const Icon(Icons.calendar_today, size: 20),
  232. ],
  233. ),
  234. ),
  235. ),
  236. const SizedBox(height: 16),
  237. // 结束日期选择
  238. InkWell(
  239. onTap: _formData.startDate == null
  240. ? null
  241. : _selectEndDate,
  242. child: InputDecorator(
  243. decoration: const InputDecoration(
  244. labelText: '结束日期',
  245. border: OutlineInputBorder(),
  246. ),
  247. child: Row(
  248. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  249. children: [
  250. Text(
  251. _formData.endDate != null
  252. ? DateFormat('yyyy-MM-dd').format(_formData.endDate!)
  253. : '请选择结束日期',
  254. ),
  255. const Icon(Icons.calendar_today, size: 20),
  256. ],
  257. ),
  258. ),
  259. ),
  260. const SizedBox(height: 16),
  261. // 难度选择下拉框
  262. // DropdownButtonFormField<int>(
  263. // decoration: const InputDecoration(
  264. // labelText: '挑战难度',
  265. // border: OutlineInputBorder(),
  266. // ),
  267. // value: _formData.difficult,
  268. // items: difficultyLevels.map((level) {
  269. // return DropdownMenuItem<int>(
  270. // value: level.value,
  271. // child: Row(
  272. // children: [
  273. // if (level.icon != null)
  274. // Icon(level.icon, size: 20),
  275. // const SizedBox(width: 8),
  276. // Text(level.label),
  277. // ],
  278. // ),
  279. // );
  280. // }).toList(),
  281. // onChanged: (value) {
  282. // setState(() {
  283. // _formData.difficult = value;
  284. // });
  285. // },
  286. // validator: (value) {
  287. // if (value == null) {
  288. // return '请选择难度等级';
  289. // }
  290. // return null;
  291. // },
  292. // onSaved: (value) => _formData.difficult = value,
  293. // ),
  294. AppDropdown(
  295. label: '挑战难度',
  296. value: _formData.difficult,
  297. items: challengeDifficultyOpts,
  298. onChanged: (value) {
  299. setState(() {
  300. _formData.difficult = value;
  301. });
  302. },
  303. ),
  304. const SizedBox(height: 16),
  305. Row(
  306. children: [
  307. Checkbox(
  308. value: _formData.agreeTerms,
  309. onChanged: (value) {
  310. setState(() {
  311. _formData.agreeTerms = value!;
  312. });
  313. },
  314. ),
  315. const Text('我同意用户协议'),
  316. ],
  317. ),
  318. const SizedBox(height: 24),
  319. ElevatedButton(
  320. onPressed: _submitForm,
  321. style: ElevatedButton.styleFrom(
  322. padding: const EdgeInsets.symmetric(vertical: 16),
  323. ),
  324. child: const Text('提交表单'),
  325. ),
  326. ],
  327. ),
  328. ),
  329. ),
  330. );
  331. }
  332. }