Flutter SQLite 本地数据库的鸿蒙化适配与实战指南

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net


各位小伙伴们好呀!👋 我是那个上海某高校的大一计算机学生,继续来给大家分享 Flutter for OpenHarmony 开发的学习心得!

今天要聊的是 SQLite 本地数据库!之前我们学了 SharedPreferences,它可以存储简单的键值对数据。但如果要存储大量有结构的数据,比如商品列表、聊天记录、订单信息… 那就得靠数据库了!

说实话,数据库听起来很高大上,但我自己实现了一套之后发现,也没有想象中那么难嘛~ 今天就给大家详细分享一下!


一、功能引入介绍 📱

1.1 什么时候需要数据库?

SharedPreferences 只适合存储小数据:

  • ✅ 用户设置
  • ✅ 搜索历史
  • ✅ 登录状态

但如果是这些场景,就需要数据库了:

  • 📦 商品列表(几百上千条)
  • 💬 聊天记录(需要查询、分页)
  • 📋 订单列表(复杂查询条件)
  • 📁 文件元数据

1.2 SQLite vs 其他方案

方案 适用场景 优点 缺点
SQLite 结构化数据、复杂查询 SQL 强大、跨平台 ✅ 需要写 SQL
Hive 键值对、对象存储 速度快、无 SQL 不适合复杂查询
Firebase 云端同步 免服务端 国内访问不稳定

我们选择 sqflite,它是 Flutter 官方支持的 SQLite 插件!


二、环境与依赖配置 🔧

2.1 pubspec.yaml 依赖

dependencies:
  flutter:
    sdk: flutter
  
  # ========== 数据库 ==========
  sqflite: ^2.4.1
  
  # ========== 路径工具 ==========
  path: ^1.9.0

三、分步实现完整代码 🚀

3.1 DatabaseService 数据库服务

这是一个完整的数据库服务封装:

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

/// 数据库服务
/// 
/// 使用 SQLite 实现本地数据持久化
/// 支持商品、收藏夹、浏览历史、购物车、聊天记录等数据管理
class DatabaseService {
  // 单例模式
  static Database? _database;
  
  // 数据库名称
  static const String _dbName = 'my_ohos_app.db';
  
  // 数据库版本(用于升级)
  static const int _dbVersion = 1;

  /// 获取数据库实例(单例)
  static Future<Database> get database async {
    if (_database != null) return _database!;
    _database = await _initDatabase();
    return _database!;
  }

  /// 初始化数据库
  static Future<Database> _initDatabase() async {
    // 获取数据库路径
    final dbPath = await getDatabasesPath();
    final path = join(dbPath, _dbName);

    return await openDatabase(
      path,
      version: _dbVersion,
      onCreate: _onCreate,      // 首次创建时调用
      onUpgrade: _onUpgrade,  // 版本升级时调用
    );
  }

  /// 创建数据库表
  static Future<void> _onCreate(Database db, int version) async {
    // ============ 商品表 ============
    await db.execute('''
      CREATE TABLE products (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        product_id TEXT UNIQUE NOT NULL,
        name TEXT NOT NULL,
        description TEXT,
        price REAL NOT NULL,
        original_price REAL,
        image_url TEXT,
        category TEXT,
        stock INTEGER DEFAULT 0,
        sales INTEGER DEFAULT 0,
        rating REAL DEFAULT 0.0,
        is_favorite INTEGER DEFAULT 0,
        created_at TEXT DEFAULT CURRENT_TIMESTAMP,
        updated_at TEXT DEFAULT CURRENT_TIMESTAMP
      )
    ''');

    // ============ 收藏表 ============
    await db.execute('''
      CREATE TABLE favorites (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        product_id TEXT NOT NULL,
        user_id TEXT,
        created_at TEXT DEFAULT CURRENT_TIMESTAMP,
        UNIQUE(product_id, user_id)
      )
    ''');

    // ============ 浏览历史表 ============
    await db.execute('''
      CREATE TABLE browse_history (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        product_id TEXT NOT NULL,
        product_name TEXT NOT NULL,
        product_image TEXT,
        product_price REAL,
        viewed_at TEXT DEFAULT CURRENT_TIMESTAMP
      )
    ''');

    // ============ 购物车表 ============
    await db.execute('''
      CREATE TABLE cart_items (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        product_id TEXT NOT NULL,
        product_name TEXT NOT NULL,
        product_image TEXT,
        price REAL NOT NULL,
        quantity INTEGER DEFAULT 1,
        selected INTEGER DEFAULT 1,
        created_at TEXT DEFAULT CURRENT_TIMESTAMP
      )
    ''');

    // ============ 聊天消息表 ============
    await db.execute('''
      CREATE TABLE chat_messages (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        conversation_id TEXT NOT NULL,
        sender_id TEXT NOT NULL,
        sender_name TEXT NOT NULL,
        content TEXT NOT NULL,
        message_type TEXT DEFAULT 'text',
        is_read INTEGER DEFAULT 0,
        created_at TEXT DEFAULT CURRENT_TIMESTAMP
      )
    ''');

    // ============ 设置表 ============
    await db.execute('''
      CREATE TABLE settings (
        key TEXT PRIMARY KEY,
        value TEXT NOT NULL,
        updated_at TEXT DEFAULT CURRENT_TIMESTAMP
      )
    ''');

    // 插入示例数据
    await _insertSampleData(db);
  }

  /// 插入示例数据
  static Future<void> _insertSampleData(Database db) async {
    final sampleProducts = [
      {
        'product_id': 'P001',
        'name': 'iPhone 15 Pro Max',
        'description': '全新A17 Pro芯片,钛金属设计',
        'price': 9999.0,
        'original_price': 10999.0,
        'image_url': 'https://via.placeholder.com/300x300/6366F1/ffffff?text=iPhone',
        'category': '手机',
        'stock': 100,
        'sales': 520,
        'rating': 4.8,
      },
      {
        'product_id': 'P002',
        'name': 'MacBook Pro 14寸',
        'description': 'M3 Pro芯片,性能强劲',
        'price': 14999.0,
        'original_price': 16999.0,
        'image_url': 'https://via.placeholder.com/300x300/8B5CF6/ffffff?text=MacBook',
        'category': '电脑',
        'stock': 50,
        'sales': 280,
        'rating': 4.9,
      },
      {
        'product_id': 'P003',
        'name': 'AirPods Pro 2',
        'description': '主动降噪,空间音频',
        'price': 1899.0,
        'original_price': 1999.0,
        'image_url': 'https://via.placeholder.com/300x300/EC4899/ffffff?text=AirPods',
        'category': '配件',
        'stock': 200,
        'sales': 1200,
        'rating': 4.7,
      },
      {
        'product_id': 'P004',
        'name': 'Apple Watch Series 9',
        'description': '全面屏,健康监测',
        'price': 3299.0,
        'original_price': 3599.0,
        'image_url': 'https://via.placeholder.com/300x300/10B981/ffffff?text=Watch',
        'category': '手表',
        'stock': 80,
        'sales': 450,
        'rating': 4.6,
      },
      {
        'product_id': 'P005',
        'name': 'iPad Pro 12.9寸',
        'description': 'M2芯片,Liquid视网膜XDR屏',
        'price': 9299.0,
        'original_price': 9999.0,
        'image_url': 'https://via.placeholder.com/300x300/3B82F6/ffffff?text=iPad',
        'category': '平板',
        'stock': 60,
        'sales': 320,
        'rating': 4.8,
      },
    ];

    // 批量插入商品
    for (final product in sampleProducts) {
      await db.insert('products', product);
    }

    // 插入默认设置
    await db.insert('settings', {'key': 'theme_mode', 'value': 'light'});
    await db.insert('settings', {'key': 'language', 'value': 'zh_CN'});
  }

  /// 数据库升级处理
  static Future<void> _onUpgrade(
    Database db,
    int oldVersion,
    int newVersion,
  ) async {
    // 如果需要升级数据库结构,可以在这里处理
    // 例如:
    // if (oldVersion < 2) {
    //   await db.execute('ALTER TABLE products ADD COLUMN new_field TEXT');
    // }
  }

  // ============================================================
  // 商品相关操作
  // ============================================================

  /// 获取商品列表
  static Future<List<Map<String, dynamic>>> getProducts({
    String? category,   // 分类筛选
    String? keyword,    // 搜索关键词
    int limit = 20,     // 每页数量
    int offset = 0,     // 偏移量
  }) async {
    final db = await database;
    
    // 构建 WHERE 条件
    String whereClause = '';
    List<dynamic> whereArgs = [];

    if (category != null && category.isNotEmpty) {
      whereClause = 'category = ?';
      whereArgs.add(category);
    }

    if (keyword != null && keyword.isNotEmpty) {
      if (whereClause.isNotEmpty) {
        whereClause += ' AND ';
      }
      whereClause += '(name LIKE ? OR description LIKE ?)';
      whereArgs.add('%$keyword%');
      whereArgs.add('%$keyword%');
    }

    return await db.query(
      'products',
      where: whereClause.isNotEmpty ? whereClause : null,
      whereArgs: whereArgs.isNotEmpty ? whereArgs : null,
      orderBy: 'sales DESC',  // 按销量排序
      limit: limit,
      offset: offset,
    );
  }

  /// 根据ID获取商品
  static Future<Map<String, dynamic>?> getProductById(String productId) async {
    final db = await database;
    final results = await db.query(
      'products',
      where: 'product_id = ?',
      whereArgs: [productId],
    );
    return results.isNotEmpty ? results.first : null;
  }

  /// 切换商品收藏状态
  static Future<void> toggleFavorite(String productId) async {
    final db = await database;
    final product = await getProductById(productId);
    if (product != null) {
      final currentFavorite = product['is_favorite'] == 1 ? 0 : 1;
      await db.update(
        'products',
        {'is_favorite': currentFavorite, 'updated_at': DateTime.now().toString()},
        where: 'product_id = ?',
        whereArgs: [productId],
      );
    }
  }

  // ============================================================
  // 收藏相关操作
  // ============================================================

  /// 获取收藏列表
  static Future<List<Map<String, dynamic>>> getFavorites() async {
    final db = await database;
    return await db.query(
      'products',
      where: 'is_favorite = 1',
      orderBy: 'updated_at DESC',
    );
  }

  /// 添加收藏
  static Future<void> addFavorite(String productId, {String? userId}) async {
    final db = await database;
    await db.insert(
      'favorites',
      {
        'product_id': productId,
        'user_id': userId ?? 'current_user',
      },
      conflictAlgorithm: ConflictAlgorithm.replace,  // 冲突时替换
    );
  }

  /// 移除收藏
  static Future<void> removeFavorite(String productId) async {
    final db = await database;
    await db.delete(
      'favorites',
      where: 'product_id = ?',
      whereArgs: [productId],
    );
  }

  // ============================================================
  // 浏览历史相关操作
  // ============================================================

  /// 添加浏览历史
  static Future<void> addBrowseHistory(Map<String, dynamic> product) async {
    final db = await database;
    await db.insert('browse_history', {
      'product_id': product['product_id'],
      'product_name': product['name'],
      'product_image': product['image_url'],
      'product_price': product['price'],
    });
  }

  /// 获取浏览历史
  static Future<List<Map<String, dynamic>>> getBrowseHistory({
    int limit = 20,
  }) async {
    final db = await database;
    return await db.query(
      'browse_history',
      orderBy: 'viewed_at DESC',
      limit: limit,
    );
  }

  /// 清空浏览历史
  static Future<void> clearBrowseHistory() async {
    final db = await database;
    await db.delete('browse_history');
  }

  // ============================================================
  // 购物车相关操作
  // ============================================================

  /// 获取购物车商品
  static Future<List<Map<String, dynamic>>> getCartItems() async {
    final db = await database;
    return await db.query('cart_items', orderBy: 'created_at DESC');
  }

  /// 添加到购物车
  static Future<void> addToCart(Map<String, dynamic> item) async {
    final db = await database;
    
    // 检查是否已存在
    final existing = await db.query(
      'cart_items',
      where: 'product_id = ?',
      whereArgs: [item['product_id']],
    );

    if (existing.isNotEmpty) {
      // 已存在,增加数量
      final quantity = (existing.first['quantity'] as int) + 
                        (item['quantity'] as int? ?? 1);
      await db.update(
        'cart_items',
        {'quantity': quantity},
        where: 'product_id = ?',
        whereArgs: [item['product_id']],
      );
    } else {
      // 不存在,插入新记录
      await db.insert('cart_items', item);
    }
  }

  /// 更新购物车商品数量
  static Future<void> updateCartQuantity(
    String productId,
    int quantity,
  ) async {
    final db = await database;
    
    if (quantity <= 0) {
      // 数量为0或负数,删除记录
      await db.delete(
        'cart_items',
        where: 'product_id = ?',
        whereArgs: [productId],
      );
    } else {
      // 更新数量
      await db.update(
        'cart_items',
        {'quantity': quantity},
        where: 'product_id = ?',
        whereArgs: [productId],
      );
    }
  }

  /// 从购物车移除
  static Future<void> removeFromCart(String productId) async {
    final db = await database;
    await db.delete(
      'cart_items',
      where: 'product_id = ?',
      whereArgs: [productId],
    );
  }

  /// 清空购物车
  static Future<void> clearCart() async {
    final db = await database;
    await db.delete('cart_items');
  }

  // ============================================================
  // 聊天消息相关操作
  // ============================================================

  /// 保存聊天消息
  static Future<void> saveChatMessage(Map<String, dynamic> message) async {
    final db = await database;
    await db.insert('chat_messages', message);
  }

  /// 获取聊天消息
  static Future<List<Map<String, dynamic>>> getChatMessages(
    String conversationId, {
    int limit = 50,
  }) async {
    final db = await database;
    return await db.query(
      'chat_messages',
      where: 'conversation_id = ?',
      whereArgs: [conversationId],
      orderBy: 'created_at DESC',
      limit: limit,
    );
  }

  // ============================================================
  // 设置相关操作
  // ============================================================

  /// 获取设置项
  static Future<String?> getSetting(String key) async {
    final db = await database;
    final results = await db.query(
      'settings',
      where: 'key = ?',
      whereArgs: [key],
    );
    return results.isNotEmpty ? results.first['value'] as String : null;
  }

  /// 保存设置项
  static Future<void> setSetting(String key, String value) async {
    final db = await database;
    await db.insert(
      'settings',
      {'key': key, 'value': value},
      conflictAlgorithm: ConflictAlgorithm.replace,
    );
  }

  // ============================================================
  // 工具方法
  // ============================================================

  /// 清空所有数据(用于测试或退出登录)
  static Future<void> clearAllData() async {
    final db = await database;
    await db.delete('browse_history');
    await db.delete('cart_items');
    await db.delete('chat_messages');
    await db.update('products', {'is_favorite': 0});
  }

  /// 关闭数据库
  static Future<void> close() async {
    final db = await database;
    await db.close();
    _database = null;
  }
}

3.2 在页面中使用数据库

import 'package:flutter/material.dart';
import '../services/database_service.dart';

/// 商品列表页面(使用 SQLite 数据)
class ProductListPage extends StatefulWidget {
  const ProductListPage({super.key});

  
  State<ProductListPage> createState() => _ProductListPageState();
}

class _ProductListPageState extends State<ProductListPage> {
  List<Map<String, dynamic>> _products = [];
  bool _isLoading = true;
  String? _selectedCategory;

  
  void initState() {
    super.initState();
    _loadProducts();
  }

  /// 加载商品数据
  Future<void> _loadProducts() async {
    setState(() => _isLoading = true);
    
    try {
      final products = await DatabaseService.getProducts(
        category: _selectedCategory,
        limit: 20,
      );
      
      setState(() {
        _products = products;
        _isLoading = false;
      });
    } catch (e) {
      debugPrint('加载商品失败: $e');
      setState(() => _isLoading = false);
    }
  }

  /// 切换分类
  void _filterByCategory(String? category) {
    setState(() => _selectedCategory = category);
    _loadProducts();
  }

  /// 切换收藏状态
  Future<void> _toggleFavorite(String productId) async {
    await DatabaseService.toggleFavorite(productId);
    _loadProducts();  // 刷新列表
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('商品列表'),
        actions: [
          IconButton(
            icon: const Icon(Icons.favorite),
            onPressed: () => _showFavorites(),
          ),
        ],
      ),
      body: Column(
        children: [
          // 分类筛选
          _buildCategoryFilter(),
          
          // 商品列表
          Expanded(
            child: _isLoading
                ? const Center(child: CircularProgressIndicator())
                : RefreshIndicator(
                    onRefresh: _loadProducts,
                    child: ListView.builder(
                      itemCount: _products.length,
                      itemBuilder: (context, index) {
                        return _buildProductCard(_products[index]);
                      },
                    ),
                  ),
          ),
        ],
      ),
    );
  }

  Widget _buildCategoryFilter() {
    final categories = ['全部', '手机', '电脑', '配件', '手表', '平板'];
    return Container(
      height: 50,
      child: ListView.builder(
        scrollDirection: Axis.horizontal,
        itemCount: categories.length,
        itemBuilder: (context, index) {
          final category = categories[index];
          final isSelected = (_selectedCategory == null && category == '全部') ||
                            _selectedCategory == category;
          
          return Padding(
            padding: const EdgeInsets.symmetric(horizontal: 4),
            child: FilterChip(
              label: Text(category),
              selected: isSelected,
              onSelected: (_) {
                _filterByCategory(category == '全部' ? null : category);
              },
            ),
          );
        },
      ),
    );
  }

  Widget _buildProductCard(Map<String, dynamic> product) {
    final isFavorite = product['is_favorite'] == 1;
    
    return Card(
      margin: const EdgeInsets.all(8),
      child: ListTile(
        leading: Image.network(
          product['image_url'] ?? '',
          width: 60,
          height: 60,
          fit: BoxFit.cover,
          errorBuilder: (_, __, ___) => const Icon(Icons.image),
        ),
        title: Text(product['name'] ?? ''),
        subtitle: Text('¥${product['price']}'),
        trailing: IconButton(
          icon: Icon(
            isFavorite ? Icons.favorite : Icons.favorite_border,
            color: isFavorite ? Colors.red : null,
          ),
          onPressed: () => _toggleFavorite(product['product_id']),
        ),
        onTap: () {
          // 添加到浏览历史
          DatabaseService.addBrowseHistory(product);
          // 跳转到详情页...
        },
      ),
    );
  }

  /// 显示收藏列表
  Future<void> _showFavorites() async {
    final favorites = await DatabaseService.getFavorites();
    
    showModalBottomSheet(
      context: context,
      builder: (context) => ListView.builder(
        itemCount: favorites.length,
        itemBuilder: (context, index) {
          final product = favorites[index];
          return ListTile(
            leading: Image.network(product['image_url'] ?? ''),
            title: Text(product['name'] ?? ''),
            trailing: IconButton(
              icon: const Icon(Icons.favorite, color: Colors.red),
              onPressed: () {
                DatabaseService.toggleFavorite(product['product_id']);
                Navigator.pop(context);
                _loadProducts();
              },
            ),
          );
        },
      ),
    );
  }
}

四、开发踩坑与挫折 😤

4.1 踩坑一:数据库路径问题

问题描述
在鸿蒙设备上运行,报错找不到数据库路径。

解决方案

// 使用 sqflite 提供的方法获取路径
final dbPath = await getDatabasesPath();
final path = join(dbPath, 'my_app.db');

4.2 踩坑二:数据库版本升级

问题描述
修改了表结构,但数据库没有更新。

解决方案

// 修改 onUpgrade 方法
static Future<void> _onUpgrade(
  Database db,
  int oldVersion,
  int newVersion,
) async {
  if (oldVersion < 2) {
    await db.execute('ALTER TABLE products ADD COLUMN new_column TEXT');
  }
}

五、最终实现效果 📸

(此处附鸿蒙设备上成功运行的截图)
在这里插入图片描述


在这里插入图片描述

六、个人学习总结 📝

通过 SQLite 数据库的学习,我收获了很多:

  1. ✅ 学会了 SQL 的基本使用(CREATE、SELECT、INSERT、UPDATE、DELETE)
  2. ✅ 理解了数据库表的设计原则
  3. ✅ 学会了数据库升级的迁移策略

作者:上海某高校大一学生,Flutter 爱好者
发布时间:2026年4月

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐