搜尋套件、儲存庫與 APP 分類

套件詳情v1.1.2

stock

Dart 套件,用於非同步資料載入與快取。輕鬆且安全地結合本機(資料庫、快取)與網路資料。

AndroidiOSWebmacOSWindowsLinux
近 30 天下載
664
喜歡數
119
Pub 分數
150

安裝套件

flutter pub add stock

套件介紹

根據套件公開資料產生的選型評估,僅供參考。

Stock 是一個 Dart 套件,專為 Flutter 應用程式中的高效異步資料載入與快取而設計。它簡化了本地資料來源(如資料庫或記憶體快取)與遠端網路請求的整合,確保資料的一致性與效能。透過提供統一介面,Stock 讓開發者能安全地取得、快取與重新整理資料,減少重複程式碼,並處理過期資料或網路失敗等邊際情況。非常適合需要可靠離線支援與即時更新的應用。

適用情境

  • 離線優先的資料取得
  • 快取 API 回應
  • 同步本地和遠端資料
  • 即時資料更新
  • 減少重複的網路呼叫
  • 使用快取資料管理應用狀態

主要特色

  • 統一的本地和網絡數據訪問
  • 自動快取失效
  • 對異步資料流的支援
  • 內建重試和回退機制
  • 輕量且無侵入性
  • 與現有資料層的無縫整合

適合對象

  • 需要強大離線功能的應用程式
  • 尋求簡化數據管理的開發者
  • 頻繁進行API互動的項目
  • 優先考慮可維護資料邏輯的團隊
  • 使用響應式程式設計模式的Flutter應用
  • 在設備狀態之間需要一致資料的應用程式

使用建議

  • 需要理解 async/await 模式
  • 可能需要額外設定以支援複雜的快取策略
  • 不是完整的ORM或資料庫解決方案
  • 最適合讀取密集型作業
  • 對複雜查詢篩選的內建支援有限
  • 依賴外部儲存實作進行持久化

原始 README

以下為關聯原始碼儲存庫的 README 原文快照,最新內容請造訪原始碼儲存庫。

stock

Pub Codecov Dart CI style: very good analysis

Stock is a dart package for loading data from both remote and local sources. It is inspired by the Store Kotlin library.

Its main goal is to prevent excessive calls to the network and disk cache. By utilizing it, you eliminate the possibility of flooding your network with the same request while, at the same time, adding layers of caching.

Although you can use it without a local source, the greatest benefit comes from combining Stock with a local database such as Floor, Drift, Sqflite, Realm, etc.

Features

  • Combine local (DB, cache) and network data simply. It provides a data Stream where you can listen and work with your data.
  • Know the data Stream state. It's useful for displaying errors or loading indicators, for example.
  • If you are not using a local DB, Stock provides a memory cache, to share and improve upon your app's experience.
  • Work with your data more safely. If an error is thrown, Stock will catch it and return it into the stream so it can be handled easily.

Overview

A Stock is responsible for managing a particular data request.

It is based on two important classes:

  • Fetcher: defines how data will be fetched over the network.
  • SourceOfTruth: defines how local data will be read and written in your local cache. Although Stock can be used without it, we recommend its usage.

Stock uses generic keys as identifiers for data. A key can be any value object that properly implements toString(), equals() and hashCode().

When you create Stock, it provides you with a bunch of methods in order to access the data. The most important one is stream(), which provides you with a Stream of your data, which can be used to update your UI or to do a specific action.

Getting started

To use this package, add stock as a dependency in your pubspec.yaml file.

1. Create a Fetcher

The Fetcher is required to fetch new data from the network. You can create it from a Future or from a Stream.

FutureFetcher is usually used alongside a RestApi, whereas StreamFetcher is used with a Stream source like a Web Socket.

  final futureFetcher = Fetcher.ofFuture<String, List<Tweet>>(
    (userId) => _api.getUserTweets(userId),
  );

  final streamFetcher = Fetcher.ofStream<String, List<Tweet>>(
    (userId) => _api.getUserTweetsStream(userId),
  );
2. Create a Source Of Truth

The SourceOfTruth is used to read and write the remote data in a local cache.

Generally you will implement the SourceOfTruth using a local database. However, if you are not using a local database / cache, the library provides the CachedSourceOfTruth, a source of truth which stores the data in memory.

  final sourceOfTruth = SourceOfTruth<String, List<Tweet>>(
    reader: (userId) => _database.getUserTweets(userId),
    writer: (userId, tweets) => _database.writeUserTweets(userId, tweets),
    delete: (userId) => _database.deleteUserTweets(userId), // this is optional
    deleteAll: () => _database.deleteAllTweets(), // this is optional
  );

In this example, Fetcher type is exactly the SourceOfTruth type. If you need to use multiple types, you can use the mapTo extension.

Note: to the proper operation, when write is invoked with new data, the source of truth has to emit the new value in the reader.

3. Create the Stock

Stock lets you combine the different data sources and get the data.

 final stock = Stock<String, List<Tweet>>(
    fetcher: fetcher,
    sourceOfTruth: sourceOfTruth,
  );
Get a data Stream from Stock

You can generate a data Stream using stream().

You need to invoke it with a specific key, and an optional refresh value that tells Stock if a refresh is optional or mandatory.

That returns a data stream of StockResponse, which has 3 possible values:

  • StockResponseLoading informs that a network request is in progress. It can be useful to display a loading indicator in your UI.
  • StockResponseData holds the response data. It has a value field which includes an instance of the type returned by Stock.
  • StockResponseError indicates that an error happened. When an error happens, Stock does not throw an exception, instead, it wraps it in this class. It includes an error field that contains the exception thrown by the given origin.

Each StockResponse includes an origin field which specifies where the event is coming from.

  stock
      .stream('user_id', refresh: true)
      .listen((StockResponse<List<Tweet>> stockResponse) {
    stockResponse.when(
      onLoading: (origin) => _displayLoadingIndicator(),
      onData: (origin, data) => _displayTweetsInUI(data),
      onError: (origin, error, stacktrace) => _displayErrorInUi(error),
    );
  });

Note: Stock provides a large list of extensions to facilitate and manipulate the responses.

Get non-stream data from Stock

Stock provides a couple of methods to get data without using a data stream.

  1. get returns cached data -if it is cached- otherwise will return fresh/network data (updating your caches).
  2. fresh returns fresh data updating your cache
  3. clear purge a particular entry from memory and disk cache
  4. clearAll Purge all entries from memory and disk cache
  // Get fresh data
  final List<Tweet> freshTweets = await stock.fresh(key);
  
  // Get the previous cached data
  final List<Tweet> cachedTweets = await stock.get(key);

  // Clear key from stock
  await stock.clear(key);

  // Clear all keys from stock
  await stock.clearAll();
Use different types for Fetcher and SourceOfTruth

Sometimes you need to use different entities for Network and DB. For that case Stock provides the StockTypeMapper, a class that transforms one entity into the other.

StockTypeMapper is used in the SourceOfTruth via the method mapToUsingMapper

class TweetMapper implements StockTypeMapper<DbTweet, NetworkTweet> {
  @override
  NetworkTweet fromInput(DbTweet value) => NetworkTweet(value);

  @override
  DbTweet fromOutput(NetworkTweet value) => DbTweet(value);
}

final SourceOfTruth<int, DbTweet> sot = _createMapper();
final SourceOfTruth<int, NetworkTweet> newSot = sot.mapToUsingMapper(TweetMapper());

You can also achieve the same result using the mapTo extension.


final SourceOfTruth<int, DbTweet> sot = _createMapper();
final SourceOfTruth<int, NetworkTweet> newSot = mapTo(
      (networkTweet) => DbTweet(networkTweet),
      (dbTweet) => NetworkTweet(dbTweet),
);
Use a non-stream source of truth

Sometimes your Source of Truth does not provide you with a real-time data stream. For example, suppose that you are using shared_preferences to store your data, or you are just catching your data in memory. For these cases, Stock provides you the CachedSourceOfTruth, a SourceOfTruth that be helpful in these cases.

class SharedPreferencesSourceOfTruth extends CachedSourceOfTruth<String, String> {
  SharedPreferencesSourceOfTruth();

  @override
  @protected
  Stream<T?> reader(String key) async* {
    final prefs = await SharedPreferences.getInstance();
    // Read data from an non-stream source
    final stringValue = prefs.getString(key);
    setCachedValue(key, stringValue);
    yield* super.reader(key);
  }

  @override
  @protected
  Future<void> write(String key, String? value) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString(key, value);
    await super.write(key, value);
  }
}

This class can be used with StockTypeMapper to transform your data into an entity.

// The mapper json to transform a string into a User 
final StockTypeMapper<String, User> mapper = _createUserMapper();
final SharedPreferencesSourceOfTruth<String, User> = SharedPreferencesSourceOfTruth()
    .mapToUsingMapper(mapper);

Additional information

For bugs please use GitHub Issues. For questions, ideas, and discussions use GitHub Discussions.

Made with ❤️ by Xmartlabs.

License

Copyright (c) 2022 Xmartlabs SRL.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

所屬分類

依賴

依賴清單 3 項

原始碼儲存庫

使用此套件的專案