- flutter create 初始化 + 依赖(riverpod 3/dio/go_router/three_dart 0.0.16) - ApiClient:JWT 拦截器 + 统一响应解包 + 401 登出回调 + multipart 上传 - TokenStorage(shared_preferences)+ AuthNotifier 登录/登出 - 登录页(含注册对话框)+ 4 Tab 主框架 - 6 个单元测试通过(网络层 4 + 认证 2) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
65 lines
1.8 KiB
Dart
65 lines
1.8 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import '../network/api_client.dart';
|
|
import '../storage/token_storage.dart';
|
|
|
|
class AuthState {
|
|
final bool authenticated;
|
|
final int? userId;
|
|
final String? name;
|
|
|
|
const AuthState({
|
|
this.authenticated = false,
|
|
this.userId,
|
|
this.name,
|
|
});
|
|
}
|
|
|
|
final tokenStorageProvider = Provider<TokenStorage>((ref) => TokenStorage());
|
|
|
|
final apiClientProvider = Provider<ApiClient>((ref) {
|
|
final client = ApiClient(tokenStorage: ref.watch(tokenStorageProvider));
|
|
client.onUnauthorized = () {
|
|
ref.read(authProvider.notifier).logout();
|
|
};
|
|
return client;
|
|
});
|
|
|
|
class AuthNotifier extends AsyncNotifier<AuthState> {
|
|
@override
|
|
Future<AuthState> build() async {
|
|
final storage = ref.watch(tokenStorageProvider);
|
|
await storage.load();
|
|
return AuthState(authenticated: storage.hasToken);
|
|
}
|
|
|
|
Future<void> login(String account, String password) async {
|
|
state = const AsyncLoading();
|
|
try {
|
|
final client = ref.read(apiClientProvider);
|
|
final data = await client.post<Map<String, dynamic>>('/user/login', {
|
|
'account': account,
|
|
'password': password,
|
|
});
|
|
final token = data['token'] as String? ?? '';
|
|
final user = data['user'] as Map<String, dynamic>? ?? {};
|
|
await ref.read(tokenStorageProvider).save(token);
|
|
state = AsyncData(AuthState(
|
|
authenticated: true,
|
|
userId: (user['id'] as num?)?.toInt(),
|
|
name: user['name'] as String?,
|
|
));
|
|
} catch (e) {
|
|
state = AsyncError(e, StackTrace.current);
|
|
}
|
|
}
|
|
|
|
Future<void> logout() async {
|
|
await ref.read(tokenStorageProvider).clear();
|
|
state = const AsyncData(AuthState());
|
|
}
|
|
}
|
|
|
|
final authProvider =
|
|
AsyncNotifierProvider<AuthNotifier, AuthState>(AuthNotifier.new);
|