ホーム/移动开发/flutter-databases
F

flutter-databases

by @flutterv
4.7(28)

FlutterアプリでMVVMに準拠した堅牢なデータ層を設計・実装し、Repositoryパターンを使用して単一のデータソースを確立します。

FlutterSQLiteSqfliteHiveFirebase FirestoreGitHub
インストール方法
npx skills add flutter/skills --skill flutter-databases
compare_arrows

Before / After 効果比較

1
使用前

Flutterアプリケーションのデータ層の設計は混乱しており、データソースが統一されておらず、保守および拡張が困難でした。MVVMアーキテクチャとRepositoryパターンが不足しており、ビジネスロジックとデータが密接に結合していました。

使用後

MVVMに準拠した堅牢なデータ層を設計・実装し、Repositoryパターンを通じて単一のデータソースを確立しました。ビジネスロジックを効果的に分離し、データ管理の効率とアプリケーションの保守性を向上させました。

description SKILL.md

flutter-databases

flutter-data-layer-persistence

Goal

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.

Decision Logic

Evaluate the user's data persistence requirements using the following decision tree to select the appropriate caching strategy:

  • Is the data small, simple key-value pairs (e.g., user preferences, theme settings)?

Yes: Use shared_preferences.

  • Is the data a large, structured, relational dataset requiring fast inserts/queries?

Yes: Use On-device relational databases (sqflite or drift).

  • Is the data a large, unstructured/non-relational dataset?

Yes: Use On-device non-relational databases (hive_ce or isar_community).

  • Is the data primarily API response caching?

Yes: Use a lightweight remote caching system or interceptors.

  • Is the data primarily images?

Yes: Use cached_network_image to store images on the file system.

  • Is the data too large for shared_preferences but doesn't require querying?

Yes: Use direct File System I/O.

Instructions

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],
    );
  }
}

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:

Check: Are the services (_databaseService, _apiClient) private members of the Repository? If not, refactor to restrict UI layer access.

  • Check: Does the Repository explicitly ensure the database is open before executing queries? If not, inject the _ensureDbOpen() pattern.

  • Check: Are primary keys (id) used effectively in SQLite queries to optimize update/delete times?

Constraints

  • Single Source of Truth: The UI layer MUST NEVER interact directly with a Service (e.g., DatabaseService or ApiClient). All data requests must route through the Repository.

  • Stateless Services: Service classes must remain stateless and contain no side effects outside of their specific external API/DB wrapper responsibilities.

  • Domain Model Isolation: Repositories must transform raw data (from APIs or DBs) into Domain Models before passing them to the UI layer.

  • SQL Injection Prevention: Always use parameterized queries (e.g., whereArgs: [id]) in sqflite operations. Never use string interpolation for SQL queries.

  • Database State: The Repository must guarantee the database connection is open before attempting any read/write operations.

Weekly Installs959Repositoryflutter/skillsGitHub Stars725First Seen14 days agoSecurity AuditsGen Agent Trust HubPassSocketPassSnykPassInstalled oncodex940github-copilot936opencode936cursor936gemini-cli935kimi-cli934

forumユーザーレビュー (0)

レビューを書く

効果
使いやすさ
ドキュメント
互換性

レビューなし

統計データ

インストール数613
評価4.7 / 5.0
バージョン
更新日2026年3月17日
比較事例1 件

ユーザー評価

4.7(28)
5
0%
4
0%
3
0%
2
0%
1
0%

この Skill を評価

0.0

対応プラットフォーム

🔧Claude Code
🔧OpenClaw
🔧OpenCode
🔧Codex
🔧Gemini CLI
🔧GitHub Copilot
🔧Amp
🔧Kimi CLI

タイムライン

作成2026年3月17日
最終更新2026年3月17日