Skip to content

Commit 7c372b4

Browse files
committed
update README
1 parent a7c8492 commit 7c372b4

File tree

3 files changed

+248
-47
lines changed

3 files changed

+248
-47
lines changed

README.md

Lines changed: 247 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,17 @@
99

1010
## Persistent reactive models in Flutter with zero boilerplate
1111

12-
Flutter Data is an offline-first data framework with a customizable REST client and powerful model relationships.
12+
Flutter Data is an offline-first data framework with a customizable REST client and powerful model relationships, built on Riverpod.
1313

1414
<small>Inspired by [Ember Data](https://github.com/emberjs/data) and [ActiveRecord](https://guides.rubyonrails.org/active_record_basics.html).</small>
1515

1616
## Features
1717

18-
- **Repositories for all models** 🚀
19-
- CRUD and custom remote endpoints
18+
- **Adapters for all models** 🚀
19+
- Default CRUD and custom remote endpoints
2020
- [StateNotifier](https://pub.dev/packages/state_notifier) watcher APIs
2121
- **Built for offline-first** 🔌
22-
- [Hive](https://docs.hivedb.dev/)-based local storage at its core
22+
- [SQLite3](https://pub.dev/packages/sqlite3)-based local storage at its core, with adapters for many other engines: Objectbox, Isar, etc (coming soon)
2323
- Failure handling & retry API
2424
- **Intuitive APIs, effortless setup** 💙
2525
- Truly configurable and composable via Dart mixins and codegen
@@ -28,17 +28,15 @@ Flutter Data is an offline-first data framework with a customizable REST client
2828
- Automatically synchronized, fully traversable relationship graph
2929
- Reactive relationships
3030

31-
**Check out the [Tutorial](https://flutterdata.dev/tutorial) 📚 where we build a TO-DO app from the ground up in record time.**
31+
## 👩🏾‍💻 Quick introduction
3232

33-
## 👩🏾‍💻 Usage
33+
In Flutter Data, every model gets is adapter. These adapters can be extended by mixing in custom adapters.
3434

35-
(See the [quickstart guide](https://flutterdata.dev/docs/quickstart/) for setup and boot configuration.)
36-
37-
For a given `User` model annotated with `@DataRepository`:
35+
Annotate a model with `@DataAdapter` and pass a custom adapter:
3836

3937
```dart
4038
@JsonSerializable()
41-
@DataRepository([MyJSONServerAdapter])
39+
@DataAdapter([MyJSONServerAdapter])
4240
class User extends DataModel<User> {
4341
@override
4442
final int? id; // ID can be of any type
@@ -53,7 +51,7 @@ mixin MyJSONServerAdapter on RemoteAdapter<User> {
5351
}
5452
```
5553

56-
After a code-gen build, Flutter Data will generate a `Repository<User>` (and shortcuts like `ref.users` for Riverpod):
54+
After code-gen, Flutter Data will generate the resulting `Adapter<User>` which is accessible via Riverpod's `ref.users` or `container.users`.
5755

5856
```dart
5957
@override
@@ -67,7 +65,7 @@ Widget build(BuildContext context, WidgetRef ref) {
6765
}
6866
```
6967

70-
Update the user:
68+
To update the user:
7169

7270
```dart
7371
TextButton(
@@ -76,11 +74,11 @@ TextButton(
7674
),
7775
```
7876

79-
`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 changes to the `User` – whether those are local or remote!
77+
`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.
8078

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

83-
In addition to the reactivity, `DataModel`s get extensions and automatic relationships, ActiveRecord-style, so the above becomes:
81+
In addition to the reactivity, models have ActiveRecord-style extension methods so the above becomes:
8482

8583
```dart
8684
GestureDetector(
@@ -89,25 +87,6 @@ GestureDetector(
8987
),
9088
```
9189

92-
More examples:
93-
94-
```dart
95-
final task = await Task(title: 'Finish docs').save();
96-
// or its equivalent:
97-
final task = await ref.tasks.save(Task(title: 'Finish docs'));
98-
// POST https://my-json-server.typicode.com/flutterdata/demo/tasks/
99-
print(task.id); // 201
100-
101-
final user = await repository.findOne(1, params: {'_embed': 'tasks'});
102-
// (remember repository can be accessed via ref.users)
103-
// GET https://my-json-server.typicode.com/flutterdata/demo/users/1?_embed=tasks
104-
print(user.tasks.length); // 20
105-
106-
await user.tasks.last.delete();
107-
```
108-
109-
**Explore the [Documentation](https://flutterdata.dev/docs/).**
110-
11190
## Compatibility
11291

11392
Fully compatible with the tools we know and love:
@@ -124,12 +103,7 @@ Fully compatible with the tools we know and love:
124103
<tr>
125104
<td class="font-bold px-4 py-2"><strong>Flutter</strong></td>
126105
<td class="px-4 py-2">✅</td>
127-
<td class="px-4 py-2 text-sm">And pure Dart, too.</td>
128-
</tr>
129-
<tr class="bg-yellow-50">
130-
<td class="font-bold px-4 py-2"><strong>Flutter Web</strong></td>
131-
<td class="px-4 py-2">✅</td>
132-
<td class="px-4 py-2 text-sm">Supported (untested)</td>
106+
<td class="px-4 py-2 text-sm">Or plain Dart. It does not require Flutter.</td>
133107
</tr>
134108
<tr>
135109
<td class="font-bold px-4 py-2"><strong>json_serializable</strong></td>
@@ -155,13 +129,18 @@ Fully compatible with the tools we know and love:
155129
<tr class="bg-yellow-50">
156130
<td class="font-bold px-4 py-2"><strong>Firebase, Supabase, GraphQL</strong></td>
157131
<td class="px-4 py-2">✅</td>
158-
<td class="px-4 py-2 text-sm">Can be fully supported by writing <a href="https://flutterdata.dev/docs/adapters/">custom adapters</a></td>
132+
<td class="px-4 py-2 text-sm">Can be fully supported by writing custom adapters</a></td>
159133
</tr>
160134
<tr>
161135
<td class="font-bold px-4 py-2"><strong>Freezed</strong></td>
162136
<td class="px-4 py-2">✅</td>
163137
<td class="px-4 py-2 text-sm">Supported!</td>
164138
</tr>
139+
<tr class="bg-yellow-50">
140+
<td class="font-bold px-4 py-2"><strong>Flutter Web</strong></td>
141+
<td class="px-4 py-2">✅</td>
142+
<td class="px-4 py-2 text-sm">TBD</td>
143+
</tr>
165144
</tbody>
166145
</table>
167146

@@ -171,6 +150,234 @@ Fully compatible with the tools we know and love:
171150

172151
- [Drexbible](https://snapcraft.io/drexbible)
173152

153+
## 📚 API
154+
155+
### Adapters
156+
157+
WIP. Method names should be self explanatory. All of these methods have a reasonable default implementation.
158+
159+
#### Public API
160+
161+
```dart
162+
// local storage
163+
164+
List<T> findAllLocal();
165+
166+
List<T> findManyLocal(Iterable<String> keys);
167+
168+
List<T> deserializeFromResult(ResultSet result);
169+
170+
T? findOneLocal(String? key);
171+
172+
T? findOneLocalById(Object id);
173+
174+
bool exists(String key);
175+
176+
T saveLocal(T model, {bool notify = true});
177+
178+
Future<List<String>?> saveManyLocal(Iterable<DataModelMixin> models,
179+
{bool notify = true, bool async = true});
180+
181+
void deleteLocal(T model, {bool notify = true});
182+
183+
void deleteLocalById(Object id, {bool notify = true});
184+
185+
void deleteLocalByKeys(Iterable<String> keys, {bool notify = true});
186+
187+
Future<void> clearLocal({bool notify = true});
188+
189+
int get countLocal;
190+
191+
Set<String> get keys;
192+
193+
// remote
194+
195+
Future<List<T>> findAll({
196+
bool remote = true,
197+
bool background = false,
198+
Map<String, dynamic>? params,
199+
Map<String, String>? headers,
200+
bool syncLocal = false,
201+
OnSuccessAll<T>? onSuccess,
202+
OnErrorAll<T>? onError,
203+
DataRequestLabel? label,
204+
});
205+
206+
Future<T?> findOne(
207+
Object id, {
208+
bool remote = true,
209+
bool? background,
210+
Map<String, dynamic>? params,
211+
Map<String, String>? headers,
212+
OnSuccessOne<T>? onSuccess,
213+
OnErrorOne<T>? onError,
214+
DataRequestLabel? label,
215+
});
216+
217+
Future<T> save(
218+
T model, {
219+
bool remote = true,
220+
Map<String, dynamic>? params,
221+
Map<String, String>? headers,
222+
OnSuccessOne<T>? onSuccess,
223+
OnErrorOne<T>? onError,
224+
DataRequestLabel? label,
225+
});
226+
227+
Future<T?> delete(
228+
Object model, {
229+
bool remote = true,
230+
Map<String, dynamic>? params,
231+
Map<String, String>? headers,
232+
OnSuccessOne<T>? onSuccess,
233+
OnErrorOne<T>? onError,
234+
DataRequestLabel? label,
235+
});
236+
237+
Set<OfflineOperation<T>> get offlineOperations;
238+
239+
// serialization
240+
241+
Map<String, dynamic> serializeLocal(T model, {bool withRelationships = true});
242+
243+
T deserializeLocal(Map<String, dynamic> map, {String? key});
244+
245+
Future<Map<String, dynamic>> serialize(T model,
246+
{bool withRelationships = true});
247+
248+
Future<DeserializedData<T>> deserialize(Object? data,
249+
{String? key, bool async = true});
250+
251+
Future<DeserializedData<T>> deserializeAndSave(Object? data,
252+
{String? key, bool notify = true, bool ignoreReturn = false});
253+
254+
// watchers
255+
256+
DataState<List<T>> watchAll({
257+
bool remote = false,
258+
Map<String, dynamic>? params,
259+
Map<String, String>? headers,
260+
bool syncLocal = false,
261+
String? finder,
262+
DataRequestLabel? label,
263+
});
264+
265+
DataState<T?> watchOne(
266+
Object model, {
267+
bool remote = false,
268+
Map<String, dynamic>? params,
269+
Map<String, String>? headers,
270+
AlsoWatch<T>? alsoWatch,
271+
String? finder,
272+
DataRequestLabel? label,
273+
});
274+
275+
DataStateNotifier<List<T>> watchAllNotifier(
276+
{bool remote = false,
277+
Map<String, dynamic>? params,
278+
Map<String, String>? headers,
279+
bool syncLocal = false,
280+
String? finder,
281+
DataRequestLabel? label});
282+
283+
DataStateNotifier<T?> watchOneNotifier(Object model,
284+
{bool remote = false,
285+
Map<String, dynamic>? params,
286+
Map<String, String>? headers,
287+
AlsoWatch<T>? alsoWatch,
288+
String? finder,
289+
DataRequestLabel? label});
290+
291+
final coreNotifierThrottleDurationProvider;
292+
```
293+
294+
#### Protected API
295+
296+
```dart
297+
// adapter
298+
299+
Future<void> onInitialized();
300+
301+
Future<Adapter<T>> initialize({required Ref ref});
302+
303+
void dispose();
304+
305+
Future<R> runInIsolate<R>(FutureOr<R> fn(Adapter adapter));
306+
307+
void log(DataRequestLabel label, String message, {int logLevel = 1});
308+
309+
void onModelInitialized(T model) {};
310+
311+
// remote
312+
313+
String get baseUrl;
314+
315+
FutureOr<Map<String, dynamic>> get defaultParams;
316+
317+
FutureOr<Map<String, String>> get defaultHeaders;
318+
319+
String urlForFindAll(Map<String, dynamic> params);
320+
321+
DataRequestMethod methodForFindAll(Map<String, dynamic> params);
322+
323+
String urlForFindOne(id, Map<String, dynamic> params);
324+
325+
DataRequestMethod methodForFindOne(id, Map<String, dynamic> params);
326+
327+
String urlForSave(id, Map<String, dynamic> params);
328+
329+
DataRequestMethod methodForSave(id, Map<String, dynamic> params);
330+
331+
String urlForDelete(id, Map<String, dynamic> params);
332+
333+
DataRequestMethod methodForDelete(id, Map<String, dynamic> params);
334+
335+
bool shouldLoadRemoteAll(
336+
bool remote,
337+
Map<String, dynamic> params,
338+
Map<String, String> headers,
339+
);
340+
341+
bool shouldLoadRemoteOne(
342+
Object? id,
343+
bool remote,
344+
Map<String, dynamic> params,
345+
Map<String, String> headers,
346+
);
347+
348+
bool isOfflineError(Object? error);
349+
350+
http.Client get httpClient;
351+
352+
Future<R?> sendRequest<R>(
353+
final Uri uri, {
354+
DataRequestMethod method = DataRequestMethod.GET,
355+
Map<String, String>? headers,
356+
Object? body,
357+
_OnSuccessGeneric<R>? onSuccess,
358+
_OnErrorGeneric<R>? onError,
359+
bool omitDefaultParams = false,
360+
bool returnBytes = false,
361+
DataRequestLabel? label,
362+
bool closeClientAfterRequest = true,
363+
});
364+
365+
FutureOr<R?> onSuccess<R>(
366+
DataResponse response, DataRequestLabel label);
367+
368+
FutureOr<R?> onError<R>(
369+
DataException e,
370+
DataRequestLabel? label,
371+
);
372+
373+
// serialization
374+
375+
Map<String, dynamic> transformSerialize(Map<String, dynamic> map,
376+
{bool withRelationships = true});
377+
378+
Map<String, dynamic> transformDeserialize(Map<String, dynamic> map);
379+
```
380+
174381
## ➕ Questions and collaborating
175382

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

lib/src/adapter/remote_adapter.dart

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -574,12 +574,6 @@ mixin _RemoteAdapter<T extends DataModelMixin<T>> on _SerializationAdapter<T> {
574574
throw e;
575575
}
576576

577-
Future<void> _saveDeserialized(DeserializedData deserialized) async {
578-
final models = [...deserialized.models, ...deserialized.included];
579-
if (models.isEmpty) return;
580-
await saveManyLocal(models.cast());
581-
}
582-
583577
/// Returns URL for [findAll]. Defaults to [type].
584578
@protected
585579
String urlForFindAll(Map<String, dynamic> params) => type;

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description: >
44
Ember Data and ActiveRecord.
55
version: 2.0.0-dev.1
66
homepage: https://github.com/flutterdata/flutter_data
7-
documentation: https://flutterdata.dev/
7+
documentation: https://github.com/flutterdata/flutter_data
88

99
environment:
1010
sdk: ">=3.1.0 <4.0.0"

0 commit comments

Comments
 (0)