修复token过期不跳转登录页:统一401和业务0002拦截,全局导航跳转
This commit is contained in:
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"e4b8dca3f1b4ede4c30371002441c88c12187e
|
|||||||
|
|
||||||
_flutter.loader.load({
|
_flutter.loader.load({
|
||||||
serviceWorkerSettings: {
|
serviceWorkerSettings: {
|
||||||
serviceWorkerVersion: "1103688684" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
|
serviceWorkerVersion: "775073087" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,5 +1,6 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
import '../constants/api_endpoints.dart';
|
import '../constants/api_endpoints.dart';
|
||||||
import '../storage/local_storage.dart';
|
import '../storage/local_storage.dart';
|
||||||
import 'api_exception.dart';
|
import 'api_exception.dart';
|
||||||
@@ -17,10 +18,13 @@ class NetworkConfig {
|
|||||||
class DioClient {
|
class DioClient {
|
||||||
late final Dio _dio;
|
late final Dio _dio;
|
||||||
|
|
||||||
/// 未授權回調(token 過期時觸發)
|
/// 全局導航 key,用於未授權時跳轉登錄頁
|
||||||
VoidCallback? onUnauthorized;
|
GlobalKey<NavigatorState>? navigatorKey;
|
||||||
|
|
||||||
DioClient() {
|
/// 未授權回調(token 過期時觸發 AuthProvider.forceLogout)
|
||||||
|
VoidCallback? onForceLogout;
|
||||||
|
|
||||||
|
DioClient({this.navigatorKey}) {
|
||||||
_dio = _createDio();
|
_dio = _createDio();
|
||||||
_setupInterceptors();
|
_setupInterceptors();
|
||||||
debugPrint('DioClient initialized with baseUrl: ${NetworkConfig.baseUrl}');
|
debugPrint('DioClient initialized with baseUrl: ${NetworkConfig.baseUrl}');
|
||||||
@@ -97,12 +101,9 @@ class DioClient {
|
|||||||
if (data is Map<String, dynamic>) {
|
if (data is Map<String, dynamic>) {
|
||||||
final apiResponse = ApiResponse.fromJson(data, fromJson);
|
final apiResponse = ApiResponse.fromJson(data, fromJson);
|
||||||
// 檢測業務層未授權(後端返回 HTTP 200 + code "0002")
|
// 檢測業務層未授權(後端返回 HTTP 200 + code "0002")
|
||||||
// 注意:不再自動清除用戶數據,避免誤判
|
|
||||||
// 只有在 HTTP 401 時才清除用戶數據
|
|
||||||
if (apiResponse.isUnauthorized) {
|
if (apiResponse.isUnauthorized) {
|
||||||
debugPrint('業務層未授權響應: ${apiResponse.message}');
|
debugPrint('業務層未授權響應: ${apiResponse.message}');
|
||||||
// 不再自動調用 onUnauthorized,避免刷新時誤判
|
_handleUnauthorized();
|
||||||
// onUnauthorized?.call();
|
|
||||||
}
|
}
|
||||||
return apiResponse;
|
return apiResponse;
|
||||||
}
|
}
|
||||||
@@ -120,8 +121,7 @@ class DioClient {
|
|||||||
debugPrint('====================');
|
debugPrint('====================');
|
||||||
|
|
||||||
if (_isUnauthorized(e)) {
|
if (_isUnauthorized(e)) {
|
||||||
_clearUserData();
|
_handleUnauthorized();
|
||||||
onUnauthorized?.call();
|
|
||||||
return ApiResponse.unauthorized('登錄已過期,請重新登錄');
|
return ApiResponse.unauthorized('登錄已過期,請重新登錄');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,6 +137,19 @@ class DioClient {
|
|||||||
LocalStorage.clearUserData();
|
LocalStorage.clearUserData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 統一處理未授權:清除本地數據 → 通知 Provider → 全局跳轉登錄頁
|
||||||
|
void _handleUnauthorized() {
|
||||||
|
_clearUserData();
|
||||||
|
onForceLogout?.call();
|
||||||
|
final context = navigatorKey?.currentContext;
|
||||||
|
if (context != null) {
|
||||||
|
Navigator.of(context).pushNamedAndRemoveUntil(
|
||||||
|
'/login',
|
||||||
|
(route) => false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String _getErrorMessage(DioException e) {
|
String _getErrorMessage(DioException e) {
|
||||||
switch (e.type) {
|
switch (e.type) {
|
||||||
case DioExceptionType.connectionTimeout:
|
case DioExceptionType.connectionTimeout:
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ import 'ui/pages/auth/login_page.dart';
|
|||||||
import 'ui/pages/main/main_page.dart';
|
import 'ui/pages/main/main_page.dart';
|
||||||
import 'ui/pages/onboarding/onboarding_page.dart';
|
import 'ui/pages/onboarding/onboarding_page.dart';
|
||||||
|
|
||||||
|
/// 全局導航 Key,用於 token 過期時全局跳轉登錄頁
|
||||||
|
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
// 確保 Flutter 綁定初始化
|
// 確保 Flutter 綁定初始化
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
@@ -72,7 +75,7 @@ class MyApp extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<SingleChildWidget> _buildProviders() {
|
List<SingleChildWidget> _buildProviders() {
|
||||||
final dioClient = DioClient();
|
final dioClient = DioClient(navigatorKey: navigatorKey);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
// Theme Provider (必須放在最前面)
|
// Theme Provider (必須放在最前面)
|
||||||
@@ -93,7 +96,7 @@ class MyApp extends StatelessWidget {
|
|||||||
create: (ctx) {
|
create: (ctx) {
|
||||||
final authProvider = AuthProvider(ctx.read<UserService>());
|
final authProvider = AuthProvider(ctx.read<UserService>());
|
||||||
// token 過期時,DioClient 回調 AuthProvider 強制登出
|
// token 過期時,DioClient 回調 AuthProvider 強制登出
|
||||||
dioClient.onUnauthorized = authProvider.forceLogout;
|
dioClient.onForceLogout = authProvider.forceLogout;
|
||||||
return authProvider;
|
return authProvider;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -112,6 +115,7 @@ class MyApp extends StatelessWidget {
|
|||||||
|
|
||||||
Widget _buildMaterialApp(BuildContext context, ThemeMode themeMode) {
|
Widget _buildMaterialApp(BuildContext context, ThemeMode themeMode) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
|
navigatorKey: navigatorKey,
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
theme: AppTheme.lightTheme,
|
theme: AppTheme.lightTheme,
|
||||||
darkTheme: AppTheme.darkTheme,
|
darkTheme: AppTheme.darkTheme,
|
||||||
|
|||||||
Reference in New Issue
Block a user