Quickstart

Add flutter_data and dependencies to your pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter

  flutter_data: ^1.5.6

  # Highly RECOMMENDED (but not required) packages
  path_provider: ^2.0.11
  json_annotation: ^4.7.0
  hooks_riverpod: ^2.1.1

dev_dependencies:
  build_runner: ^2.2.0 # REQUIRED!

  # Highly RECOMMENDED (but not required) packages
  json_serializable: ^6.4.1

Flutter Data doesn’t require any library besides build_runner for code generation.

However, json_serializable and path_provider are very convenient so they are recommended.

The latest flutter_data should be 1.5.6. Please check for all packages latest stable versions before copy-pasting dependencies.

On Riverpod

This package is developed for Riverpod, specifically Riverpod 2.x Hooks. Other libraries such as Provider or GetIt might work but there are no guarantees.

Basic configuration 🔧

Make your models extend DataModel<T>, override id and annotate them with @DataRepository().

import 'package:flutter_data/flutter_data.dart';
import 'package:json_annotation/json_annotation.dart';

part 'task.g.dart';

@JsonSerializable()
@DataRepository([])
class Task extends DataModel<Task> {
  @override
  final int? id;
  final String title;
  final bool completed;

  Task({this.id, required this.title, this.completed = false});
}

@DataRepository() takes a list of adapters.

Adapters are Dart mixins used to customize the framework’s behavior, ranging from the very basic to the extremely powerful. They are applied on Flutter Data’s RemoteAdapter<T> base class.

Let’s start by the most typical configuration to access a remote API, the base URL.

mixin JsonServerAdapter<T extends DataModel<T>> on RemoteAdapter<T> {
  @override
  String get baseUrl => 'https://my-json-server.typicode.com/flutterdata/demo/';
}

Next, we’ll pass it to the annotation:

@JsonSerializable()
@DataRepository([JsonServerAdapter])
class Task extends DataModel<Task> {
  final int? id;
  final String title;
  final bool completed;

  Task({this.id, required this.title, this.completed = false});
}

Notice two things about our model above:

  • We used int? to represent the actual type of the id identifier field as it is null when new (it could have been a String too)
  • The fromJson and toJson functions were skipped as they are not required (Flutter Data will automatically use _$TaskFromJson and _$TaskToJson generated by json_serializable – but they can both be overridden)

Default serialization

Flutter Data ships with a built-in serializer/deserializer for classic JSON.

A Task instance in JSON would look like:

{
  "id": 1,
  "title": "Finish this documentation for once",
  "completed": false,
  "userId": 1
}

We are now ready to run a build:

flutter pub run build_runner build

Flutter Data auto-generated a Repository class for Task.

It also generated a Dart library at main.data.dart which makes Flutter Data initialization effortless. It’s out-of-the-box compatible with Riverpod.

Trouble generating code? See here.

Here is how to make it work with Provider and GetIt.

Next step is to configure local storage and initialize the framework:

import 'package:flutter/material.dart';
import 'package:flutter_data/flutter_data.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:tutorial/main.data.dart';

void main() {
  runApp(
    ProviderScope(
      child: TasksApp(),
      overrides: [configureRepositoryLocalStorage()],
    ),
  );
}

class TasksApp extends HookConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: ref.watch(repositoryInitializerProvider).when(
                error: (error, _) => Text(error.toString()),
                loading: () => const CircularProgressIndicator(),
                data: (_) => Text('Hello from Flutter Data ${ref.tasks}!'),
              ),
        ),
      ),
      debugShowCheckedModeBanner: false,
    );
  }
}

Once the data callback is invoked, Flutter Data is ready and the Task repository can be accessed via ref.tasks!

The configureRepositoryLocalStorage setup function has several optional arguments.

If you do not have path_provider as a dependency you will have to supply baseDirFn (a function that returns a base directory for local storage).

For more information see initialization.

Prefer a setup example? Here’s the sample setup app with support for Riverpod, Provider and get_it.

âž¡ Continue with the tutorial for a Tasks app or learn more about Repositories