这是CaiYouHui前端,一个关于flutter的安卓app,前端使用flutter实现

profile_screen.dart 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. import 'package:flutter/material.dart';
  2. import 'package:provider/provider.dart';
  3. import '../../../core/constants/route_constants.dart';
  4. import '../../providers/auth_provider.dart';
  5. import '../../providers/user_provider.dart';
  6. import '../../navigation/bottom_nav_bar.dart';
  7. import 'profile_detail_screen.dart';
  8. import '../../widgets/custom/protected_widget.dart';
  9. class ProfileScreen extends StatelessWidget {
  10. const ProfileScreen({super.key});
  11. @override
  12. Widget build(BuildContext context) {
  13. // 直接显示内容,认证由路由层处理
  14. return _ProfileContent();
  15. }
  16. }
  17. class _ProfileContent extends StatefulWidget {
  18. @override
  19. State<_ProfileContent> createState() => _ProfileContentState();
  20. }
  21. class _ProfileContentState extends State<_ProfileContent> {
  22. @override
  23. void initState() {
  24. super.initState();
  25. WidgetsBinding.instance.addPostFrameCallback((_) {
  26. final userProvider = Provider.of<UserProvider>(context, listen: false);
  27. userProvider.loadUserProfile();
  28. });
  29. }
  30. @override
  31. Widget build(BuildContext context) {
  32. final authProvider = Provider.of<AuthProvider>(context);
  33. // final userProvider = Provider.of<UserProvider>(context);
  34. return Scaffold(
  35. appBar: AppBar(
  36. title: const Text('我的'),
  37. actions: [
  38. IconButton(
  39. icon: const Icon(Icons.settings_outlined),
  40. onPressed: () {
  41. // 跳转到设置页面
  42. },
  43. ),
  44. ],
  45. ),
  46. body: SingleChildScrollView(
  47. child: Column(
  48. children: [
  49. // 用户信息卡片
  50. Card(
  51. margin: const EdgeInsets.all(16),
  52. shape: RoundedRectangleBorder(
  53. borderRadius: BorderRadius.circular(16),
  54. ),
  55. elevation: 2,
  56. child: Padding(
  57. padding: const EdgeInsets.all(20),
  58. child: Column(
  59. children: [
  60. GestureDetector(
  61. onTap: () {
  62. // 点击头像
  63. },
  64. child: Stack(
  65. children: [
  66. CircleAvatar(
  67. radius: 50,
  68. backgroundColor: Colors.blue[100],
  69. backgroundImage: authProvider.user?.avatar != null
  70. ? NetworkImage(authProvider.user!.avatar!)
  71. : null,
  72. child: authProvider.user?.avatar == null
  73. ? const Icon(
  74. Icons.person,
  75. size: 60,
  76. color: Colors.blue,
  77. )
  78. : null,
  79. ),
  80. Positioned(
  81. bottom: 0,
  82. right: 0,
  83. child: Container(
  84. padding: const EdgeInsets.all(6),
  85. decoration: BoxDecoration(
  86. color: Colors.blue,
  87. borderRadius: BorderRadius.circular(20),
  88. border: Border.all(
  89. color: Colors.white,
  90. width: 2,
  91. ),
  92. ),
  93. child: const Icon(
  94. Icons.edit,
  95. size: 16,
  96. color: Colors.white,
  97. ),
  98. ),
  99. ),
  100. ],
  101. ),
  102. ),
  103. const SizedBox(height: 16),
  104. Text(
  105. authProvider.user?.fullName ?? '用户',
  106. style: const TextStyle(
  107. fontSize: 22,
  108. fontWeight: FontWeight.bold,
  109. ),
  110. ),
  111. const SizedBox(height: 8),
  112. Text(
  113. authProvider.user?.email ?? '',
  114. style: TextStyle(
  115. fontSize: 14,
  116. color: Colors.grey[600],
  117. ),
  118. ),
  119. if (authProvider.user?.phone != null)
  120. Padding(
  121. padding: const EdgeInsets.only(top: 4),
  122. child: Text(
  123. authProvider.user!.phone!,
  124. style: TextStyle(
  125. fontSize: 14,
  126. color: Colors.grey[600],
  127. ),
  128. ),
  129. ),
  130. const SizedBox(height: 16),
  131. Row(
  132. mainAxisAlignment: MainAxisAlignment.center,
  133. children: [
  134. _buildStatItem('关注', '128'),
  135. _buildVerticalDivider(),
  136. _buildStatItem('粉丝', '256'),
  137. _buildVerticalDivider(),
  138. _buildStatItem('积分', '1024'),
  139. ],
  140. ),
  141. ],
  142. ),
  143. ),
  144. ),
  145. // 菜单项
  146. Padding(
  147. padding: const EdgeInsets.symmetric(horizontal: 16),
  148. child: Column(
  149. children: [
  150. _buildMenuCard(
  151. title: '账户设置',
  152. items: [
  153. _buildMenuItem(
  154. icon: Icons.person_outline,
  155. title: '个人信息',
  156. subtitle: '查看和编辑个人信息',
  157. onTap: () {
  158. Navigator.of(context).push(
  159. MaterialPageRoute(
  160. builder: (_) => const ProfileDetailScreen(),
  161. ),
  162. );
  163. },
  164. ),
  165. _buildMenuItem(
  166. icon: Icons.lock_outline,
  167. title: '账号安全',
  168. subtitle: '修改密码和安全设置',
  169. onTap: () {},
  170. ),
  171. _buildMenuItem(
  172. icon: Icons.notifications_none,
  173. title: '消息通知',
  174. subtitle: '管理通知偏好设置',
  175. onTap: () {},
  176. ),
  177. ],
  178. ),
  179. const SizedBox(height: 16),
  180. _buildMenuCard(
  181. title: '我的内容',
  182. items: [
  183. _buildMenuItem(
  184. icon: Icons.bookmark_border,
  185. title: '我的收藏',
  186. subtitle: '查看收藏的内容',
  187. onTap: () {},
  188. ),
  189. _buildMenuItem(
  190. icon: Icons.history,
  191. title: '浏览历史',
  192. subtitle: '查看最近浏览记录',
  193. onTap: () {},
  194. ),
  195. _buildMenuItem(
  196. icon: Icons.download,
  197. title: '我的下载',
  198. subtitle: '管理下载的文件',
  199. onTap: () {},
  200. ),
  201. ],
  202. ),
  203. const SizedBox(height: 16),
  204. _buildMenuCard(
  205. title: '其他',
  206. items: [
  207. _buildMenuItem(
  208. icon: Icons.help_outline,
  209. title: '帮助中心',
  210. subtitle: '常见问题和帮助文档',
  211. onTap: () {},
  212. ),
  213. _buildMenuItem(
  214. icon: Icons.info_outline,
  215. title: '关于我们',
  216. subtitle: '了解应用信息',
  217. onTap: () {},
  218. ),
  219. _buildMenuItem(
  220. icon: Icons.logout,
  221. title: '退出登录',
  222. subtitle: '安全退出当前账号',
  223. onTap: () async {
  224. // 防止重复点击
  225. if (authProvider.isLoading) return;
  226. final shouldLogout = await showDialog<bool>(
  227. context: context,
  228. builder: (context) {
  229. final isIOS = Theme.of(context).platform == TargetPlatform.iOS;
  230. if (isIOS) {
  231. // iOS风格:确定在右边,取消在左边
  232. return AlertDialog(
  233. title: const Text('确认退出'),
  234. content: const Text('确定要退出登录吗?'),
  235. actionsAlignment: MainAxisAlignment.spaceBetween,
  236. actions: [
  237. TextButton(
  238. onPressed: () => Navigator.of(context).pop(true),
  239. child: const Text('确定'),
  240. ),
  241. TextButton(
  242. onPressed: () => Navigator.of(context).pop(false),
  243. child: const Text('取消'),
  244. ),
  245. ],
  246. );
  247. } else {
  248. // Android/Material风格:取消在左边,确定在右边
  249. return AlertDialog(
  250. title: const Text('确认退出'),
  251. content: const Text('确定要退出登录吗?'),
  252. actionsAlignment: MainAxisAlignment.spaceBetween,
  253. actions: [
  254. TextButton(
  255. onPressed: () => Navigator.of(context).pop(false),
  256. child: const Text('取消'),
  257. ),
  258. TextButton(
  259. onPressed: () => Navigator.of(context).pop(true),
  260. child: const Text('确定'),
  261. ),
  262. ],
  263. );
  264. }
  265. },
  266. );
  267. if (shouldLogout == true) {
  268. await authProvider.logout();
  269. // 登出后,确保在组件仍然存在时进行导航
  270. if (mounted) {
  271. // 使用pushReplacementNamed清空导航栈
  272. Navigator.of(context).pushReplacementNamed(RouteConstants.home);
  273. }
  274. }
  275. },
  276. ),
  277. ],
  278. ),
  279. const SizedBox(height: 20),
  280. // 版本信息
  281. Padding(
  282. padding: const EdgeInsets.symmetric(vertical: 20),
  283. child: Text(
  284. '版本 v1.0.0',
  285. style: TextStyle(
  286. fontSize: 12,
  287. color: Colors.grey[500],
  288. ),
  289. ),
  290. ),
  291. ],
  292. ),
  293. ),
  294. ],
  295. ),
  296. ),
  297. bottomNavigationBar: const BottomNavBar(initialIndex: 3),
  298. );
  299. }
  300. Widget _buildStatItem(String label, String value) {
  301. return Expanded(
  302. child: Column(
  303. children: [
  304. Text(
  305. value,
  306. style: const TextStyle(
  307. fontSize: 18,
  308. fontWeight: FontWeight.bold,
  309. ),
  310. ),
  311. const SizedBox(height: 4),
  312. Text(
  313. label,
  314. style: TextStyle(
  315. fontSize: 12,
  316. color: Colors.grey[600],
  317. ),
  318. ),
  319. ],
  320. ),
  321. );
  322. }
  323. Widget _buildVerticalDivider() {
  324. return Container(
  325. width: 1,
  326. height: 40,
  327. color: Colors.grey[300],
  328. margin: const EdgeInsets.symmetric(horizontal: 16),
  329. );
  330. }
  331. Widget _buildMenuCard({
  332. required String title,
  333. required List<Widget> items,
  334. }) {
  335. return Card(
  336. elevation: 2,
  337. shape: RoundedRectangleBorder(
  338. borderRadius: BorderRadius.circular(12),
  339. ),
  340. child: Padding(
  341. padding: const EdgeInsets.all(16),
  342. child: Column(
  343. crossAxisAlignment: CrossAxisAlignment.start,
  344. children: [
  345. Text(
  346. title,
  347. style: const TextStyle(
  348. fontSize: 16,
  349. fontWeight: FontWeight.bold,
  350. ),
  351. ),
  352. const SizedBox(height: 12),
  353. Column(children: items),
  354. ],
  355. ),
  356. ),
  357. );
  358. }
  359. Widget _buildMenuItem({
  360. required IconData icon,
  361. required String title,
  362. required String subtitle,
  363. required VoidCallback onTap,
  364. }) {
  365. return ListTile(
  366. contentPadding: EdgeInsets.zero,
  367. leading: Icon(icon),
  368. title: Text(title),
  369. subtitle: Text(subtitle),
  370. trailing: const Icon(Icons.chevron_right),
  371. onTap: onTap,
  372. );
  373. }
  374. }