学习内容:
安装 SharedPreferences 依赖
本地存储操作:存数据(
setString/setInt)、取数据(getString)、删数据(remove)实践任务:
优化登录页:登录成功后,本地存储 “用户名”
App 启动时,读取本地存储:若有用户名则直接进入首页,否则进入登录页
一、SharedPreferences简介与环境准备※
1. 什么是SharedPreferences※
SharedPreferences是一个简单的、异步的、键值对(Key-Value)存储方案。它适用于存储少量的、简单的数据类型,例如:
用户偏好设置(如主题、语言)。
登录令牌(Token)或用户名。
应用状态(如:是否首次启动)。
注意:它不适合存储大量数据或敏感信息(如:密码)。对于密码等敏感数据,应使用更安全的存储方案,如:flutter_secure_storage。
数据存储位置:
Android:SharedPreferences会存储在应用的私有SharedPreferences目录下的XML文件中。
iOS:会存储在应用的NSUserDefaults中。
2. 安装SharedPreferences依赖※
在pubspec.yaml文件中添加shared_preferences依赖:
dependencies:
flutter:
sdk: flutter
# 添加 shared_preferences 依赖
shared_preferences: ^2.5.3 # 请使用 pub.dev 上的最新版本在终端执行命令安装依赖:
flutter pub get在需要使用的文件中导入:
import 'package:shared_preferences/shared_preferences.dart';二、SharedPreferences基础操作※
SharedPreferences的所有操作都是异步的,因此需要使用async/await。
1. 获取SharedPreferences实例※
在进行任何操作之前,你需要先获取一个SharedPreferences的实例。通常,我们会用一个静态方法getInstance()来获取。
final prefs = await SharedPreferences.getInstance();为了方便,可以创建一个工具类来封装这些操作。
推荐实践:创建一个存储工具类
创建一个新文件:lib/services/storage_service.dart
import 'package:shared_preferences/shared_preferences.dart';
class StorageService {
// 私有构造函数
StorageService._();
//单例模式
static final StorageService instance = StorageService._();
// SharedPreferences实例
late final SharedPreferences _prefs;
// 初始化,在应用启动时调用
Future<void> init() async {
_prefs = await SharedPreferences.getInstance();
}
// --- 通用方法 ---
// 存储数据
Future<bool> setValue(String key, dynamic value) async {
if (value is String) {
return await _prefs.setString(key, value);
} else if (value is int) {
return await _prefs.setInt(key, value);
} else if (value is bool) {
return await _prefs.setBool(key, value);
} else if (value is double) {
return await _prefs.setDouble(key, value);
}
throw UnsupportedError('不支持的类型');
}
// 获取数据
T? getValue<T>(String key) {
try {
if (T == String) {
return _prefs.getString(key) as T?;
} else if (T == int) {
return _prefs.getInt(key) as T?;
} else if (T == bool) {
return _prefs.getBool(key) as T?;
} else if (T == double) {
return _prefs.getDouble(key) as T?;
}
} catch (e) {
return null;
}
throw UnsupportedError('不支持的类型');
}
// 删除数据
Future<bool> remove(String key) async {
return await _prefs.remove(key);
}
// 清空所有数据
Future<bool> clear() async {
return await _prefs.clear();
}
// --- 业务相关方法 ---
}使用工具类的好处:
- 集中管理:所有存储操作都在一个地方,便于维护和修改。
- 代码简洁:在业务逻辑中调用时更加清晰。
- 单例模式:确保整个应用中使用的是同一个SharedPreferences实例。
2. 在main.dart中初始化※
为了确保StoreService在应用的其他部分使用之间已经初始化,最好在main函数中进行初始化。
import 'services/storage_service.dart';
void main() async {
// 确保Flutter绑定已初始化
WidgetsFlutterBinding.ensureInitialized();
// 初始化存储服务
await StorageService.instance.init();
runApp(const App());
}三、综合实战※
1. 任务需求※
- 登录成功后:在SharedPreferences中存储用户名。
- 应用启动时:LoginPage在构建时检查SharedPreferences中是否存在用户名。
- 如果存在:自动跳转到HomePage。
- 如果不存在:显示正常的登录界面。
- 退出登录时:从SharedPreferences 中删除用户名,并跳转回LoginPage。
2. 代码实现※
首页(home_page.dart)
// /pages/home_page.dart import 'package:flutter/material.dart'; import 'login_page.dart'; import '../services/storage_services.dart'; class HomePage extends StatefulWidget { final String username; const HomePage({super.key, required this.username}); @override State<HomePage> createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { Future<void> _handleLogout() async { // 退出登录时,删除本地存储的用户名 await StorageService.instance.removeUsername(); // 跳转到登录页 if (mounted) { Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => const LoginPage())); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('首页'), actions: [ IconButton( icon: const Icon(Icons.logout, color: Colors.black,), onPressed: _handleLogout, tooltip: '退出登录', ) ], ), body: Center( child: Text( '欢迎回来,${widget.username}!', style: Theme.of(context).textTheme.headlineMedium, textAlign: TextAlign.center, ), ), ); } }登录页(login_page.dart)
// /pages/login_page.dart import 'package:flutter/material.dart'; import 'home_page.dart'; import '../services/storage_services.dart'; class LoginPage extends StatefulWidget { const LoginPage({super.key}); @override State<LoginPage> createState() => _LoginPageState(); } class _LoginPageState extends State<LoginPage> { final TextEditingController _usernameController = TextEditingController(); final TextEditingController _passwordCOntroller = TextEditingController(); final _formKey = GlobalKey<FormState>(); // 标记是否正在检查登录状态 bool _isCheckingLoginStats = true; @override void initState() { super.initState(); // 页面初始化时,检查是否已登录 _checkAutoLogin(); } // 检查是否可以自动登录 Future<void> _checkAutoLogin() async { // 从本地存储获取用户名 String? username = StorageService.instance.getUsername(); if (username != null && username.isNotEmpty) { // 如果存在用户名,则自动跳转到首页 if (mounted) { // 确保Widget还在树中 // 延迟到当前帧构建完成后再执行路由跳转 WidgetsBinding.instance.addPostFrameCallback((_) { Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => HomePage(username: username))); }); } } else { // 如果不存在,则显示登录界面 setState(() { _isCheckingLoginStats = false; }); } } Future<void> _handleLogin() async { if (_formKey.currentState!.validate()) { String username = _usernameController.text; String password = _passwordCOntroller.text; // 这里可以添加真实的登录验证逻辑 if (password.isNotEmpty) { // 登录成功,存储用户名 await StorageService.instance.saveUsername(username); // 跳转到首页 if (mounted) { Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => HomePage(username: username)), ); } } else { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('密码不能为空')) ); } } } } @override Widget build(BuildContext context) { // 在检查登录状态时,可以显示一个加载指示器 if (_isCheckingLoginStats) { return const Scaffold( body: Center(child: CircularProgressIndicator(),), ); } return Scaffold( appBar: AppBar(title: const Text('登录'),), body: Padding( padding: const EdgeInsets.all(16.0), child: Form( key: _formKey, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ TextFormField( controller: _usernameController, decoration: const InputDecoration( labelText: '用户名', hintText: '请输入用户名', ), validator: (value) { if (value == null || value.isEmpty) { return '请输入用户名'; } return null; }, ), const SizedBox(height: 16,), TextFormField( controller: _passwordCOntroller, obscureText: true, decoration: const InputDecoration( labelText: '密码', hintText: '请输入密码' ), validator: (value) { if (value == null || value.isEmpty) { return '请输入密码'; } return null; }, ), const SizedBox(height: 32), ElevatedButton( onPressed: _handleLogin, child: const Text('登录'), ) ], ), ), ), ); } }存储工具类(storage_services.dart)
// /services/storage_services.dart import 'package:shared_preferences/shared_preferences.dart'; class StorageService { // 私有构造函数 StorageService._(); //单例模式 static final StorageService instance = StorageService._(); // SharedPreferences实例 late final SharedPreferences _prefs; // 初始化,在应用启动时调用 Future<void> init() async { _prefs = await SharedPreferences.getInstance(); } // --- 通用方法 --- // 存储数据 Future<bool> setValue(String key, dynamic value) async { if (value is String) { return await _prefs.setString(key, value); } else if (value is int) { return await _prefs.setInt(key, value); } else if (value is bool) { return await _prefs.setBool(key, value); } else if (value is double) { return await _prefs.setDouble(key, value); } throw UnsupportedError('不支持的类型'); } // 获取数据 T? getValue<T>(String key) { try { if (T == String) { return _prefs.getString(key) as T?; } else if (T == int) { return _prefs.getInt(key) as T?; } else if (T == bool) { return _prefs.getBool(key) as T?; } else if (T == double) { return _prefs.getDouble(key) as T?; } } catch (e) { return null; } throw UnsupportedError('不支持的类型'); } // 删除数据 Future<bool> remove(String key) async { return await _prefs.remove(key); } // 清空所有数据 Future<bool> clear() async { return await _prefs.clear(); } // --- 业务相关方法 --- // 存储用户名 Future<bool> saveUsername(String username) async { return await setValue('username', username); } // 获取用户名 String? getUsername() { return getValue<String>('username'); } // 删除用户名 Future<bool> removeUsername() async { return await remove('username'); } }主文件(main.dart)
import 'package:flutter/material.dart'; import 'services/storage_services.dart'; import 'pages/login_page.dart'; void main() async { // 确保Flutter绑定已初始化 WidgetsFlutterBinding.ensureInitialized(); // 初始化存储服务 await StorageService.instance.init(); runApp(const App()); } class App extends StatelessWidget{ const App({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'SharedPreferences Demo', theme: ThemeData(primarySwatch: Colors.yellow), home: const LoginPage(), // 初始页面设为登录页,它判断是否需要自动登录。 ); } }- 说明
- mounted是State类的属性,只有在 StatefulWidget 的 State 子类中才能直接使用。
- 在 StatelessWidget 中,用 ModalRoute.of(context)?.isCurrent ?? false 替代 mounted,判断当前页面是否还在栈顶(是否有效)。
3. 运行效果※

说明:登录后,退出程序,下次再打开,依旧会保持登录状态。