An in-memory implementation of the DataClient
interface, designed primarily for testing, local development, or scenarios where a lightweight, non-persistent data store is sufficient. This package is part of the Flutter News App - Full Source Code Toolkit.
DataInMemory
provides a way to simulate a backend data source entirely in memory. It supports:
- Standard CRUD (Create, Read, Update, Delete) operations.
- User-scoped data: Operations can be tied to a specific
userId
. - Global data: Operations can target data not associated with any user.
- Rich, document-style querying via the
readAll
method'sfilter
parameter. - General Filtering: Supports operators like
$in
,$nin
,$ne
,$gte
, etc., on any field, including nested ones (category.id
). - Simulated Full-Text Search: Accepts a special
q
key in the filter ({'q': 'search term'}
) to perform a case-insensitive substring search on the primary text field of a model (title
for headlines,name
for topics/sources). - Multi-field sorting via a list of
SortOption
objects. - Cursor-based pagination via the
PaginationOptions
object.
This client is useful for:
- Unit and integration testing of repositories or BLoCs that depend on
DataClient
. - Rapid prototyping and local development without needing a live backend.
- Demonstrations or examples.
This package is typically used as a development dependency or a direct dependency in projects that require an in-memory data store for local or test environments.
To use this package, add data_inmemory
to your pubspec.yaml
dependencies.
dependencies:
# data_client is also required as it defines the interface
data_client:
git:
url: https://github.com/flutter-news-app-full-source-code/data-client.git
# ref: <specific_commit_or_tag> # Optional: pin to a version
data_inmemory:
git:
url: https://github.com/flutter-news-app-full-source-code/data-inmemory.git
# ref: <specific_commit_or_tag> # Optional: pin to a version
# core is needed for models and exceptions
core:
git:
url: https://github.com/flutter-news-app-full-source-code/core.git
# ref: <specific_commit_or_tag> # Optional: pin to a version
Then run dart pub get
or flutter pub get
.
- Implements the
DataClient<T>
interface frompackage:data_client
. - In-memory storage for generic data types
T
. - Support for
initialData
to pre-populate the client. - User-scoped and global data operations.
create
,read
,update
,delete
methods.- A unified
readAll
method with support for rich filtering, multi-field sorting, and cursor-based pagination. - Throws standard exceptions from
package:core
(e.g.,NotFoundException
,BadRequestException
). count
method for efficient document counting without fetching data.aggregate
method to simulate basic MongoDB aggregation pipelines (supports$match
,$group
,$sort
,$limit
), enabling testing of analytics-style queries.
Here's a basic example of how to use DataInMemoryClient
with a simple Article
model:
import 'package:data_client/data_client.dart'; // Defines DataClient
import 'package:data_inmemory/data_inmemory.dart';
import 'package:core/core.dart'; // Assuming SuccessApiResponse etc. are here
// 1. Define your model (ensure it has an ID and toJson method)
class Article {
Article({required this.id, required this.title, this.content});
final String id;
final String title;
final String? content;
// Required for DataInMemoryClient
Map<String, dynamic> toJson() => {
'id': id,
'title': title,
// Add other fields for querying
if (content != null) 'content': content,
};
// For easy comparison in examples/tests
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Article &&
runtimeType == other.runtimeType &&
id == other.id &&
title == other.title &&
content == other.content;
@override
int get hashCode => id.hashCode ^ title.hashCode ^ content.hashCode;
}
void main() async {
// 2. Define helper functions for the client
String getArticleId(Article article) => article.id;
Map<String, dynamic> articleToJson(Article article) => article.toJson();
// 3. Instantiate the client
final client = DataInMemoryClient<Article>(
getId: getArticleId,
toJson: articleToJson,
initialData: [
Article(id: 'article1', title: 'First Article', content: 'Hello world!'),
],
);
// 4. Use the client
try {
// Create a new article
final newArticle = Article(id: 'article2', title: 'Second Article');
SuccessApiResponse<Article> createResponse =
await client.create(item: newArticle);
print('Created: ${createResponse.data.title}');
// Read an article
SuccessApiResponse<Article> readResponse =
await client.read(id: 'article1');
print('Read: ${readResponse.data.title}');
// Query articles with a search term and other filters
final filter = {
'q': 'article', // Search for 'article' in title
'content': {'\$ne': null} // And content is not null
};
final sort = [const SortOption('title', SortOrder.desc)];
final pagination = const PaginationOptions(limit: 5);
final queryResponse = await client.readAll(
filter: filter,
sort: sort,
pagination: pagination,
);
print('Found ${queryResponse.data.items.length} sorted articles matching query:');
for (final article in queryResponse.data.items) {
print('- ${article.title}');
}
print('Has more pages: ${queryResponse.data.hasMore}');
// Count published articles
final countResponse = await client.count(filter: {'isPublished': true});
print('Number of published articles: ${countResponse.data}');
// Run an aggregation pipeline to get article count per category
final aggregateResponse = await client.aggregate(
pipeline: [
{
r'$group': {
'_id': r'$category.name',
'count': {r'$sum': 1},
},
},
{
r'$sort': {'count': -1},
},
],
);
print('Article count per category:');
for (final result in aggregateResponse.data) {
print('- ${result['_id']}: ${result['count']}');
}
} on HtHttpException catch (e) {
print('An error occurred: ${e.message}');
}
}
This package is source-available and licensed under the PolyForm Free Trial 1.0.0. Please review the terms before use.
For commercial licensing options that grant the right to build and distribute unlimited applications, please visit the main Flutter News App - Full Source Code Toolkit organization.