diff --git a/airhub_app/lib/core/network/token_manager.dart b/airhub_app/lib/core/network/token_manager.dart index b3e654f..19e7e41 100644 --- a/airhub_app/lib/core/network/token_manager.dart +++ b/airhub_app/lib/core/network/token_manager.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -11,7 +12,7 @@ TokenManager tokenManager(Ref ref) { return TokenManager(); } -class TokenManager { +class TokenManager extends ChangeNotifier { SharedPreferences? _prefs; Future get _preferences async { @@ -26,6 +27,7 @@ class TokenManager { final prefs = await _preferences; await prefs.setString(_keyAccessToken, access); await prefs.setString(_keyRefreshToken, refresh); + notifyListeners(); } Future getAccessToken() async { @@ -47,5 +49,6 @@ class TokenManager { final prefs = await _preferences; await prefs.remove(_keyAccessToken); await prefs.remove(_keyRefreshToken); + notifyListeners(); } } diff --git a/airhub_app/lib/core/router/app_router.dart b/airhub_app/lib/core/router/app_router.dart index a2acb04..67faa4c 100644 --- a/airhub_app/lib/core/router/app_router.dart +++ b/airhub_app/lib/core/router/app_router.dart @@ -18,6 +18,7 @@ GoRouter goRouter(Ref ref) { return GoRouter( initialLocation: '/login', + refreshListenable: tokenManager, redirect: (context, state) async { final hasToken = await tokenManager.hasToken(); final isLoginRoute = state.matchedLocation == '/login'; diff --git a/airhub_app/lib/core/router/app_router.g.dart b/airhub_app/lib/core/router/app_router.g.dart index 631592a..c2f3d9a 100644 --- a/airhub_app/lib/core/router/app_router.g.dart +++ b/airhub_app/lib/core/router/app_router.g.dart @@ -48,4 +48,4 @@ final class GoRouterProvider } } -String _$goRouterHash() => r'b559a84bcf9ae1ffda1deba4cf213f31a4006782'; +String _$goRouterHash() => r'8e620e452bb81f2c6ed87b136283a9e508dca2e9'; diff --git a/airhub_app/lib/features/auth/presentation/controllers/auth_controller.dart b/airhub_app/lib/features/auth/presentation/controllers/auth_controller.dart index 618866b..b3a1b6e 100644 --- a/airhub_app/lib/features/auth/presentation/controllers/auth_controller.dart +++ b/airhub_app/lib/features/auth/presentation/controllers/auth_controller.dart @@ -14,6 +14,7 @@ class AuthController extends _$AuthController { state = const AsyncLoading(); final repository = ref.read(authRepositoryProvider); final result = await repository.sendCode(phone); + if (!ref.mounted) return; state = result.fold( (failure) => AsyncError(failure.message, StackTrace.current), (_) => const AsyncData(null), @@ -24,6 +25,7 @@ class AuthController extends _$AuthController { state = const AsyncLoading(); final repository = ref.read(authRepositoryProvider); final result = await repository.codeLogin(phone, code); + if (!ref.mounted) return false; return result.fold( (failure) { state = AsyncError(failure.message, StackTrace.current); @@ -40,6 +42,7 @@ class AuthController extends _$AuthController { state = const AsyncLoading(); final repository = ref.read(authRepositoryProvider); final result = await repository.tokenLogin(token); + if (!ref.mounted) return false; return result.fold( (failure) { state = AsyncError(failure.message, StackTrace.current); @@ -56,6 +59,7 @@ class AuthController extends _$AuthController { state = const AsyncLoading(); final repository = ref.read(authRepositoryProvider); final result = await repository.logout(); + if (!ref.mounted) return; state = result.fold( (failure) => AsyncError(failure.message, StackTrace.current), (_) => const AsyncData(null), @@ -66,6 +70,7 @@ class AuthController extends _$AuthController { state = const AsyncLoading(); final repository = ref.read(authRepositoryProvider); final result = await repository.deleteAccount(); + if (!ref.mounted) return false; return result.fold( (failure) { state = AsyncError(failure.message, StackTrace.current); diff --git a/airhub_app/lib/features/auth/presentation/controllers/auth_controller.g.dart b/airhub_app/lib/features/auth/presentation/controllers/auth_controller.g.dart index fba6b23..82a5a8a 100644 --- a/airhub_app/lib/features/auth/presentation/controllers/auth_controller.g.dart +++ b/airhub_app/lib/features/auth/presentation/controllers/auth_controller.g.dart @@ -33,7 +33,7 @@ final class AuthControllerProvider AuthController create() => AuthController(); } -String _$authControllerHash() => r'fac48518f4825055a266b5ea7e11163320342153'; +String _$authControllerHash() => r'3a290ddd5b4b091786d5020ecb57b7fb1d3a287a'; abstract class _$AuthController extends $AsyncNotifier { FutureOr build(); diff --git a/airhub_app/lib/pages/profile/profile_info_page.dart b/airhub_app/lib/pages/profile/profile_info_page.dart index 3741630..6facade 100644 --- a/airhub_app/lib/pages/profile/profile_info_page.dart +++ b/airhub_app/lib/pages/profile/profile_info_page.dart @@ -35,13 +35,16 @@ class _ProfileInfoPageState extends ConsumerState { super.dispose(); } + static const _genderToDisplay = {'male': '男', 'female': '女'}; + static const _displayToGender = {'男': 'male', '女': 'female'}; + void _initFromUser() { if (_initialized) return; final userAsync = ref.read(userControllerProvider); final user = userAsync.value; if (user != null) { _nicknameController.text = user.nickname ?? ''; - _gender = user.gender ?? ''; + _gender = _genderToDisplay[user.gender] ?? user.gender ?? ''; _birthday = user.birthday ?? ''; _avatarUrl = user.avatar; _initialized = true; @@ -153,12 +156,7 @@ class _ProfileInfoPageState extends ConsumerState { Future _saveProfile() async { // 转换性别为后端格式 - String? genderCode; - if (_gender == '男') { - genderCode = 'M'; - } else if (_gender == '女') { - genderCode = 'F'; - } + final genderCode = _displayToGender[_gender]; final success = await ref.read(userControllerProvider.notifier).updateProfile( nickname: _nicknameController.text.trim(), diff --git a/airhub_app/test/widget_test.dart b/airhub_app/test/widget_test.dart index 746d912..bd743ae 100644 --- a/airhub_app/test/widget_test.dart +++ b/airhub_app/test/widget_test.dart @@ -1,30 +1,13 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:airhub_app/main.dart'; void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); + testWidgets('App smoke test', (WidgetTester tester) async { + await tester.pumpWidget( + const ProviderScope(child: AirhubApp()), + ); + await tester.pumpAndSettle(); }); }