Skip to main content

Flutter Additions

If you are using flutter there are a couple of useful additions added.

To globally configure Cached Query Flutter use CachedQuery.configFlutter instead of config.

CachedQuery.instance.configFlutter(
storage: await CachedStorage.ensureInitialized(),
config: QueryConfig(),
);

Connection Monitoring

Cached query flutter uses the Connectivity Plus to monitor the connection status. If the connection changes from no-connection to valid connection Cached Query will ping example.com to verify the connection status. Any Query or Infinite Query that has listeners will be considered "active". Any active queries will be re-fetched if the connection is restored. Use the config to turn this off.

CachedQuery.instance.configFlutter(
config: QueryConfigFlutter(
refetchOnConnection: true,
),
);

This can be configured in the individual query.

Query(
key: "a query key",
queryFn: () async => _api.getData(),
config: QueryConfigFlutter(
refetchOnResume: false,
refetchOnConnection: true,
),
),

Refetch On Resume

Cached Query Flutter uses the WidgetsBindingObserver to monitor the lifecycle state of the app. If the app state is resumed any active queries will be re-fetched. Turn this off with the global flutter config.

CachedQuery.instance.configFlutter(
config: QueryConfigFlutter(
refetchOnResume: false,
refetchOnConnection: true,
),
);

This can be configured in the individual query.

Query(
key: "a query key",
queryFn: () async => _api.getData(),
config: QueryConfigFlutter(
refetchOnResume: false,
refetchOnConnection: true,
),
),

Builders

Three builders are added for ease of use. They act very similar to a StreamBuilder.

Enabling and Disabling

By default, the builders will subscribe to the query's listener on initState and unsubscribe on dispose. This means that the query will be fetched when the widget is first rendered. You can prevent this using the enabled flag on the InfiniteQueryBuilder and QueryBuilder.

danger

This will only prevent the widget from adding a listener to the query. If you have other listeners elsewhere then the query will still be fetched.

QueryBuilder

QueryBuilder takes a query and will call the builder method whenever the query state changes.

 QueryBuilder<DataModel?>(
enabled: false,
query: Query(
key: "a query key",
queryFn: () async => _api.getData(),
),
builder: (context, state) {
return Column(
children: [
if(state.status == QuerStatus.loading)
const CircularProgressIndicator(),
const DisplayData(data: state.data)
],
);
},
),

If you know that a query has already been instantiated then you can pass a key to the Query Builder instead, however this will fail if there is no query in the cache with that key.

 QueryBuilder<DataModel?>(
queryKey: "a query key",
builder: (context, state) {
return Column(
children: [
if(state.status == QuerStatus.loading)
const CircularProgressIndicator(),
const DisplayData(data: state.data)
],
);
},
),

InfiniteQueryBuilder

InfiniteQueryBuilder takes an infinite query and will call the builder method whenever the query state changes.

 InfiniteQueryBuilder<DataModel?>(
query: InfiniteQuery(
key: "a query key",
queryFn: () async => _api.getData(),
getNextArg: (state) {
if (state.lastPage?.isEmpty ?? false) return null;
return state.length + 1;
},
),
builder: (context, state) {
if(state.data == null) return SizedBox();
final allPosts = state.data!.expand((e) => e).toList();

return CustomScrollView(
slivers: [
if (state.status == QueryStatus.error &&
state.error is SocketException)
SliverToBoxAdapter(
child: DecoratedBox(
decoration:
BoxDecoration(color: Theme.of(context).errorColor),
child: const Text(
"No internet connection",
style: TextStyle(color: Colors.white),
textAlign: TextAlign.center,
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, i) => _Post(
post: allPosts[i],
index: i,
),
childCount: allPosts.length,
),
),
if (state.status == QueryStatus.loading)
const SliverToBoxAdapter(
child: Center(
child: SizedBox(
height: 40,
width: 40,
child: CircularProgressIndicator(),
),
),
),
SliverPadding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom,
),
)
],
);
},
)

Similar to the query builder you can also pass a key to the InfiniteQueryBuilder if you know there is a query available.

 InfiniteQueryBuilder<DataModel?>(
query: InfiniteQuery(
key: "a query key",
queryFn: () async => _api.getData(),
getNextArg: (state) {
if (state.lastPage?.isEmpty ?? false) return null;
return state.length + 1;
},
),
builder: (context, state) {
//...build ui
},
)

MutationBuilder

Much the same as the query builder. It will call the builder function when the mutation state changes.

 MutationBuilder<PostModel, PostModel>(
mutation: _postService.createPost(),
builder: (context, state, mutate) {
// Can use the mutate() function directly in the builder.
},
),