Skip to main content

Migrating from 2.x to 3.x

Environment Requirements

Cached Query 3.0 requires Dart 3.0.0 or higher. Make sure to update your pubspec.yaml:

environment:
sdk: ">=3.0.0 <4.0.0"

Query State

All query, infinite query and mutation states have been changed to sealed classes to take advantage of dart 3 pattern matching.

There is no longer a query status enum. There is now a matching sealed class, for exaple the query sealed class is:

class QueryInitial<T> extends QueryStatus<T> {}
class QueryLoading<T> extends QueryStatus<T> {}
class QuerySuccess<T> extends QueryStatus<T> {}
class QueryError<T> extends QueryStatus<T> {}

This change allows for more specific information to be passed with each state, such as the reason for the loading state or the data key being non-nullable in the success state.

For convenience you can check the current status of a query using:

final allStatuses = state.isLoading || state.isInitial || state.isError || state.isSuccess;

Example

class Post extends StatelessWidget {
final int id;
final bool enabled;

const Post({Key? key, required this.id, required this.enabled})
: super(key: key);


Widget build(BuildContext context) {
return QueryBuilder<QueryState<PostModel>>(
enabled: enabled,
// Can use key if the query already exists.
queryKey: service.postKey(id),
builder: (context, state) {
final data = state.data;
if (state case QueryError(:final error)) return Text(error.toString());
if (data == null) return const SizedBox();
return Container(
margin: const EdgeInsets.all(10),
child: Column(
children: [
const Text(
"Title",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 20),
),
Text(
data.title,
textAlign: TextAlign.center,
),
const Text(
"Body",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 20),
),
Text(
data.body,
textAlign: TextAlign.center,
),
],
),
);
},
);
}
}

The base class for each cachable item is:

  • QueryStatus - for single queries.
  • InfiniteQueryStatus - for infinite queries.
  • MutationState - for mutations

Configuration Changes

The configuration system has been updated with separate global and query-specific config objects.

Classes are now:

  • GlobalQueryConfigFlutter - Global config for all queries, infinite queries and mutations. If using Cached Query Flutter.
  • QueryConfigFlutter - Query specific config for queries and infinite queries. If using Cached Query Flutter.
  • GlobalQueryConfig - Global config for all queries, infinite queries and mutations. If using Cached Query (non-Flutter).
  • QueryConfig - Query specific config for queries and infinite queries. If using Cached Query (non-Flutter).

The local config will still inherit from the global config which is the same as before.

void main() async {
WidgetsFlutterBinding.ensureInitialized();
CachedQuery.instance.configFlutter(
config: GlobalQueryConfigFlutter(
refetchOnResume: true,
),
storage: await CachedStorage.ensureInitialized(),
observers: [
Observer(),
QueryLoggingObserver(colors: !Platform.isIOS),
],
);
runApp(const MyApp());
}

Multiple observers can now be passed to the global config.

Data Type Changes

Query success state data is now the type of the generic, usually this is a non-null type. If you need nullable data, explicitly pass T? as the generic type.

QueryBuilder<PostModel>(
queryKey: ['post', id],
queryFn: () => api.getPost(id),
builder: (context, state) {
return switch (state) {
QuerySuccess<PostModel>(:final data) => Text(data.title), // data is PostModel (non-nullable)
QueryError<PostModel>() => Text('Error: ${state.error}'),
_ => const SizedBox(),
};
},
)

For nullable data, use:

Query<PostModel?>(
...
)

Query Interface and Observers

The base class for queries is now Cachable<T>. This has changed from QueryBase<T>.

class Observer extends QueryObserver {

void onChange(
Cacheable<dynamic> query,
QueryState<dynamic> nextState,
) {
// Do something when changing
super.onChange(query, nextState);
}
}

Mutation

The queryFn parameter has been renamed to mutationFn in the Mutation class.