Skip to content

Commit 80f33bd

Browse files
committed
修復 QR 碼 404 問題並優化用戶體驗
🔧 主要修改: - 修改路由權限控制,允許未登入用戶訪問 /user/:uid 和 /flag/:uid - 添加公開頁面列表,只對敏感頁面要求登入 - 優化 UserDetailScreen 的用戶體驗 ✨ 新功能: - 未登入用戶可以瀏覽他人的個人資料頁面 - 添加登入提示和快速登入按鈕 - 智慧導航:根據登入狀態決定返回頁面 📚 文檔: - 創建 QR_CODE_404_FIX.md 詳細說明解決方案 - 創建 DYNAMIC_ROUTING_TUTORIAL.md 動態路由教學 🎯 解決問題: - QR 碼連結現在可以被任何人訪問 - 保持安全性的同時提升分享功能實用性
1 parent 000f2b4 commit 80f33bd

File tree

6 files changed

+467
-56
lines changed

6 files changed

+467
-56
lines changed

DYNAMIC_ROUTING_TUTORIAL.md

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# 🎯 動態路由教學 - 給高一生的完整指南
2+
3+
## 📚 什麼是動態路由?
4+
5+
### 🏫 用學校的例子來理解
6+
7+
想像你在學校裡:
8+
9+
**靜態路由(固定路徑):**
10+
- 去「圖書館」 → 永遠是同一個地方
11+
- 去「操場」 → 永遠是同一個地方
12+
13+
**動態路由(變化路徑):**
14+
- 去「高一X班的教室」 → X可以是1班、2班、3班...
15+
- 去「學號12345同學的座位」 → 12345可以是任何學號
16+
17+
### 🌐 在網頁應用中
18+
19+
**靜態路由:**
20+
```
21+
/home → 首頁(固定)
22+
/profile → 個人資料頁(固定)
23+
/map → 地圖頁(固定)
24+
```
25+
26+
**動態路由:**
27+
```
28+
/user/12345 → 顯示用戶12345的資料
29+
/user/67890 → 顯示用戶67890的資料
30+
/user/abc123 → 顯示用戶abc123的資料
31+
```
32+
33+
## 🔧 在你的專案中如何實作?
34+
35+
### 1. 設定動態路由
36+
37+
`app_router.dart` 中:
38+
39+
```dart
40+
GoRoute(
41+
path: '/user/:uid', // ⭐ :uid 是動態參數
42+
name: 'userDetail',
43+
builder: (context, state) {
44+
final uid = state.pathParameters['uid']!; // 📝 取得動態參數
45+
return UserDetailScreen(uid: uid); // 📝 傳給頁面元件
46+
},
47+
),
48+
```
49+
50+
**解釋:**
51+
- `:uid` 就像一個「變數」,可以放入任何值
52+
- `state.pathParameters['uid']` 用來取得這個變數的值
53+
- 例如訪問 `/user/abc123`,uid 就會是 "abc123"
54+
55+
### 2. QR 碼如何連接到動態路由?
56+
57+
#### 🎯 生成 QR 碼
58+
59+
```dart
60+
final user = FirebaseAuth.instance.currentUser;
61+
final uid = user?.uid ?? '';
62+
63+
// 🔥 QR 碼內容就是動態路由的完整 URL
64+
final qrData = 'https://auto30next.alearn.org.tw/user/$uid';
65+
```
66+
67+
**這樣生成的 QR 碼內容會是:**
68+
- 如果你的 uid 是 "student123"
69+
- QR 碼內容:`https://auto30next.alearn.org.tw/user/student123`
70+
71+
#### 📱 掃描 QR 碼並跳轉
72+
73+
```dart
74+
void _startScanning(BuildContext context) {
75+
// 假設掃描到:https://auto30next.alearn.org.tw/user/friend456
76+
final scannedUrl = 'https://auto30next.alearn.org.tw/user/friend456';
77+
78+
// 📝 從 URL 中提取用戶 ID
79+
final uri = Uri.parse(scannedUrl);
80+
final pathSegments = uri.pathSegments; // ['user', 'friend456']
81+
82+
if (pathSegments.length >= 2 && pathSegments[0] == 'user') {
83+
final scannedUserId = pathSegments[1]; // 'friend456'
84+
85+
// 🚀 跳轉到動態路由
86+
context.push('/user/$scannedUserId');
87+
}
88+
}
89+
```
90+
91+
## 🎮 完整流程示例
92+
93+
### 情境:小明想分享他的個人資料給小華
94+
95+
1. **小明生成 QR 碼:**
96+
- 小明的 uid: "ming123"
97+
- QR 碼內容: `https://auto30next.alearn.org.tw/user/ming123`
98+
99+
2. **小華掃描 QR 碼:**
100+
- 掃描器讀取到: `https://auto30next.alearn.org.tw/user/ming123`
101+
- 程式提取出用戶 ID: "ming123"
102+
103+
3. **跳轉到動態路由:**
104+
- 執行: `context.push('/user/ming123')`
105+
- Go Router 找到對應路由: `/user/:uid`
106+
- 將 "ming123" 傳給 `UserDetailScreen`
107+
108+
4. **顯示小明的個人頁面:**
109+
- `UserDetailScreen` 收到 uid = "ming123"
110+
- 從 Firebase 載入小明的資料
111+
- 顯示小明的個人資料頁面
112+
113+
## 💡 為什麼要用動態路由?
114+
115+
### ✅ 優點:
116+
117+
1. **靈活性:** 一個路由模板可以處理無數個用戶
118+
2. **可分享:** 每個用戶都有獨特的 URL 可以分享
119+
3. **SEO 友善:** 搜尋引擎可以索引每個用戶頁面
120+
4. **書籤功能:** 可以收藏特定用戶的頁面
121+
122+
### 🔄 如果不用動態路由會怎樣?
123+
124+
你需要為每個用戶創建一個路由:
125+
```dart
126+
GoRoute(path: '/user_ming123', ...),
127+
GoRoute(path: '/user_hua456', ...),
128+
GoRoute(path: '/user_mei789', ...),
129+
// ... 無限多個路由 😱
130+
```
131+
132+
這樣顯然不實際!
133+
134+
## 🛠️ 其他動態路由的應用
135+
136+
在你的專案中還有這些動態路由:
137+
138+
```dart
139+
// 地圖詳細頁面 - 傳入座標
140+
GoRoute(
141+
path: '/map_detail/:latlng',
142+
builder: (context, state) {
143+
final latlng = state.pathParameters['latlng']!;
144+
return MapScreen(latlng: latlng);
145+
},
146+
),
147+
148+
// 互助旗頁面 - 傳入用戶 ID
149+
GoRoute(
150+
path: '/flag/:uid',
151+
builder: (context, state) {
152+
final uid = state.pathParameters['uid']!;
153+
return UserDetailScreen(uid: uid, showAsFlag: true);
154+
},
155+
),
156+
```
157+
158+
## 🎯 實際練習
159+
160+
試著理解這些 URL 會發生什麼:
161+
162+
1. `https://auto30next.alearn.org.tw/user/alice123`
163+
- 顯示用戶 alice123 的個人資料
164+
165+
2. `https://auto30next.alearn.org.tw/flag/bob456`
166+
- 顯示用戶 bob456 的互助旗
167+
168+
3. `https://auto30next.alearn.org.tw/map_detail/25.0330,121.5654`
169+
- 顯示座標 25.0330,121.5654 的地圖
170+
171+
## 🚀 總結
172+
173+
動態路由就像是:
174+
- 📋 一個模板:`/user/:uid`
175+
- 🔄 可以填入不同的值:`/user/任何用戶ID`
176+
- 🎯 每個 URL 都指向不同用戶的頁面
177+
- 📱 QR 碼包含完整的 URL,掃描後直接跳轉
178+
179+
這樣你就能讓每個用戶都有自己專屬的可分享連結了!🎉

QR_CODE_404_FIX.md

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# 🔧 QR 碼 404 問題解決方案
2+
3+
## 🚨 問題描述
4+
5+
用戶 `[email protected]` (UID: `b8643tA3lUfm4CEAd2pRKVyOe9v1`) 的 QR 碼連結:
6+
```
7+
https://auto30next.alearn.org.tw/user/b8643tA3lUfm4CEAd2pRKVyOe9v1
8+
```
9+
10+
當其他用戶掃描此 QR 碼時會出現 404 頁面。
11+
12+
## 🔍 問題原因
13+
14+
原本的路由設定有**全域認證檢查**
15+
16+
```dart
17+
// 舊的 redirect 邏輯
18+
if (!isAuthenticated && !isLoggingIn) {
19+
return '/login'; // 所有未登入用戶都被重導向到登入頁
20+
}
21+
```
22+
23+
這意味著:
24+
- 未登入用戶訪問 `/user/任何ID` 都會被強制重導向到 `/login`
25+
- 導致 QR 碼連結無法正常顯示用戶資料
26+
27+
## ✅ 解決方案
28+
29+
### 1. 修改路由權限控制
30+
31+
`lib/core/config/app_router.dart` 中,我們設定了**公開頁面**列表:
32+
33+
```dart
34+
// 🎯 允許未登入用戶訪問的公開頁面
35+
final publicPaths = [
36+
'/login',
37+
'/user/', // 用戶詳細頁面 ✅
38+
'/flag/', // 互助旗頁面 ✅
39+
];
40+
41+
// 檢查是否為公開路徑
42+
final isPublicPath = publicPaths.any((path) =>
43+
state.matchedLocation.startsWith(path));
44+
45+
// 只有非公開頁面才需要登入
46+
if (!isAuthenticated && !isLoggingIn && !isPublicPath) {
47+
return '/login';
48+
}
49+
```
50+
51+
### 2. 優化用戶體驗
52+
53+
`UserDetailScreen` 中添加了:
54+
55+
#### 🔔 未登入用戶提示
56+
```dart
57+
if (!isLoggedIn)
58+
Container(
59+
color: Colors.orange.shade100,
60+
child: Text('您正在以訪客身份瀏覽。登入後可使用更多功能!'),
61+
)
62+
```
63+
64+
#### 🔐 登入按鈕
65+
```dart
66+
// AppBar 中的登入按鈕
67+
if (!isLoggedIn)
68+
TextButton(
69+
onPressed: () => context.go('/login'),
70+
child: const Text('登入'),
71+
)
72+
```
73+
74+
#### 🏠 智慧導航
75+
```dart
76+
// 返回按鈕邏輯
77+
context.go(isLoggedIn ? '/' : '/login');
78+
```
79+
80+
## 🎯 現在的使用流程
81+
82+
### 情境 1: 已登入用戶掃描 QR 碼
83+
1. 掃描 QR 碼:`https://auto30next.alearn.org.tw/user/b8643tA3lUfm4CEAd2pRKVyOe9v1`
84+
2. 直接顯示用戶資料頁面 ✅
85+
3. 可以使用所有功能(地圖查看等)
86+
87+
### 情境 2: 未登入用戶掃描 QR 碼
88+
1. 掃描 QR 碼:`https://auto30next.alearn.org.tw/user/b8643tA3lUfm4CEAd2pRKVyOe9v1`
89+
2. 顯示用戶資料頁面 ✅
90+
3. 頂部顯示登入提示
91+
4. AppBar 有登入按鈕
92+
5. 可以瀏覽基本資料,但某些功能需要登入
93+
94+
## 🔒 安全性考量
95+
96+
### ✅ 保持的安全措施:
97+
- 其他敏感頁面(個人設定、編輯資料等)仍需要登入
98+
- 只有用戶資料的**瀏覽**功能對外開放
99+
- 用戶資料本身從 Firebase 讀取,有既有的權限控制
100+
101+
### 🔓 開放的功能:
102+
- 查看用戶基本資料
103+
- 查看用戶在地圖上的位置
104+
- 查看用戶的技能和興趣
105+
106+
## 🧪 測試方法
107+
108+
### 本地測試:
109+
1. 確保應用程式正在運行
110+
2. 在瀏覽器中訪問:
111+
```
112+
http://localhost:8000/user/b8643tA3lUfm4CEAd2pRKVyOe9v1
113+
```
114+
3. 應該能看到用戶資料,不會被重導向到登入頁
115+
116+
### 線上測試:
117+
1. 部署後訪問:
118+
```
119+
https://auto30next.alearn.org.tw/user/b8643tA3lUfm4CEAd2pRKVyOe9v1
120+
```
121+
2. 應該能正常顯示用戶資料
122+
123+
## 🚀 其他改進
124+
125+
### 🔗 QR 碼分享功能
126+
現在 QR 碼可以:
127+
- 分享給任何人(包括未註冊用戶)
128+
- 讓收到的人直接查看資料
129+
- 引導未登入用戶註冊使用
130+
131+
### 📱 社交媒體分享
132+
用戶資料頁面現在可以:
133+
- 透過連結分享到社交媒體
134+
- 作為個人名片使用
135+
- 增加應用程式的曝光度
136+
137+
## 🎉 總結
138+
139+
修改後的系統:
140+
- ✅ 解決了 QR 碼 404 問題
141+
- ✅ 保持了安全性
142+
- ✅ 提升了用戶體驗
143+
- ✅ 增加了分享功能的實用性
144+
145+
現在任何人掃描 QR 碼都能正常查看用戶資料了!🎯

lib/core/config/app_router.dart

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,20 @@ class AppRouter {
2020
redirect: (context, state) {
2121
final isAuthenticated = authProvider.isAuthenticated;
2222
final isLoggingIn = state.matchedLocation == '/login';
23+
24+
// 🎯 允許未登入用戶訪問的公開頁面
25+
final publicPaths = [
26+
'/login',
27+
'/user/', // 用戶詳細頁面
28+
'/flag/', // 互助旗頁面
29+
];
30+
31+
// 檢查是否為公開路徑
32+
final isPublicPath = publicPaths.any((path) =>
33+
state.matchedLocation.startsWith(path));
2334

24-
// 如果未登入且不在登入頁面,導向登入頁面
25-
if (!isAuthenticated && !isLoggingIn) {
35+
// 如果未登入且不在登入頁面且不是公開頁面,導向登入頁面
36+
if (!isAuthenticated && !isLoggingIn && !isPublicPath) {
2637
return '/login';
2738
}
2839

0 commit comments

Comments
 (0)