flutter-databases by flutter/skills
npx skills add https://github.com/flutter/skills --skill flutter-databases在 Flutter 应用程序中架构并实现一个健壮的、符合 MVVM 模式的数据层。使用 Repository 模式建立单一数据源,将外部 API 和本地数据库交互隔离到无状态的 Service 中,并根据数据需求实现最优的本地缓存策略(例如,通过 sqflite 使用 SQLite)。假设 Flutter 环境已预先配置好。
使用以下决策树评估用户的数据持久化需求,以选择合适的缓存策略:
shared_preferences。sqflite 或 drift)。hive_ce 或 isar_community)。广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
cached_network_image 将图片存储到文件系统。shared_preferences 来说是否太大,但又不需要查询?
分析数据需求 请暂停并询问用户: "数据层需要管理哪些具体的数据实体,它们的持久化需求是什么(例如,大小、关系复杂度、离线优先能力)?" 等待用户回复后再继续步骤 2。
配置依赖项 根据决策逻辑,添加所需的依赖项。对于标准的 SQLite 实现,请执行:
flutter pub add sqflite path
定义领域模型 创建代表领域模型的纯 Dart 数据类。这些模型应仅包含应用程序其他部分所需的信息。
class Todo {
final int? id;
final String title;
final bool isCompleted;
const Todo({this.id, required this.title, required this.isCompleted});
Map<String, dynamic> toMap() {
return {
'id': id,
'title': title,
'isCompleted': isCompleted ? 1 : 0,
};
}
factory Todo.fromMap(Map<String, dynamic> map) {
return Todo(
id: map['id'] as int?,
title: map['title'] as String,
isCompleted: map['isCompleted'] == 1,
);
}
}
实现数据库服务 创建一个无状态的服务类来处理与 SQLite 数据库的直接交互。
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class DatabaseService {
Database? _database;
Future<void> open() async {
if (_database != null && _database!.isOpen) return;
_database = await openDatabase(
join(await getDatabasesPath(), 'app_database.db'),
onCreate: (db, version) {
return db.execute(
'CREATE TABLE todos(id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, isCompleted INTEGER)',
);
},
version: 1,
);
}
bool get isOpen => _database != null && _database!.isOpen;
Future<int> insertTodo(Todo todo) async {
return await _database!.insert(
'todos',
todo.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
Future<List<Todo>> fetchTodos() async {
final List<Map<String, dynamic>> maps = await _database!.query('todos');
return maps.map((map) => Todo.fromMap(map)).toList();
}
Future<void> deleteTodo(int id) async {
await _database!.delete(
'todos',
where: 'id = ?',
whereArgs: [id],
);
}
}
实现 API 客户端服务(可选/如果适用) 创建一个用于远程数据获取的无状态服务。
class ApiClient {
Future<List<dynamic>> fetchRawTodos() async {
// Implementation for HTTP GET request
return [];
}
}
实现 Repository 创建 Repository 类。这是应用程序数据的单一数据源。它必须将服务封装为私有成员。
class TodoRepository {
final DatabaseService _databaseService;
final ApiClient _apiClient;
TodoRepository({
required DatabaseService databaseService,
required ApiClient apiClient,
}) : _databaseService = databaseService,
_apiClient = apiClient;
Future<List<Todo>> getTodos() async {
await _ensureDbOpen();
// 离线优先逻辑示例:获取本地数据,可选地与远程同步
return await _databaseService.fetchTodos();
}
Future<void> createTodo(Todo todo) async {
await _ensureDbOpen();
await _databaseService.insertTodo(todo);
// 如有必要,在此处触发 API 同步
}
Future<void> removeTodo(int id) async {
await _ensureDbOpen();
await _databaseService.deleteTodo(id);
}
Future<void> _ensureDbOpen() async {
if (!_databaseService.isOpen) {
await _databaseService.open();
}
}
}
验证与修复 根据以下检查项审查生成的实现:
_databaseService、_apiClient)是否是 Repository 的私有成员?如果不是,请重构以限制 UI 层访问。_ensureDbOpen() 模式。id)以优化更新/删除时间?DatabaseService 或 ApiClient)交互。所有数据请求必须通过 Repository 路由。sqflite 操作中始终使用参数化查询(例如 whereArgs: [id])。切勿在 SQL 查询中使用字符串插值。每周安装量
959
代码仓库
GitHub 星标数
784
首次出现
2026年3月4日
安全审计
安装于
codex940
github-copilot936
opencode936
cursor936
gemini-cli935
kimi-cli934
Architects and implements a robust, MVVM-compliant data layer in Flutter applications. Establishes a single source of truth using the Repository pattern, isolates external API and local database interactions into stateless Services, and implements optimal local caching strategies (e.g., SQLite via sqflite) based on data requirements. Assumes a pre-configured Flutter environment.
Evaluate the user's data persistence requirements using the following decision tree to select the appropriate caching strategy:
shared_preferences.sqflite or drift).hive_ce or isar_community).cached_network_image to store images on the file system.shared_preferences but doesn't require querying?
Analyze Data Requirements STOP AND ASK THE USER: "What specific data entities need to be managed in the data layer, and what are their persistence requirements (e.g., size, relational complexity, offline-first capabilities)?" Wait for the user's response before proceeding to step 2.
Configure Dependencies Based on the decision logic, add the required dependencies. For a standard SQLite implementation, execute:
flutter pub add sqflite path
Define Domain Models Create pure Dart data classes representing the domain models. These models should contain only the information needed by the rest of the app.
class Todo {
final int? id;
final String title;
final bool isCompleted;
const Todo({this.id, required this.title, required this.isCompleted});
Map<String, dynamic> toMap() {
return {
'id': id,
'title': title,
'isCompleted': isCompleted ? 1 : 0,
};
}
factory Todo.fromMap(Map<String, dynamic> map) {
return Todo(
id: map['id'] as int?,
title: map['title'] as String,
isCompleted: map['isCompleted'] == 1,
);
}
}
Implement the Database Service Create a stateless service class to handle direct interactions with the SQLite database.
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class DatabaseService {
Database? _database;
Future<void> open() async {
if (_database != null && _database!.isOpen) return;
_database = await openDatabase(
join(await getDatabasesPath(), 'app_database.db'),
onCreate: (db, version) {
return db.execute(
'CREATE TABLE todos(id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, isCompleted INTEGER)',
);
},
version: 1,
);
}
bool get isOpen => _database != null && _database!.isOpen;
Future<int> insertTodo(Todo todo) async {
return await _database!.insert(
'todos',
todo.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
Future<List<Todo>> fetchTodos() async {
final List<Map<String, dynamic>> maps = await _database!.query('todos');
return maps.map((map) => Todo.fromMap(map)).toList();
}
Future<void> deleteTodo(int id) async {
await _database!.delete(
'todos',
where: 'id = ?',
whereArgs: [id],
);
}
}
DatabaseService or ApiClient). All data requests must route through the Repository.whereArgs: [id]) in sqflite operations. Never use string interpolation for SQL queries.Weekly Installs
959
Repository
GitHub Stars
784
First Seen
Mar 4, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex940
github-copilot936
opencode936
cursor936
gemini-cli935
kimi-cli934
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
102,200 周安装
Grimoire CLI 使用指南:区块链法术编写、验证与执行全流程
940 周安装
Grimoire Uniswap 技能:查询 Uniswap 元数据与生成代币/资金池快照的 CLI 工具
940 周安装
Grimoire Aave 技能:查询 Aave V3 元数据和储备快照的 CLI 工具
941 周安装
Railway CLI 部署指南:使用 railway up 命令快速部署代码到 Railway 平台
942 周安装
n8n Python 代码节点使用指南:在自动化工作流中编写 Python 脚本
943 周安装
Flutter Platform Views 实现指南:Android/iOS/macOS原生视图与Web嵌入教程
943 周安装
Implement the API Client Service (Optional/If Applicable) Create a stateless service for remote data fetching.
class ApiClient {
Future<List<dynamic>> fetchRawTodos() async {
// Implementation for HTTP GET request
return [];
}
}
Implement the Repository Create the Repository class. This is the single source of truth for the application data. It must encapsulate the services as private members.
class TodoRepository {
final DatabaseService _databaseService;
final ApiClient _apiClient;
TodoRepository({
required DatabaseService databaseService,
required ApiClient apiClient,
}) : _databaseService = databaseService,
_apiClient = apiClient;
Future<List<Todo>> getTodos() async {
await _ensureDbOpen();
// Example of offline-first logic: fetch local, optionally sync with remote
return await _databaseService.fetchTodos();
}
Future<void> createTodo(Todo todo) async {
await _ensureDbOpen();
await _databaseService.insertTodo(todo);
// Trigger API sync here if necessary
}
Future<void> removeTodo(int id) async {
await _ensureDbOpen();
await _databaseService.deleteTodo(id);
}
Future<void> _ensureDbOpen() async {
if (!_databaseService.isOpen) {
await _databaseService.open();
}
}
}
Validate-and-Fix Review the generated implementation against the following checks:
_databaseService, _apiClient) private members of the Repository? If not, refactor to restrict UI layer access._ensureDbOpen() pattern.id) used effectively in SQLite queries to optimize update/delete times?