Skip to content

第10章:Flutter 本地存储

10.1 本地存储的应用场景

什么是本地存储?

本地存储是指将数据保存在设备的本地存储空间中,即使应用重启或设备重启后,数据仍然存在。

常见应用场景

  • 保存用户信息:如用户名、登录状态、用户偏好设置
  • 保存配置信息:如应用主题、语言设置、界面布局偏好
  • 保存离线数据:如下载的内容、缓存的图片、离线阅读的文章
  • 保存应用状态:如游戏进度、表单填写进度、浏览历史
  • 保存临时数据:如搜索历史、最近使用的功能

10.2 常用本地存储方案

shared_preferences

shared_preferences 是 Flutter 中最常用的轻量级本地存储方案,适合保存简单的键值对数据。

安装

yaml
dependencies:
  shared_preferences: ^2.2.3

基本使用

1. 数据存储

dart
import 'package:shared_preferences/shared_preferences.dart';

Future<void> saveData() async {
  // 获取 SharedPreferences 实例
  final prefs = await SharedPreferences.getInstance();
  
  // 存储字符串
  await prefs.setString('username', 'john_doe');
  
  // 存储布尔值
  await prefs.setBool('isLoggedIn', true);
  
  // 存储整数
  await prefs.setInt('age', 25);
  
  // 存储浮点数
  await prefs.setDouble('score', 89.5);
  
  // 存储字符串列表
  await prefs.setStringList('hobbies', ['reading', 'coding', 'gaming']);
}

2. 数据读取

dart
Future<void> readData() async {
  final prefs = await SharedPreferences.getInstance();
  
  // 读取字符串
  final username = prefs.getString('username');
  print('Username: $username');
  
  // 读取布尔值
  final isLoggedIn = prefs.getBool('isLoggedIn');
  print('Is logged in: $isLoggedIn');
  
  // 读取整数
  final age = prefs.getInt('age');
  print('Age: $age');
  
  // 读取浮点数
  final score = prefs.getDouble('score');
  print('Score: $score');
  
  // 读取字符串列表
  final hobbies = prefs.getStringList('hobbies');
  print('Hobbies: $hobbies');
}

3. 数据删除

dart
Future<void> removeData() async {
  final prefs = await SharedPreferences.getInstance();
  
  // 删除单个键值对
  await prefs.remove('username');
  
  // 清空所有数据
  await prefs.clear();
  
  // 检查键是否存在
  final exists = prefs.containsKey('username');
  print('Username exists: $exists');
}

sqflite

sqflite 是 Flutter 中的本地数据库解决方案,适合保存大量结构化数据。

安装

yaml
dependencies:
  sqflite: ^2.3.3+1
  path: ^1.9.0

基本使用

1. 数据库创建

dart
import 'dart:async';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';

class DatabaseHelper {
  static Database? _database;

  Future<Database> get database async {
    if (_database != null) return _database!;
    
    // 初始化数据库
    _database = await _initDatabase();
    return _database!;
  }

  Future<Database> _initDatabase() async {
    // 数据库文件路径
    final dbPath = await getDatabasesPath();
    final path = join(dbPath, 'example.db');
    
    // 打开或创建数据库
    return await openDatabase(
      path,
      version: 1,
      onCreate: (db, version) {
        // 创建表
        return db.execute(
          '''
          CREATE TABLE users(
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            email TEXT NOT NULL UNIQUE,
            age INTEGER
          )
          ''',
        );
      },
    );
  }
}

2. 增删改查操作

dart
// 插入数据
Future<void> insertUser(User user) async {
  final db = await DatabaseHelper().database;
  await db.insert(
    'users',
    user.toMap(),
    conflictAlgorithm: ConflictAlgorithm.replace,
  );
}

// 查询所有用户
Future<List<User>> getUsers() async {
  final db = await DatabaseHelper().database;
  final List<Map<String, dynamic>> maps = await db.query('users');
  return List.generate(maps.length, (i) {
    return User(
      id: maps[i]['id'],
      name: maps[i]['name'],
      email: maps[i]['email'],
      age: maps[i]['age'],
    );
  });
}

// 更新用户
Future<void> updateUser(User user) async {
  final db = await DatabaseHelper().database;
  await db.update(
    'users',
    user.toMap(),
    where: 'id = ?',
    whereArgs: [user.id],
  );
}

// 删除用户
Future<void> deleteUser(int id) async {
  final db = await DatabaseHelper().database;
  await db.delete(
    'users',
    where: 'id = ?',
    whereArgs: [id],
  );
}

// User 模型
class User {
  final int? id;
  final String name;
  final String email;
  final int? age;

  User({this.id, required this.name, required this.email, this.age});

  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'name': name,
      'email': email,
      'age': age,
    };
  }

  factory User.fromMap(Map<String, dynamic> map) {
    return User(
      id: map['id'],
      name: map['name'],
      email: map['email'],
      age: map['age'],
    );
  }
}

10.3 本地存储与状态管理结合

保存状态到本地存储

结合 Providershared_preferences 实现状态持久化:

dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';

class UserModel extends ChangeNotifier {
  String _username = '';
  bool _isLoggedIn = false;

  String get username => _username;
  bool get isLoggedIn => _isLoggedIn;

  // 初始化:从本地存储加载数据
  Future<void> init() async {
    final prefs = await SharedPreferences.getInstance();
    _username = prefs.getString('username') ?? '';
    _isLoggedIn = prefs.getBool('isLoggedIn') ?? false;
    notifyListeners();
  }

  // 登录
  Future<void> login(String username, String password) async {
    // 模拟登录验证
    if (username.isNotEmpty && password.isNotEmpty) {
      _username = username;
      _isLoggedIn = true;
      
      // 保存到本地存储
      final prefs = await SharedPreferences.getInstance();
      await prefs.setString('username', username);
      await prefs.setBool('isLoggedIn', true);
      
      notifyListeners();
    }
  }

  // 登出
  Future<void> logout() async {
    _username = '';
    _isLoggedIn = false;
    
    // 从本地存储删除
    final prefs = await SharedPreferences.getInstance();
    await prefs.remove('username');
    await prefs.setBool('isLoggedIn', false);
    
    notifyListeners();
  }
}

使用持久化状态

dart
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => UserModel()..init(),
      child: const MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Local Storage Demo',
      home: Consumer<UserModel>(
        builder: (context, userModel, child) {
          return userModel.isLoggedIn
              ? const HomePage()
              : const LoginPage();
        },
      ),
    );
  }
}

10.4 实操案例:用shared_preferences保存用户登录状态

目标

创建一个登录页面,使用 shared_preferences 保存用户登录状态,实现重启应用后保持登录状态。

步骤 1:创建登录页面

dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'user_model.dart';

class LoginPage extends StatefulWidget {
  const LoginPage({super.key});

  @override
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  final _formKey = GlobalKey<FormState>();
  final _usernameController = TextEditingController();
  final _passwordController = TextEditingController();
  bool _isLoading = false;
  String? _error;

  @override
  void dispose() {
    _usernameController.dispose();
    _passwordController.dispose();
    super.dispose();
  }

  Future<void> _login() async {
    if (!_formKey.currentState!.validate()) return;

    setState(() {
      _isLoading = true;
      _error = null;
    });

    try {
      final userModel = Provider.of<UserModel>(context, listen: false);
      await userModel.login(
        _usernameController.text,
        _passwordController.text,
      );
    } catch (e) {
      setState(() {
        _error = 'Login failed: $e';
      });
    } finally {
      setState(() {
        _isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Login')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Form(
          key: _formKey,
          child: Column(
            children: [
              if (_error != null)
                Text(_error!, style: const TextStyle(color: Colors.red)),
              TextFormField(
                controller: _usernameController,
                decoration: const InputDecoration(labelText: 'Username'),
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return 'Please enter username';
                  }
                  return null;
                },
              ),
              TextFormField(
                controller: _passwordController,
                decoration: const InputDecoration(labelText: 'Password'),
                obscureText: true,
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return 'Please enter password';
                  }
                  return null;
                },
              ),
              const SizedBox(height: 20),
              _isLoading
                  ? const CircularProgressIndicator()
                  : ElevatedButton(
                      onPressed: _login,
                      child: const Text('Login'),
                    ),
            ],
          ),
        ),
      ),
    );
  }
}

步骤 2:创建首页

dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'user_model.dart';

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    final userModel = Provider.of<UserModel>(context);

    return Scaffold(
      appBar: AppBar(title: const Text('Home')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Welcome, ${userModel.username}!'),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () async {
                await userModel.logout();
              },
              child: const Text('Logout'),
            ),
          ],
        ),
      ),
    );
  }
}

步骤 3:创建用户模型

dart
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

class UserModel extends ChangeNotifier {
  String _username = '';
  bool _isLoggedIn = false;

  String get username => _username;
  bool get isLoggedIn => _isLoggedIn;

  Future<void> init() async {
    final prefs = await SharedPreferences.getInstance();
    _username = prefs.getString('username') ?? '';
    _isLoggedIn = prefs.getBool('isLoggedIn') ?? false;
    notifyListeners();
  }

  Future<void> login(String username, String password) async {
    // 模拟登录验证
    if (username.isNotEmpty && password.isNotEmpty) {
      _username = username;
      _isLoggedIn = true;
      
      final prefs = await SharedPreferences.getInstance();
      await prefs.setString('username', username);
      await prefs.setBool('isLoggedIn', true);
      
      notifyListeners();
    }
  }

  Future<void> logout() async {
    _username = '';
    _isLoggedIn = false;
    
    final prefs = await SharedPreferences.getInstance();
    await prefs.remove('username');
    await prefs.setBool('isLoggedIn', false);
    
    notifyListeners();
  }
}

步骤 4:运行应用

  1. 启动模拟器或连接真机
  2. 运行项目
  3. 输入用户名和密码登录
  4. 重启应用,验证登录状态是否保持
  5. 点击登出按钮,验证登出功能

10.5 本地存储最佳实践

1. 选择合适的存储方案

  • 轻量级数据:使用 shared_preferences(如用户偏好设置、登录状态)
  • 大量结构化数据:使用 sqflite(如用户列表、详细数据)
  • 大文件:使用 path_provider 结合文件操作(如图片、视频)

2. 数据安全

  • 敏感数据:考虑使用 flutter_secure_storage 存储敏感信息
  • 加密:对敏感数据进行加密后再存储
  • 权限:确保应用有适当的存储权限

3. 性能优化

  • 批量操作:减少频繁的存储操作
  • 异步操作:所有存储操作都应该在异步中进行
  • 缓存:合理使用内存缓存,减少存储读写

4. 错误处理

  • 异常捕获:捕获存储操作中的异常
  • 降级策略:当存储失败时,提供合理的降级方案
  • 数据迁移:处理应用版本更新时的数据迁移

10.6 小结

本章介绍了 Flutter 的本地存储方案,包括 shared_preferencessqflite,以及如何与状态管理结合实现状态持久化。本地存储是 Flutter 应用中不可或缺的一部分,它可以帮助我们保存用户数据、配置信息和应用状态,提升用户体验。

我们学习了:

  • shared_preferences:轻量级键值对存储,适合保存简单数据
  • sqflite:本地数据库,适合保存大量结构化数据
  • 本地存储与状态管理结合:实现状态持久化
  • 实操案例:使用 shared_preferences 保存用户登录状态

通过实操案例,我们实现了一个登录页面,使用 shared_preferences 保存用户登录状态,实现了重启应用后保持登录状态的功能。在实际开发中,你应该根据数据的类型和大小选择合适的存储方案,并遵循最佳实践。

在接下来的章节中,我们将学习 Flutter 的样式与主题,掌握如何提升应用的视觉效果和用户体验。

© 2026 编程马·菜鸟教程 版权所有