Flutter中使用SharedPreferences存储Token/Cookie处理登录态及API请求的相关问题
Hey there! Let's break this down step by step since you're new to Flutter—no worries, it's simpler than it sounds 😊
1. 登录请求中获取Token
首先,你需要发送登录请求(带上用户名和密码),然后从API返回的响应里提取token。这里给你两种常见的实现方式,用原生http包或者更易用的dio包:
用http包的示例:
import 'package:http/http.dart' as http; import 'dart:convert'; Future<void> login(String username, String password) async { final response = await http.post( Uri.parse('https://your-api-url.com/login'), body: { 'username': username, 'password': password, }, ); if (response.statusCode == 200) { // 解析响应JSON,取出token(字段名根据你的API返回调整,比如`token`或`access_token`) final Map<String, dynamic> data = jsonDecode(response.body); final String token = data['token']; // 拿到token后,下一步就是存储它,咱们接着看 await saveToken(token); } else { // 处理登录失败的情况,比如提示用户用户名密码错误 throw Exception('登录失败,请检查账号密码'); } }
用dio包的示例(更推荐,拦截器等功能更强大):
先在pubspec.yaml添加依赖:
dependencies: dio: ^5.4.0+1
然后写登录逻辑:
import 'package:dio/dio.dart'; Future<void> login(String username, String password) async { final dio = Dio(); try { final response = await dio.post( 'https://your-api-url.com/login', data: { 'username': username, 'password': password, }, ); // 提取token final String token = response.data['token']; await saveToken(token); } on DioException catch (e) { // 处理请求错误 throw Exception('登录失败:${e.response?.data['message'] ?? '未知错误'}'); } }
2. 用SharedPreferences存储Token
接下来咱们把拿到的token存在SharedPreferences里,这样就算退出APP再打开,token还在。
首先添加shared_preferences依赖到pubspec.yaml:
dependencies: shared_preferences: ^2.2.2
然后封装一个存储和读取token的方法:
import 'package:shared_preferences/shared_preferences.dart'; // 存储token Future<void> saveToken(String token) async { final prefs = await SharedPreferences.getInstance(); await prefs.setString('auth_token', token); } // 读取token Future<String?> getToken() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString('auth_token'); } // 清除token(比如用户退出登录时) Future<void> clearToken() async { final prefs = await SharedPreferences.getInstance(); await prefs.remove('auth_token'); }
登录成功后调用saveToken(token)就完成存储啦!
3. 后续API请求自动携带Token
现在每次发API请求时,咱们需要把token放到请求头里(一般是Authorization: Bearer {token}的格式,具体要看你的API要求)。
用http包的请求示例:
Future<Map<String, dynamic>> fetchUserData() async { final token = await getToken(); if (token == null) { // 没有token,说明用户未登录,跳转到登录页 // 这里可以用Navigator.pushReplacement跳转 throw Exception('用户未登录'); } final response = await http.get( Uri.parse('https://your-api-url.com/user'), headers: { 'Authorization': 'Bearer $token', // 按API要求的格式设置 'Content-Type': 'application/json', }, ); if (response.statusCode == 200) { return jsonDecode(response.body); } else if (response.statusCode == 401) { // token过期或无效,清除存储的token并跳回登录页 await clearToken(); // Navigator.pushReplacement(...) throw Exception('登录已过期,请重新登录'); } else { throw Exception('请求失败'); } }
用dio的拦截器自动加token(更高效,不用每次手动写):
可以创建一个全局的Dio实例,添加拦截器自动在请求头里加token:
import 'package:dio/dio.dart'; Dio createDioInstance() { final dio = Dio(); // 添加请求拦截器 dio.interceptors.add(InterceptorsWrapper( onRequest: (options, handler) async { final token = await getToken(); if (token != null) { options.headers['Authorization'] = 'Bearer $token'; } return handler.next(options); }, onResponse: (response, handler) { return handler.next(response); }, onError: (DioException e, handler) { if (e.response?.statusCode == 401) { // token过期,清除token并跳转登录页 clearToken(); // Navigator.pushReplacement(...) } return handler.next(e); }, )); return dio; } // 之后用这个实例发请求就自动带token了 final dio = createDioInstance(); Future<Map<String, dynamic>> fetchUserData() async { try { final response = await dio.get('https://your-api-url.com/user'); return response.data; } on DioException catch (e) { throw Exception('请求失败:${e.message}'); } }
一些实用小建议
- 把这些token相关的方法封装成一个
AuthService类,代码更整洁,方便全局调用。 - 记得处理token过期的情况(比如API返回401状态码),这时候要清除本地token并引导用户重新登录。
- 如果有更高的安全需求,可以考虑用
flutter_secure_storage来加密存储token,比SharedPreferences更安全。
备注:内容来源于stack exchange,提问作者Vedo




