Просмотр исходного кода

Merge branch 'afan_local_1215' of afan/flutter_app_android into develop

刘清 1 месяц назад
Родитель
Сommit
1d6d063acd

+ 1
- 6
lib/presentation/navigation/app_router.dart Просмотреть файл

32
         return MaterialPageRoute(builder: (_) => const ServicesScreen());
32
         return MaterialPageRoute(builder: (_) => const ServicesScreen());
33
       
33
       
34
       case RouteConstants.profile:
34
       case RouteConstants.profile:
35
-        return MaterialPageRoute(
36
-          builder: (_) => ProtectedRoute(
37
-            routeName: RouteConstants.profile,
38
-            child: const ProfileScreen(),
39
-          ),
40
-        );
35
+        return MaterialPageRoute(builder: (_) => const ProfileScreen());
41
       
36
       
42
       case RouteConstants.profileDetail:
37
       case RouteConstants.profileDetail:
43
         return MaterialPageRoute(
38
         return MaterialPageRoute(

+ 1
- 10
lib/presentation/navigation/bottom_nav_bar.dart Просмотреть файл

1
 import 'package:flutter/material.dart';
1
 import 'package:flutter/material.dart';
2
-import 'package:provider/provider.dart';
3
 import '../../core/constants/route_constants.dart';
2
 import '../../core/constants/route_constants.dart';
4
-import '../../presentation/providers/auth_provider.dart';
5
 
3
 
6
 class BottomNavBar extends StatefulWidget {
4
 class BottomNavBar extends StatefulWidget {
7
   final int initialIndex;
5
   final int initialIndex;
25
   }
23
   }
26
   
24
   
27
   void _onItemTapped(int index) {
25
   void _onItemTapped(int index) {
28
-    final authProvider = Provider.of<AuthProvider>(context, listen: false);
29
-    
30
-    // 如果点击"我的"但未登录,跳转到登录页
31
-    if (index == 3 && !authProvider.isAuthenticated) {
32
-      Navigator.of(context).pushNamed(RouteConstants.login);
33
-      return;
34
-    }
35
     
26
     
36
     setState(() {
27
     setState(() {
37
       _selectedIndex = index;
28
       _selectedIndex = index;
73
         BottomNavigationBarItem(
64
         BottomNavigationBarItem(
74
           icon: Icon(Icons.newspaper_outlined),
65
           icon: Icon(Icons.newspaper_outlined),
75
           activeIcon: Icon(Icons.newspaper),
66
           activeIcon: Icon(Icons.newspaper),
76
-          label: '资讯',
67
+          label: '推荐',
77
         ),
68
         ),
78
         BottomNavigationBarItem(
69
         BottomNavigationBarItem(
79
           icon: Icon(Icons.work_outline),
70
           icon: Icon(Icons.work_outline),

+ 5
- 0
lib/presentation/providers/auth_provider.dart Просмотреть файл

112
     _error = null;
112
     _error = null;
113
     notifyListeners();
113
     notifyListeners();
114
   }
114
   }
115
+
116
+  void updateUser(User? user) {
117
+    _user = user;
118
+    notifyListeners();
119
+  }
115
 }
120
 }

+ 5
- 0
lib/presentation/providers/user_provider.dart Просмотреть файл

59
     _error = null;
59
     _error = null;
60
     notifyListeners();
60
     notifyListeners();
61
   }
61
   }
62
+
63
+  void updateUser(User? user) {
64
+    _user = user;
65
+    notifyListeners();
66
+  }
62
 }
67
 }

+ 5
- 0
lib/presentation/screens/auth/login_screen.dart Просмотреть файл

2
 import 'package:provider/provider.dart';
2
 import 'package:provider/provider.dart';
3
 import '../../providers/auth_provider.dart';
3
 import '../../providers/auth_provider.dart';
4
 import '../../../core/constants/route_constants.dart';
4
 import '../../../core/constants/route_constants.dart';
5
+import '../../providers/user_provider.dart';
5
 import '../../widgets/common/app_button.dart';
6
 import '../../widgets/common/app_button.dart';
6
 import '../../widgets/common/app_text_field.dart';
7
 import '../../widgets/common/app_text_field.dart';
7
 import '../../widgets/common/loading_indicator.dart';
8
 import '../../widgets/common/loading_indicator.dart';
120
                           _passwordController.text,
121
                           _passwordController.text,
121
                         );
122
                         );
122
                         if (authProvider.isAuthenticated) {
123
                         if (authProvider.isAuthenticated) {
124
+                          // ✅ 同时更新 userProvider
125
+                          final userProvider = Provider.of<UserProvider>(context, listen: false);
126
+                          userProvider.updateUser(authProvider.user);
127
+
123
                           // widget.onSuccess?.call();
128
                           // widget.onSuccess?.call();
124
                           Navigator.of(context).pushReplacementNamed(RouteConstants.home);
129
                           Navigator.of(context).pushReplacementNamed(RouteConstants.home);
125
                           // Navigator.of(context).pushNamedAndRemoveUntil(RouteConstants.home);
130
                           // Navigator.of(context).pushNamedAndRemoveUntil(RouteConstants.home);

+ 7
- 24
lib/presentation/screens/home/home_screen.dart Просмотреть файл

2
 import 'package:provider/provider.dart';
2
 import 'package:provider/provider.dart';
3
 import '../../navigation/bottom_nav_bar.dart';
3
 import '../../navigation/bottom_nav_bar.dart';
4
 import '../../providers/auth_provider.dart';
4
 import '../../providers/auth_provider.dart';
5
-import '../../widgets/common/app_button.dart';
6
 
5
 
7
 class HomeScreen extends StatelessWidget {
6
 class HomeScreen extends StatelessWidget {
8
   const HomeScreen({super.key});
7
   const HomeScreen({super.key});
18
           if (authProvider.isAuthenticated)
17
           if (authProvider.isAuthenticated)
19
             IconButton(
18
             IconButton(
20
               icon: const Icon(Icons.notifications_none),
19
               icon: const Icon(Icons.notifications_none),
21
-              onPressed: () {},
20
+              onPressed: () {
21
+                // 进入消息中心界面
22
+                // Navigator.of(context).pushNamed('/login');
23
+              },
22
             ),
24
             ),
23
         ],
25
         ],
24
       ),
26
       ),
42
                     children: [
44
                     children: [
43
                       Row(
45
                       Row(
44
                         children: [
46
                         children: [
45
-                          CircleAvatar(
46
-                            radius: 30,
47
-                            backgroundColor: Colors.blue[100],
48
-                            child: Icon(
49
-                              Icons.person,
50
-                              size: 30,
51
-                              color: Colors.blue,
52
-                            ),
53
-                          ),
54
-                          const SizedBox(width: 16),
55
                           Expanded(
47
                           Expanded(
56
                             child: Column(
48
                             child: Column(
57
                               crossAxisAlignment: CrossAxisAlignment.start,
49
                               crossAxisAlignment: CrossAxisAlignment.start,
68
                                 const SizedBox(height: 4),
60
                                 const SizedBox(height: 4),
69
                                 Text(
61
                                 Text(
70
                                   authProvider.isAuthenticated
62
                                   authProvider.isAuthenticated
71
-                                      ? '欢迎回来!'
63
+                                      ? '这里会展示一些有趣的东西!'
72
                                       : '请登录以使用完整功能',
64
                                       : '请登录以使用完整功能',
73
                                   style: TextStyle(
65
                                   style: TextStyle(
74
                                     fontSize: 14,
66
                                     fontSize: 14,
80
                           ),
72
                           ),
81
                         ],
73
                         ],
82
                       ),
74
                       ),
83
-                      const SizedBox(height: 20),
84
-                      if (!authProvider.isAuthenticated)
85
-                        AppButton(
86
-                          text: '立即登录',
87
-                          onPressed: () {
88
-                            Navigator.of(context).pushNamed('/login');
89
-                          },
90
-                          backgroundColor: Colors.blue,
91
-                          height: 45,
92
-                        ),
75
+                      const SizedBox(height: 100),
93
                     ],
76
                     ],
94
                   ),
77
                   ),
95
                 ),
78
                 ),
113
                 children: [
96
                 children: [
114
                   _buildFeatureCard(
97
                   _buildFeatureCard(
115
                     icon: Icons.newspaper,
98
                     icon: Icons.newspaper,
116
-                    title: '资讯',
99
+                    title: '推荐',
117
                     color: Colors.green,
100
                     color: Colors.green,
118
                     onTap: () {
101
                     onTap: () {
119
                       Navigator.of(context).pushNamed('/news');
102
                       Navigator.of(context).pushNamed('/news');

+ 80
- 52
lib/presentation/screens/news/news_screen.dart Просмотреть файл

1
 import 'package:flutter/material.dart';
1
 import 'package:flutter/material.dart';
2
 import '../../navigation/bottom_nav_bar.dart';
2
 import '../../navigation/bottom_nav_bar.dart';
3
+import '../../widgets/common/app_search_bar.dart';
3
 
4
 
4
 class NewsScreen extends StatelessWidget {
5
 class NewsScreen extends StatelessWidget {
5
   const NewsScreen({super.key});
6
   const NewsScreen({super.key});
8
   Widget build(BuildContext context) {
9
   Widget build(BuildContext context) {
9
     return Scaffold(
10
     return Scaffold(
10
       appBar: AppBar(
11
       appBar: AppBar(
11
-        title: const Text('资讯'),
12
-        actions: [
13
-          IconButton(
14
-            icon: const Icon(Icons.search),
15
-            onPressed: () {},
16
-          ),
17
-        ],
12
+        title: const Text('推荐'),
18
       ),
13
       ),
19
-      body: ListView.builder(
20
-        padding: const EdgeInsets.all(16),
21
-        itemCount: 10,
22
-        itemBuilder: (context, index) {
23
-          return _buildNewsCard(index);
24
-        },
14
+      body: SingleChildScrollView(
15
+        padding: const EdgeInsets.all(20),
16
+        child: Column(
17
+          crossAxisAlignment: CrossAxisAlignment.start,
18
+          children: [
19
+            // 使用 StatefulWidget 搜索框
20
+            SearchBarCustom(
21
+              hintText: '搜索...',
22
+              onSearch: (query) {
23
+                // 处理搜索逻辑
24
+                // print('搜索: $query');
25
+              },
26
+              onChanged: (query) {
27
+                // 实时搜索
28
+                // print('输入变化: $query');
29
+              },
30
+              onClear: () {
31
+                // print('清除搜索');
32
+              },
33
+            ),
34
+            const SizedBox(height: 24),
35
+
36
+            // 推荐列表
37
+            ListView.builder(
38
+              shrinkWrap: true,
39
+              physics: const NeverScrollableScrollPhysics(),
40
+              itemCount: 10,
41
+              itemBuilder: (context, index) {
42
+                return _buildNewsCard(index);
43
+              },
44
+            ),
45
+          ],
46
+        ),
25
       ),
47
       ),
26
       bottomNavigationBar: const BottomNavBar(initialIndex: 1),
48
       bottomNavigationBar: const BottomNavBar(initialIndex: 1),
27
     );
49
     );
29
   
51
   
30
   Widget _buildNewsCard(int index) {
52
   Widget _buildNewsCard(int index) {
31
     final titles = [
53
     final titles = [
32
-      'Flutter 3.0 新特性详解',
33
-      'Dart 语言最新更新',
34
-      '移动开发趋势分析',
35
-      '前端框架对比',
36
-      '用户体验设计原则',
37
-      '后端架构最佳实践',
38
-      '数据库性能优化',
39
-      '云原生技术解析',
40
-      '人工智能在移动端的应用',
41
-      '跨平台开发方案比较',
54
+      '体彩 大乐透 20260001期',
55
+      '福彩 双色球 20260002期',
56
+      '福彩 双色球 20260003期',
57
+      '体彩 大乐透 20260004期',
58
+      '福彩 双色球 20260005期',
59
+      '福彩 双色球 20260007期',
60
+      '体彩 大乐透 20260006期',
61
+      '福彩 双色球 20260008期',
62
+      '福彩 双色球 20260009期',
63
+      '福彩 快乐八 20260010期',
42
     ];
64
     ];
43
     
65
     
44
     final subtitles = [
66
     final subtitles = [
45
-      '深入了解Flutter最新版本的重要更新和改进',
46
-      'Dart语言的最新特性和优化方向',
47
-      '2024年移动开发的重要趋势和技术方向',
48
-      '主流前端框架的优缺点对比分析',
49
-      '提升应用用户体验的关键设计原则',
50
-      '构建高性能后端服务的最佳实践',
51
-      '数据库查询优化和性能调优技巧',
52
-      '云原生技术在微服务中的应用',
53
-      'AI技术在移动应用中的创新应用',
54
-      '各种跨平台开发方案的对比和选择',
67
+      '预测号码:4 5 8 6 25 8 9 6',
68
+      '预测号码:4 5 8 6 25 8 9 6',
69
+      '预测号码:4 5 8 6 25 8 9 6',
70
+      '预测号码:4 5 8 6 25 8 9 6',
71
+      '预测号码:4 5 8 6 25 8 9 6',
72
+      '预测号码:4 5 8 6 25 8 9 6',
73
+      '预测号码:4 5 8 6 25 8 9 6',
74
+      '预测号码:4 5 8 6 25 8 9 6',
75
+      '预测号码:4 5 8 6 25 8 9 6',
76
+      '预测号码:4 5 8 6 25 8 9 6',
55
     ];
77
     ];
56
     
78
     
57
     final times = [
79
     final times = [
58
-      '2小时前',
59
-      '5小时前',
60
-      '昨天',
61
-      '2天前',
62
-      '3天前',
63
-      '1周前',
64
-      '1周前',
65
-      '2周前',
66
-      '2周前',
67
-      '1个月前',
80
+      '2小时前 :aaa',
81
+      '5小时前 :aaa',
82
+      '昨天 :aaa',
83
+      '2天前 :aaa',
84
+      '3天前 :aaa',
85
+      '1周前 :aaa',
86
+      '1周前 :aaa',
87
+      '2周前 :aaa',
88
+      '2周前 :aaa',
89
+      '1个月前 :aaa',
68
     ];
90
     ];
69
     
91
     
70
     return Card(
92
     return Card(
73
         borderRadius: BorderRadius.circular(12),
95
         borderRadius: BorderRadius.circular(12),
74
       ),
96
       ),
75
       child: InkWell(
97
       child: InkWell(
76
-        onTap: () {},
98
+        onTap: () {
99
+          print(index);
100
+        },
77
         borderRadius: BorderRadius.circular(12),
101
         borderRadius: BorderRadius.circular(12),
78
         child: Padding(
102
         child: Padding(
79
           padding: const EdgeInsets.all(16),
103
           padding: const EdgeInsets.all(16),
91
                     ),
115
                     ),
92
                     child: Center(
116
                     child: Center(
93
                       child: Text(
117
                       child: Text(
94
-                        '新闻',
118
+                        '推号',
95
                         style: TextStyle(
119
                         style: TextStyle(
96
                           color: Colors.white,
120
                           color: Colors.white,
97
                           fontWeight: FontWeight.bold,
121
                           fontWeight: FontWeight.bold,
107
                         Text(
131
                         Text(
108
                           titles[index],
132
                           titles[index],
109
                           style: const TextStyle(
133
                           style: const TextStyle(
110
-                            fontSize: 16,
134
+                            fontSize: 18,
111
                             fontWeight: FontWeight.bold,
135
                             fontWeight: FontWeight.bold,
112
                           ),
136
                           ),
113
                           maxLines: 2,
137
                           maxLines: 2,
117
                         Text(
141
                         Text(
118
                           subtitles[index],
142
                           subtitles[index],
119
                           style: TextStyle(
143
                           style: TextStyle(
120
-                            fontSize: 14,
144
+                            fontSize: 16,
121
                             color: Colors.grey[600],
145
                             color: Colors.grey[600],
122
                           ),
146
                           ),
123
                           maxLines: 2,
147
                           maxLines: 2,
128
                   ),
152
                   ),
129
                 ],
153
                 ],
130
               ),
154
               ),
131
-              const SizedBox(height: 12),
155
+              const SizedBox(height: 4),
132
               Row(
156
               Row(
133
                 children: [
157
                 children: [
134
                   Icon(
158
                   Icon(
135
                     Icons.schedule,
159
                     Icons.schedule,
136
-                    size: 14,
160
+                    size: 16,
137
                     color: Colors.grey[500],
161
                     color: Colors.grey[500],
138
                   ),
162
                   ),
139
                   const SizedBox(width: 4),
163
                   const SizedBox(width: 4),
140
                   Text(
164
                   Text(
141
                     times[index],
165
                     times[index],
142
                     style: TextStyle(
166
                     style: TextStyle(
143
-                      fontSize: 12,
167
+                      fontSize: 14,
144
                       color: Colors.grey[500],
168
                       color: Colors.grey[500],
145
                     ),
169
                     ),
146
                   ),
170
                   ),
150
                       Icons.bookmark_border,
174
                       Icons.bookmark_border,
151
                       color: Colors.grey[500],
175
                       color: Colors.grey[500],
152
                     ),
176
                     ),
153
-                    onPressed: () {},
177
+                    onPressed: () {
178
+                      // 添加书签
179
+                    },
154
                     iconSize: 20,
180
                     iconSize: 20,
155
                   ),
181
                   ),
156
                   IconButton(
182
                   IconButton(
158
                       Icons.share,
184
                       Icons.share,
159
                       color: Colors.grey[500],
185
                       color: Colors.grey[500],
160
                     ),
186
                     ),
161
-                    onPressed: () {},
187
+                    onPressed: () {
188
+                      // 分享
189
+                    },
162
                     iconSize: 20,
190
                     iconSize: 20,
163
                   ),
191
                   ),
164
                 ],
192
                 ],

+ 9
- 2
lib/presentation/screens/profile/profile_detail_screen.dart Просмотреть файл

1
 import 'package:flutter/material.dart';
1
 import 'package:flutter/material.dart';
2
 import 'package:provider/provider.dart';
2
 import 'package:provider/provider.dart';
3
 import '../../../data/models/user.dart';
3
 import '../../../data/models/user.dart';
4
+import '../../providers/auth_provider.dart';
4
 import '../../providers/user_provider.dart';
5
 import '../../providers/user_provider.dart';
5
 import '../../widgets/common/app_button.dart';
6
 import '../../widgets/common/app_button.dart';
6
 import '../../widgets/common/app_text_field.dart';
7
 import '../../widgets/common/app_text_field.dart';
24
     super.initState();
25
     super.initState();
25
     final userProvider = Provider.of<UserProvider>(context, listen: false);
26
     final userProvider = Provider.of<UserProvider>(context, listen: false);
26
     _currentUser = userProvider.user ?? User(id: -1, email: '', username: '', createdAt: DateTime.now());
27
     _currentUser = userProvider.user ?? User(id: -1, email: '', username: '', createdAt: DateTime.now());
27
-    _nameController = TextEditingController(text: _currentUser.username);
28
+    _nameController = TextEditingController(text: _currentUser.fullName);
28
     _emailController = TextEditingController(text: _currentUser.email);
29
     _emailController = TextEditingController(text: _currentUser.email);
29
     _phoneController = TextEditingController(text: _currentUser.phone ?? '');
30
     _phoneController = TextEditingController(text: _currentUser.phone ?? '');
30
   }
31
   }
183
                     
184
                     
184
                     await userProvider.updateUserProfile(updatedUser);
185
                     await userProvider.updateUserProfile(updatedUser);
185
                     
186
                     
186
-                    if (userProvider.error == null) {
187
+                    if (userProvider.error == null && mounted) {
188
+                      // ✅ 同时更新 AuthProvider
189
+                      final authProvider = Provider.of<AuthProvider>(context, listen: false);
190
+                      authProvider.updateUser(userProvider.user);  // 使用接口返回的完整用户数据
191
+
187
                       ScaffoldMessenger.of(context).showSnackBar(
192
                       ScaffoldMessenger.of(context).showSnackBar(
188
                         const SnackBar(
193
                         const SnackBar(
189
                           content: Text('个人信息已更新'),
194
                           content: Text('个人信息已更新'),
190
                           duration: Duration(seconds: 2),
195
                           duration: Duration(seconds: 2),
191
                         ),
196
                         ),
192
                       );
197
                       );
198
+
199
+                      // ✅ 直接返回 - 数据已同步更新
193
                       Navigator.of(context).pop();
200
                       Navigator.of(context).pop();
194
                     }
201
                     }
195
                   }
202
                   }

+ 272
- 206
lib/presentation/screens/profile/profile_screen.dart Просмотреть файл

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

+ 0
- 20
lib/presentation/screens/services/services_screen.dart Просмотреть файл

15
         child: Column(
15
         child: Column(
16
           crossAxisAlignment: CrossAxisAlignment.start,
16
           crossAxisAlignment: CrossAxisAlignment.start,
17
           children: [
17
           children: [
18
-            // 搜索框
19
-            Container(
20
-              decoration: BoxDecoration(
21
-                color: Colors.grey[100],
22
-                borderRadius: BorderRadius.circular(25),
23
-              ),
24
-              child: TextField(
25
-                decoration: InputDecoration(
26
-                  hintText: '搜索服务...',
27
-                  border: InputBorder.none,
28
-                  prefixIcon: const Icon(Icons.search),
29
-                  contentPadding: const EdgeInsets.symmetric(
30
-                    vertical: 15,
31
-                    horizontal: 20,
32
-                  ),
33
-                ),
34
-              ),
35
-            ),
36
-            const SizedBox(height: 30),
37
-            
38
             // 服务分类
18
             // 服务分类
39
             const Text(
19
             const Text(
40
               '服务分类',
20
               '服务分类',

+ 72
- 0
lib/presentation/widgets/common/app_search_bar.dart Просмотреть файл

1
+// 独立的搜索框组件
2
+import 'package:flutter/material.dart';
3
+
4
+class SearchBarCustom extends StatefulWidget {
5
+  final ValueChanged<String>? onSearch;
6
+  final ValueChanged<String>? onChanged;
7
+  final VoidCallback? onClear;
8
+  final String hintText;
9
+  
10
+  const SearchBarCustom({
11
+    super.key,
12
+    this.onSearch,
13
+    this.onChanged,
14
+    this.onClear,
15
+    this.hintText = '搜索...',
16
+  });
17
+  
18
+  @override
19
+  State<SearchBarCustom> createState() => _SearchBarCustomState();
20
+}
21
+
22
+class _SearchBarCustomState extends State<SearchBarCustom> {
23
+  final TextEditingController _controller = TextEditingController();
24
+  final FocusNode _focusNode = FocusNode();
25
+  
26
+  @override
27
+  void dispose() {
28
+    _controller.dispose();
29
+    _focusNode.dispose();
30
+    super.dispose();
31
+  }
32
+  
33
+  @override
34
+  Widget build(BuildContext context) {
35
+    return Container(
36
+      decoration: BoxDecoration(
37
+        color: Colors.grey[100],
38
+        borderRadius: BorderRadius.circular(15),
39
+      ),
40
+      child: TextField(
41
+        controller: _controller,
42
+        focusNode: _focusNode,
43
+        decoration: InputDecoration(
44
+          hintText: widget.hintText,
45
+          border: InputBorder.none,
46
+          prefixIcon: const Icon(Icons.search),
47
+          suffixIcon: _controller.text.isNotEmpty
48
+              ? IconButton(
49
+                  icon: const Icon(Icons.clear),
50
+                  onPressed: () {
51
+                    _controller.clear();
52
+                    widget.onClear?.call();
53
+                  },
54
+                )
55
+              : null,
56
+          contentPadding: const EdgeInsets.symmetric(
57
+            vertical: 15,
58
+            horizontal: 20,
59
+          ),
60
+        ),
61
+        onChanged: (value) {
62
+          setState(() {}); // 更新清除按钮显示
63
+          widget.onChanged?.call(value);
64
+        },
65
+        onSubmitted: (value) {
66
+          widget.onSearch?.call(value);
67
+          _focusNode.unfocus();
68
+        },
69
+      ),
70
+    );
71
+  }
72
+}