パッケージ、リポジトリ、アプリカテゴリを検索

パッケージ詳細v2.0.0

flutter_data

Flutterで永続データモデルを扱うためのスムーズな方法。Ember Data および ActiveRecord にインスパイアされています。

AndroidiOSmacOSWindowsLinux
30日間ダウンロード
1020
いいね数
373
Pub ポイント
130

パッケージをインストール

flutter pub add flutter_data

パッケージ概要

公開情報に基づく選定ガイドです。参考としてご利用ください。

Flutter Data は、Ember Data や ActiveRecord を参考に、Flutter アプリケーションにおける永続データモデルの管理を簡素化するアプローチを提供します。ローカルおよびリモートのデータソースに対して一貫した API を提供することで、CRUD 操作、状態管理、デバイス間のデータ同期を容易にします。このパッケージは Flutter のエコシステムとシームレスに統合され、型安全なモデル、自動シリアル化、組み込みキャッシュをサポートしています。開発者はデータモデルと関係性を簡単に定義でき、ボイラープレートコードなしで効率的なデータ処理が可能になります。

用途

  • オフライン優先のデータワークフローの管理
  • ローカルストレージとAPI間でのデータ同期
  • スケーラブルな CRUD アプリケーションの構築
  • 複雑なデータ関係の処理
  • リアクティブなデータ更新の実装

主な機能

  • 自動モデルシリアライズ
  • SQLite を用いた組み込みの永続化
  • リアクティブな状態管理
  • REST および GraphQL API のサポート
  • 型安全なデータモデルとクエリ

適している対象

  • オフラインデータアクセスを必要とするアプリ
  • REST や GraphQL ベースのバックエンドを使用するプロジェクト
  • ボイラープレートを減らしたい開発者
  • MVCに類するデータアーキテクチャを採用するチーム
  • 複雑なデータ関係を持つアプリケーション

注意事項

  • 新規ユーザーの習得曲線
  • 成熟した代替品と比べてドキュメントが限られている
  • カスタムエンドポイント用に追加の設定が必要になる場合があります
  • データ要件が非常に少ない単純なアプリには適していない
  • json_serializable などの外部依存関係に依存しています

元の README

関連するソースリポジトリの README 原文のスナップショットです。最新の内容はリポジトリをご覧ください。

Flutter Data

tests codecov pub.dev license

Persistent reactive models in Flutter with zero boilerplate

Flutter Data is a local-first data framework with a customizable REST client and powerful model relationships, built on Riverpod.

Inspired by Ember Data and ActiveRecord.

Features

  • Adapters for all models 🚀
    • Default CRUD and custom remote endpoints
    • StateNotifier watcher APIs
  • Built for offline-first 🔌
    • SQLite3-based local storage at its core, with adapters for many other engines: Objectbox, Isar, etc (coming soon)
  • Intuitive APIs, effortless setup 💙
    • Truly configurable and composable via Dart mixins and codegen
    • Built-in Riverpod providers for all models
  • Exceptional relationship support ⚡️
    • Automatically synchronized, fully traversable relationship graph
    • Reactive relationships

👩🏾‍💻 Quick introduction

In Flutter Data, every model gets its default adapter. These adapters can be extended by mixing in custom adapters.

Annotate a model with @DataAdapter and pass a custom adapter:

@JsonSerializable()
@DataAdapter([MyJSONServerAdapter])
class User extends DataModel<User> {
  @override
  final int? id; // ID can be of any type
  final String name;
  User({this.id, required this.name});
  // `User.fromJson` and `toJson` optional
}

mixin MyJSONServerAdapter on RemoteAdapter<User> {
  @override
  String get baseUrl => "https://my-json-server.typicode.com/flutterdata/demo/";
}

After code-gen, Flutter Data will generate the resulting Adapter<User> which is accessible via Riverpod's ref.users or container.users.

@override
Widget build(BuildContext context, WidgetRef ref) {
  final state = ref.users.watchOne(1);
  if (state.isLoading) {
    return Center(child: const CircularProgressIndicator());
  }
  final user = state.model;
  return Text(user.name);
}

To update the user:

TextButton(
  onPressed: () => ref.users.save(User(id: 1, name: 'Updated')),
  child: Text('Update'),
),

ref.users.watchOne(1) will make a background HTTP request (to https://my-json-server.typicode.com/flutterdata/demo/users/1 in this case), deserialize data and listen for any further local changes to the user.

state is of type DataState which has loading, error and data substates.

In addition to the reactivity, models have ActiveRecord-style extension methods so the above becomes:

GestureDetector(
  onTap: () => User(id: 1, name: 'Updated').save(),
  child: Text('Update')
),

Compatibility

Fully compatible with the tools we know and love:

Flutter ✅ Or plain Dart. It does not require Flutter.
json_serializable ✅ Fully supported (but not required)
Riverpod ✅ Supported & automatically wired up
Classic JSON REST API ✅ Built-in support!
JSON:API ✅ Supported via external adapter
Firebase, Supabase, GraphQL ✅ Can be fully supported by writing custom adapters
Freezed ✅ Supported!
Flutter Web - TBD

📲 Apps using Flutter Data in production

logos

Integrations

  • Appwrite Offline. A Flutter Data adapter for Appwrite that provides offline support, real-time updates, and seamless integration with Flutter Data's powerful features.

🚨 BREAKING CHANGES IN 2.0

  • All methods are now directly on Adapter, there is no Repository, RemoteAdapter or LocalAdapter. Any method you are looking for is probably on Adapter, for example, findAll from LocalAdapter is now called findAllLocal
  • For initialization we no longer call the configure... method on the Riverpod overrides, we just do localStorageProvider.overrideWithValue and pass a LocalStorage instance; the actual initialization is done via initializeFlutterData which needs an adapter map. An adapterProvidersMap is conveniently code-generated and available on main.data.dart
  • Offline operations are still supported, but retries are not, it is up to clients to implement them

📚 Public API

Initialization

First you need to supply a local storage provider, via Riverpod configuration. A popular option for the base directory is using the path_provider package.

ProviderScope(
  overrides: [
    localStorageProvider.overrideWithValue(
      LocalStorage(
        baseDirFn: () async {
          return (await getApplicationSupportDirectory()).path;
        },
        busyTimeout: 5000,
        clear: LocalStorageClearStrategy.never,
      ),
    )
  ],
  // ...
),

And initialize like so:

return Scaffold(
  body: ref.watch(initializeFlutterData(adapterProvidersMap)).when(
    data: (_) => child,
    error: (e, _) => const Text('Error'),
    loading: () => const Center(child: CircularProgressIndicator()),
  ),
Adapters

Method names should be self explanatory. All of these methods have a reasonable default implementation.

Local storage

All models are identified by keys, that might be associated to an id (either self-assigned or fetched from a remote source).

Keys have the format model#5.

List<T> findAllLocal();

List<T> findManyLocal(Iterable<String> keys);

T? findOneLocal(String? key);

T? findOneLocalById(Object id);

bool exists(String key);

T saveLocal(T model, {bool notify = true});

Future<List<String>?> saveManyLocal(Iterable<DataModelMixin> models,
      {bool notify = true, bool async = true});

void deleteLocal(T model, {bool notify = true});

void deleteLocalById(Object id, {bool notify = true});

void deleteLocalByKeys(Iterable<String> keys, {bool notify = true});

Future<void> clearLocal({bool notify = true});

int get countLocal;

Set<String> get keys;
Remote & watchers
Future<List<T>> findAll({
    bool remote = true,
    bool background = false,
    Map<String, dynamic>? params,
    Map<String, String>? headers,
    bool syncLocal = false,
    OnSuccessAll<T>? onSuccess,
    OnErrorAll<T>? onError,
    DataRequestLabel? label,
  });

Future<T?> findOne(
    Object id, {
    bool remote = true,
    bool background = false,
    Map<String, dynamic>? params,
    Map<String, String>? headers,
    OnSuccessOne<T>? onSuccess,
    OnErrorOne<T>? onError,
    DataRequestLabel? label,
  });

Future<T> save(
    T model, {
    bool remote = true,
    Map<String, dynamic>? params,
    Map<String, String>? headers,
    OnSuccessOne<T>? onSuccess,
    OnErrorOne<T>? onError,
    DataRequestLabel? label,
  });

Future<T?> delete(
    Object model, {
    bool remote = true,
    Map<String, dynamic>? params,
    Map<String, String>? headers,
    OnSuccessOne<T>? onSuccess,
    OnErrorOne<T>? onError,
    DataRequestLabel? label,
  });

Set<OfflineOperation<T>> get offlineOperations;

DataState<List<T>> watchAll({
    bool remote = false,
    Map<String, dynamic>? params,
    Map<String, String>? headers,
    bool syncLocal = false,
    String? finder,
    DataRequestLabel? label,
  });

DataState<T?> watchOne(
    Object model, {
    bool remote = false,
    Map<String, dynamic>? params,
    Map<String, String>? headers,
    AlsoWatch<T>? alsoWatch,
    String? finder,
    DataRequestLabel? label,
  });

DataStateNotifier<List<T>> watchAllNotifier(
      {bool remote = false,
      Map<String, dynamic>? params,
      Map<String, String>? headers,
      bool syncLocal = false,
      String? finder,
      DataRequestLabel? label});

DataStateNotifier<T?> watchOneNotifier(Object model,
      {bool remote = false,
      Map<String, dynamic>? params,
      Map<String, String>? headers,
      AlsoWatch<T>? alsoWatch,
      String? finder,
      DataRequestLabel? label});

final coreNotifierThrottleDurationProvider;
Serialization
Future<Map<String, dynamic>> serialize(T model,
      {bool withRelationships = true});

Future<DeserializedData<T>> deserialize(Object? data,
      {String? key, bool async = true});

Future<DeserializedData<T>> deserializeAndSave(Object? data,
      {String? key, bool notify = true, bool ignoreReturn = false});

➕ Questions and collaborating

Please use Github to ask questions, open issues and send PRs. Thanks!

Tests can be run with: dart test

📝 License

See LICENSE.

カテゴリー

依存関係

依存関係一覧 17 件

ソースリポジトリ

このパッケージを使用しているプロジェクト

データはまだありません。