feat: add support for keepPreviousData & examples regarding this
This commit is contained in:
@@ -1 +1 @@
|
|||||||
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity_plus","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus-2.3.5/","native_build":true,"dependencies":[]}],"android":[{"name":"connectivity_plus","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus-2.3.5/","native_build":true,"dependencies":[]}],"macos":[{"name":"connectivity_plus_macos","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_macos-1.2.4/","native_build":true,"dependencies":[]}],"linux":[{"name":"connectivity_plus_linux","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_linux-1.3.1/","native_build":false,"dependencies":[]}],"windows":[{"name":"connectivity_plus_windows","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_windows-1.2.2/","native_build":true,"dependencies":[]}],"web":[{"name":"connectivity_plus_web","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_web-1.2.2/","dependencies":[]}]},"dependencyGraph":[{"name":"connectivity_plus","dependencies":["connectivity_plus_linux","connectivity_plus_macos","connectivity_plus_web","connectivity_plus_windows"]},{"name":"connectivity_plus_linux","dependencies":[]},{"name":"connectivity_plus_macos","dependencies":[]},{"name":"connectivity_plus_web","dependencies":[]},{"name":"connectivity_plus_windows","dependencies":[]}],"date_created":"2022-08-04 09:09:34.759333","version":"3.0.1"}
|
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity_plus","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus-2.3.5/","native_build":true,"dependencies":[]}],"android":[{"name":"connectivity_plus","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus-2.3.5/","native_build":true,"dependencies":[]}],"macos":[{"name":"connectivity_plus_macos","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_macos-1.2.4/","native_build":true,"dependencies":[]}],"linux":[{"name":"connectivity_plus_linux","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_linux-1.3.1/","native_build":false,"dependencies":[]}],"windows":[{"name":"connectivity_plus_windows","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_windows-1.2.2/","native_build":true,"dependencies":[]}],"web":[{"name":"connectivity_plus_web","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_web-1.2.2/","dependencies":[]}]},"dependencyGraph":[{"name":"connectivity_plus","dependencies":["connectivity_plus_linux","connectivity_plus_macos","connectivity_plus_web","connectivity_plus_windows"]},{"name":"connectivity_plus_linux","dependencies":[]},{"name":"connectivity_plus_macos","dependencies":[]},{"name":"connectivity_plus_web","dependencies":[]},{"name":"connectivity_plus_windows","dependencies":[]}],"date_created":"2022-08-07 10:53:50.758755","version":"3.0.1"}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
final todoJob = QueryJob.withVariableKey<Map, void>(
|
||||||
|
preQueryKey: "todo",
|
||||||
|
task: (queryKey, _) async {
|
||||||
|
final res = await http.get(
|
||||||
|
Uri.parse(
|
||||||
|
"https://jsonplaceholder.typicode.com/todos/${getVariable(queryKey)}"),
|
||||||
|
);
|
||||||
|
return jsonDecode(res.body);
|
||||||
|
},
|
||||||
|
keepPreviousData: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
class QueryPreviousDataExample extends StatefulWidget {
|
||||||
|
const QueryPreviousDataExample({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<QueryPreviousDataExample> createState() =>
|
||||||
|
_QueryPreviousDataExampleState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _QueryPreviousDataExampleState extends State<QueryPreviousDataExample> {
|
||||||
|
int id = 1;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"# Query Variable Key with keepPreviousData",
|
||||||
|
style: Theme.of(context).textTheme.headline5,
|
||||||
|
),
|
||||||
|
QueryBuilder(
|
||||||
|
job: todoJob(id.toString()),
|
||||||
|
externalData: null,
|
||||||
|
builder: (context, query) {
|
||||||
|
if (query.hasError) return Text(query.error.toString());
|
||||||
|
if (!query.hasData) return const CircularProgressIndicator();
|
||||||
|
return Text(jsonEncode(query.data ?? {}));
|
||||||
|
}),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.remove),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
id -= 1;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
id += 1;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import 'package:fl_query_example/components/basic_query.dart';
|
|||||||
import 'package:fl_query_example/components/lazy_query.dart';
|
import 'package:fl_query_example/components/lazy_query.dart';
|
||||||
import 'package:fl_query_example/components/mutation_variable_key.dart';
|
import 'package:fl_query_example/components/mutation_variable_key.dart';
|
||||||
import 'package:fl_query_example/components/query_external_data.dart';
|
import 'package:fl_query_example/components/query_external_data.dart';
|
||||||
|
import 'package:fl_query_example/components/query_previous_data.dart';
|
||||||
import 'package:fl_query_example/components/query_variable_key.dart';
|
import 'package:fl_query_example/components/query_variable_key.dart';
|
||||||
import 'package:fl_query/fl_query.dart';
|
import 'package:fl_query/fl_query.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -53,6 +54,7 @@ class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
|
|||||||
QueryExternalDataExample(),
|
QueryExternalDataExample(),
|
||||||
LazyQueryExample(),
|
LazyQueryExample(),
|
||||||
QueryVariableKeyExample(),
|
QueryVariableKeyExample(),
|
||||||
|
QueryPreviousDataExample(),
|
||||||
Divider(),
|
Divider(),
|
||||||
BasicMutationExample(),
|
BasicMutationExample(),
|
||||||
MutationVariableKeyExample(),
|
MutationVariableKeyExample(),
|
||||||
|
|||||||
@@ -7,4 +7,4 @@ export 'src/mutation.dart';
|
|||||||
export 'src/mutation_builder.dart';
|
export 'src/mutation_builder.dart';
|
||||||
export 'src/models/query_job.dart';
|
export 'src/models/query_job.dart';
|
||||||
export 'src/models/mutation_job.dart';
|
export 'src/models/mutation_job.dart';
|
||||||
export 'src/utils.dart' show isShallowEqual;
|
export 'src/utils.dart' show isShallowEqual, getVariable;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||||
import 'package:fl_query/src/query.dart';
|
import 'package:fl_query/src/query.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
class QueryJob<T extends Object, Outside> {
|
class QueryJob<T extends Object, Outside> {
|
||||||
// all params
|
// all params
|
||||||
@@ -7,21 +8,25 @@ class QueryJob<T extends Object, Outside> {
|
|||||||
QueryTaskFunction<T, Outside> task;
|
QueryTaskFunction<T, Outside> task;
|
||||||
final int? retries;
|
final int? retries;
|
||||||
final Duration? retryDelay;
|
final Duration? retryDelay;
|
||||||
final T? initialData;
|
T? initialData;
|
||||||
|
|
||||||
/// If set to false then the initial fetch will not be called & to
|
/// If set to false then the initial fetch will not be called & to
|
||||||
/// start the process the user has to call the refetch first
|
/// start the process the user has to call the refetch first
|
||||||
final bool? enabled;
|
final bool? enabled;
|
||||||
|
|
||||||
// got from global options
|
// got from global options
|
||||||
bool? refetchOnMount;
|
final bool? refetchOnMount;
|
||||||
bool? refetchOnReconnect;
|
final bool? refetchOnReconnect;
|
||||||
bool? refetchOnExternalDataChange;
|
final bool? refetchOnExternalDataChange;
|
||||||
Duration? staleTime;
|
final bool? keepPreviousData;
|
||||||
Duration? cacheTime;
|
final Duration? staleTime;
|
||||||
|
final Duration? cacheTime;
|
||||||
|
|
||||||
Duration? refetchInterval;
|
final Duration? refetchInterval;
|
||||||
Connectivity? connectivity;
|
final Connectivity? connectivity;
|
||||||
|
|
||||||
|
@protected
|
||||||
|
bool isDynamic = false;
|
||||||
|
|
||||||
QueryJob({
|
QueryJob({
|
||||||
required String queryKey,
|
required String queryKey,
|
||||||
@@ -37,6 +42,7 @@ class QueryJob<T extends Object, Outside> {
|
|||||||
this.refetchOnReconnect,
|
this.refetchOnReconnect,
|
||||||
this.refetchOnExternalDataChange,
|
this.refetchOnExternalDataChange,
|
||||||
this.connectivity,
|
this.connectivity,
|
||||||
|
this.keepPreviousData,
|
||||||
}) : _queryKey = queryKey;
|
}) : _queryKey = queryKey;
|
||||||
|
|
||||||
String get queryKey => _queryKey;
|
String get queryKey => _queryKey;
|
||||||
@@ -60,10 +66,11 @@ class QueryJob<T extends Object, Outside> {
|
|||||||
bool? refetchOnReconnect,
|
bool? refetchOnReconnect,
|
||||||
bool? refetchOnExternalDataChange,
|
bool? refetchOnExternalDataChange,
|
||||||
Connectivity? connectivity,
|
Connectivity? connectivity,
|
||||||
|
bool? keepPreviousData,
|
||||||
}) {
|
}) {
|
||||||
return (String queryKey) {
|
return (String queryKey) {
|
||||||
if (preQueryKey != null) queryKey = "$preQueryKey#$queryKey";
|
if (preQueryKey != null) queryKey = "$preQueryKey#$queryKey";
|
||||||
return QueryJob<T, Outside>(
|
final query = QueryJob<T, Outside>(
|
||||||
queryKey: queryKey,
|
queryKey: queryKey,
|
||||||
task: task,
|
task: task,
|
||||||
retries: retries,
|
retries: retries,
|
||||||
@@ -77,7 +84,10 @@ class QueryJob<T extends Object, Outside> {
|
|||||||
refetchOnReconnect: refetchOnReconnect,
|
refetchOnReconnect: refetchOnReconnect,
|
||||||
refetchOnExternalDataChange: refetchOnExternalDataChange,
|
refetchOnExternalDataChange: refetchOnExternalDataChange,
|
||||||
connectivity: connectivity,
|
connectivity: connectivity,
|
||||||
|
keepPreviousData: keepPreviousData,
|
||||||
);
|
);
|
||||||
|
query.isDynamic = true;
|
||||||
|
return query;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ class Query<T extends Object, Outside> extends BaseOperation<T> {
|
|||||||
|
|
||||||
Connectivity _connectivity;
|
Connectivity _connectivity;
|
||||||
|
|
||||||
|
T? _previousData;
|
||||||
|
|
||||||
Query({
|
Query({
|
||||||
required this.queryKey,
|
required this.queryKey,
|
||||||
required this.task,
|
required this.task,
|
||||||
@@ -85,6 +87,7 @@ class Query<T extends Object, Outside> extends BaseOperation<T> {
|
|||||||
this.refetchOnReconnect,
|
this.refetchOnReconnect,
|
||||||
this.refetchInterval,
|
this.refetchInterval,
|
||||||
this.enabled = true,
|
this.enabled = true,
|
||||||
|
T? previousData,
|
||||||
Connectivity? connectivity,
|
Connectivity? connectivity,
|
||||||
T? initialData,
|
T? initialData,
|
||||||
QueryListener<T>? onData,
|
QueryListener<T>? onData,
|
||||||
@@ -92,9 +95,10 @@ class Query<T extends Object, Outside> extends BaseOperation<T> {
|
|||||||
}) : _staleTime = staleTime,
|
}) : _staleTime = staleTime,
|
||||||
_initialData = initialData,
|
_initialData = initialData,
|
||||||
_externalData = externalData,
|
_externalData = externalData,
|
||||||
status = QueryStatus.idle,
|
status = previousData == null ? QueryStatus.idle : QueryStatus.success,
|
||||||
_connectivity = connectivity ?? Connectivity(),
|
_connectivity = connectivity ?? Connectivity(),
|
||||||
super(data: initialData) {
|
_previousData = previousData,
|
||||||
|
super(data: previousData ?? initialData) {
|
||||||
if (onData != null) _onDataListeners.add(onData);
|
if (onData != null) _onDataListeners.add(onData);
|
||||||
if (onError != null) _onErrorListeners.add(onError);
|
if (onError != null) _onErrorListeners.add(onError);
|
||||||
|
|
||||||
@@ -107,6 +111,7 @@ class Query<T extends Object, Outside> extends BaseOperation<T> {
|
|||||||
QueryJob<T, Outside> options, {
|
QueryJob<T, Outside> options, {
|
||||||
required super.queryBowl,
|
required super.queryBowl,
|
||||||
required Outside externalData,
|
required Outside externalData,
|
||||||
|
T? previousData,
|
||||||
QueryListener<T>? onData,
|
QueryListener<T>? onData,
|
||||||
QueryListener<dynamic>? onError,
|
QueryListener<dynamic>? onError,
|
||||||
}) : queryKey = options.queryKey,
|
}) : queryKey = options.queryKey,
|
||||||
@@ -118,13 +123,14 @@ class Query<T extends Object, Outside> extends BaseOperation<T> {
|
|||||||
refetchInterval = options.refetchInterval,
|
refetchInterval = options.refetchInterval,
|
||||||
refetchOnMount = options.refetchOnMount,
|
refetchOnMount = options.refetchOnMount,
|
||||||
refetchOnReconnect = options.refetchOnReconnect,
|
refetchOnReconnect = options.refetchOnReconnect,
|
||||||
status = QueryStatus.idle,
|
status = previousData == null ? QueryStatus.idle : QueryStatus.success,
|
||||||
_connectivity = options.connectivity ?? Connectivity(),
|
_connectivity = options.connectivity ?? Connectivity(),
|
||||||
|
_previousData = previousData,
|
||||||
super(
|
super(
|
||||||
cacheTime: options.cacheTime ?? const Duration(minutes: 5),
|
cacheTime: options.cacheTime ?? const Duration(minutes: 5),
|
||||||
retries: options.retries ?? 3,
|
retries: options.retries ?? 3,
|
||||||
retryDelay: options.retryDelay ?? const Duration(milliseconds: 200),
|
retryDelay: options.retryDelay ?? const Duration(milliseconds: 200),
|
||||||
data: options.initialData,
|
data: previousData ?? options.initialData,
|
||||||
) {
|
) {
|
||||||
if (onData != null) _onDataListeners.add(onData);
|
if (onData != null) _onDataListeners.add(onData);
|
||||||
if (onError != null) _onErrorListeners.add(onError);
|
if (onError != null) _onErrorListeners.add(onError);
|
||||||
@@ -222,12 +228,20 @@ class Query<T extends Object, Outside> extends BaseOperation<T> {
|
|||||||
_onErrorListeners.remove(listener);
|
_onErrorListeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// fetches data or runs the provided task initially
|
||||||
|
///
|
||||||
|
/// Once [data] is available it won't run the [task] ever again
|
||||||
|
/// and will only return the available data
|
||||||
|
///
|
||||||
|
/// If a [fetch] is already running in the background it'll just return
|
||||||
|
/// the current available [data] (which can be nul if no [initialData]
|
||||||
|
/// was provided) instead of running the task to prevent race conditions
|
||||||
Future<T?> fetch() async {
|
Future<T?> fetch() async {
|
||||||
if (!enabled) return null;
|
if (!enabled) return null;
|
||||||
|
|
||||||
/// if isLoading/isRefetching is true that means its already fetching/
|
/// if isLoading/isRefetching is true that means its already fetching/
|
||||||
/// refetching. So [_execute] again can create a race condition
|
/// refetching. So [_execute] again can create a race condition
|
||||||
if (isLoading || isRefetching || hasData) return data;
|
if (isLoading || isRefetching || (hasData && !isPreviousData)) return data;
|
||||||
status = QueryStatus.loading;
|
status = QueryStatus.loading;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
return _execute().then((_) {
|
return _execute().then((_) {
|
||||||
@@ -236,6 +250,18 @@ class Query<T extends Object, Outside> extends BaseOperation<T> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// refetches a valid or invalid [Query]
|
||||||
|
///
|
||||||
|
/// When called before calling [fetch] in a [Query] it'll
|
||||||
|
/// automatically run [fetch]
|
||||||
|
///
|
||||||
|
/// But if it's used to fetch the first data of a non-enabled [Query]
|
||||||
|
/// aka `LazyQuery`, it'll execute the task & will set the status
|
||||||
|
/// `enabled=true`
|
||||||
|
///
|
||||||
|
/// If a [refetch] is already running in the background it'll just return
|
||||||
|
/// the current available [data] instead of running the task to prevent
|
||||||
|
/// race conditions
|
||||||
Future<T?> refetch() async {
|
Future<T?> refetch() async {
|
||||||
/// if isLoading/isRefetching is true that means its already fetching/
|
/// if isLoading/isRefetching is true that means its already fetching/
|
||||||
/// refetching. So [_execute] again can create a race condition
|
/// refetching. So [_execute] again can create a race condition
|
||||||
@@ -266,14 +292,22 @@ class Query<T extends Object, Outside> extends BaseOperation<T> {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the [externalData] from outside of the query
|
||||||
|
///
|
||||||
|
/// Remember, it's for the very instance of [Query]
|
||||||
|
/// So this won't persist through later UI/[Query] updates
|
||||||
void setExternalData(Outside externalData) {
|
void setExternalData(Outside externalData) {
|
||||||
_prevUsedExternalData = _externalData;
|
_prevUsedExternalData = _externalData;
|
||||||
_externalData = externalData;
|
_externalData = externalData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resets the query
|
||||||
|
///
|
||||||
|
/// The values of internal state of the query are reset to the
|
||||||
|
/// initial ones
|
||||||
void reset() {
|
void reset() {
|
||||||
refetchCount = 0;
|
refetchCount = 0;
|
||||||
data = _initialData;
|
data = _previousData ?? _initialData;
|
||||||
error = null;
|
error = null;
|
||||||
fetched = false;
|
fetched = false;
|
||||||
status = QueryStatus.idle;
|
status = QueryStatus.idle;
|
||||||
@@ -283,8 +317,12 @@ class Query<T extends Object, Outside> extends BaseOperation<T> {
|
|||||||
mounts.clear();
|
mounts.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update configurations of the query after already creating the Query
|
/// Update configurations of the query
|
||||||
/// instance
|
/// after already creating the Query instance
|
||||||
|
///
|
||||||
|
/// Remember, it's just for the single query instance
|
||||||
|
/// In the next UI update/render the options will get reset
|
||||||
|
/// to the default ones defined in the [QueryJob] or [QueryBowlScope]
|
||||||
void updateDefaultOptions({
|
void updateDefaultOptions({
|
||||||
Duration? refetchInterval,
|
Duration? refetchInterval,
|
||||||
Duration? staleTime,
|
Duration? staleTime,
|
||||||
@@ -310,11 +348,20 @@ class Query<T extends Object, Outside> extends BaseOperation<T> {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// checks if the application is connected to internet in any mean
|
||||||
|
///
|
||||||
|
/// It's true when any one this is connected -
|
||||||
|
/// - ethernet
|
||||||
|
/// - mobile
|
||||||
|
/// - wifi
|
||||||
Future<bool> isInternetConnected() async {
|
Future<bool> isInternetConnected() async {
|
||||||
return isConnectedToInternet(await _connectivity.checkConnectivity());
|
return isConnectedToInternet(await _connectivity.checkConnectivity());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// invalidates the query
|
/// invalidates the query
|
||||||
|
///
|
||||||
|
/// Forcefully makes the query stale & expired which results in a refetch
|
||||||
|
/// when met conditions
|
||||||
void invalidate() {
|
void invalidate() {
|
||||||
/// subtracting [staleTime] from [updatedAt] as staleTime=Duration.zero
|
/// subtracting [staleTime] from [updatedAt] as staleTime=Duration.zero
|
||||||
/// indicates the query must never become stale but subtracting the
|
/// indicates the query must never become stale but subtracting the
|
||||||
@@ -340,6 +387,9 @@ class Query<T extends Object, Outside> extends BaseOperation<T> {
|
|||||||
bool get isLoading => status == QueryStatus.loading;
|
bool get isLoading => status == QueryStatus.loading;
|
||||||
bool get isRefetching => status == QueryStatus.refetching;
|
bool get isRefetching => status == QueryStatus.refetching;
|
||||||
bool get isSuccess => status == QueryStatus.success;
|
bool get isSuccess => status == QueryStatus.success;
|
||||||
|
bool get isPreviousData {
|
||||||
|
return _previousData != null ? _previousData == data : false;
|
||||||
|
}
|
||||||
|
|
||||||
A? cast<A>() => this is A ? this as A : null;
|
A? cast<A>() => this is A ? this as A : null;
|
||||||
|
|
||||||
@@ -348,6 +398,10 @@ class Query<T extends Object, Outside> extends BaseOperation<T> {
|
|||||||
@override
|
@override
|
||||||
void mount(ValueKey<String> uKey) {
|
void mount(ValueKey<String> uKey) {
|
||||||
super.mount(uKey);
|
super.mount(uKey);
|
||||||
|
|
||||||
|
/// refetching on mount if it's set to true
|
||||||
|
/// also checking if the is stale or not
|
||||||
|
/// no need to refetch a valid query for no reason
|
||||||
if (refetchOnMount == true && isStale) {
|
if (refetchOnMount == true && isStale) {
|
||||||
this.isInternetConnected().then((isConnected) async {
|
this.isInternetConnected().then((isConnected) async {
|
||||||
if (isConnected) await refetch();
|
if (isConnected) await refetch();
|
||||||
|
|||||||
@@ -284,11 +284,13 @@ class QueryBowl extends InheritedWidget {
|
|||||||
|
|
||||||
Query<T, Outside> _createQueryWithDefaults<T extends Object, Outside>(
|
Query<T, Outside> _createQueryWithDefaults<T extends Object, Outside>(
|
||||||
QueryJob<T, Outside> options,
|
QueryJob<T, Outside> options,
|
||||||
Outside externalData,
|
Outside externalData, [
|
||||||
) {
|
T? previousData,
|
||||||
|
]) {
|
||||||
final query = Query<T, Outside>.fromOptions(
|
final query = Query<T, Outside>.fromOptions(
|
||||||
options,
|
options,
|
||||||
externalData: externalData,
|
externalData: externalData,
|
||||||
|
previousData: previousData,
|
||||||
queryBowl: this,
|
queryBowl: this,
|
||||||
);
|
);
|
||||||
query.updateDefaultOptions(
|
query.updateDefaultOptions(
|
||||||
@@ -361,6 +363,7 @@ class QueryBowl extends InheritedWidget {
|
|||||||
required ValueKey<String> key,
|
required ValueKey<String> key,
|
||||||
final QueryListener<T>? onData,
|
final QueryListener<T>? onData,
|
||||||
final QueryListener<dynamic>? onError,
|
final QueryListener<dynamic>? onError,
|
||||||
|
final T? previousData,
|
||||||
}) {
|
}) {
|
||||||
final prevQuery =
|
final prevQuery =
|
||||||
_queries.firstWhereOrNull((q) => q.queryKey == queryJob.queryKey);
|
_queries.firstWhereOrNull((q) => q.queryKey == queryJob.queryKey);
|
||||||
@@ -381,7 +384,11 @@ class QueryBowl extends InheritedWidget {
|
|||||||
// mounting the widget that is using the query in the prevQuery
|
// mounting the widget that is using the query in the prevQuery
|
||||||
return prevQuery;
|
return prevQuery;
|
||||||
}
|
}
|
||||||
final query = _createQueryWithDefaults<T, Outside>(queryJob, externalData);
|
final query = _createQueryWithDefaults<T, Outside>(
|
||||||
|
queryJob,
|
||||||
|
externalData,
|
||||||
|
previousData,
|
||||||
|
);
|
||||||
if (onData != null) query.addDataListener(onData);
|
if (onData != null) query.addDataListener(onData);
|
||||||
if (onError != null) query.addErrorListener(onError);
|
if (onError != null) query.addErrorListener(onError);
|
||||||
query.mount(key);
|
query.mount(key);
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// ignore_for_file: invalid_use_of_protected_member
|
||||||
|
|
||||||
import 'package:fl_query/src/models/query_job.dart';
|
import 'package:fl_query/src/models/query_job.dart';
|
||||||
import 'package:fl_query/src/query.dart';
|
import 'package:fl_query/src/query.dart';
|
||||||
import 'package:fl_query/src/query_bowl.dart';
|
import 'package:fl_query/src/query_bowl.dart';
|
||||||
@@ -43,11 +45,12 @@ class _QueryBuilderState<T extends Object, Outside>
|
|||||||
WidgetsBinding.instance.addPostFrameCallback((_) => init());
|
WidgetsBinding.instance.addPostFrameCallback((_) => init());
|
||||||
}
|
}
|
||||||
|
|
||||||
void init([QueryBowl? bowl]) async {
|
void init([T? previousData]) async {
|
||||||
bowl ??= QueryBowl.of(context);
|
final bowl = QueryBowl.of(context);
|
||||||
query = bowl.addQuery<T, Outside>(
|
query = bowl.addQuery<T, Outside>(
|
||||||
widget.job,
|
widget.job,
|
||||||
externalData: widget.externalData,
|
externalData: widget.externalData,
|
||||||
|
previousData: previousData,
|
||||||
key: uKey,
|
key: uKey,
|
||||||
onData: widget.onData,
|
onData: widget.onData,
|
||||||
onError: widget.onError,
|
onError: widget.onError,
|
||||||
@@ -72,7 +75,17 @@ class _QueryBuilderState<T extends Object, Outside>
|
|||||||
// re-init the query-builder when new queryJob is appended
|
// re-init the query-builder when new queryJob is appended
|
||||||
if (oldWidget.job.queryKey != widget.job.queryKey) {
|
if (oldWidget.job.queryKey != widget.job.queryKey) {
|
||||||
_queryDispose();
|
_queryDispose();
|
||||||
init();
|
|
||||||
|
/// setting the new query's initial data as prev query's data
|
||||||
|
/// when [job.keepPreviousData] is true and both are dynamic
|
||||||
|
if (oldWidget.job.isDynamic &&
|
||||||
|
widget.job.isDynamic &&
|
||||||
|
oldWidget.job.keepPreviousData == true &&
|
||||||
|
widget.job.keepPreviousData == true) {
|
||||||
|
init(query?.data);
|
||||||
|
} else {
|
||||||
|
init();
|
||||||
|
}
|
||||||
} else if (oldWidget.externalData != null &&
|
} else if (oldWidget.externalData != null &&
|
||||||
widget.externalData != null &&
|
widget.externalData != null &&
|
||||||
!isShallowEqual(oldWidget.externalData!, widget.externalData!)) {
|
!isShallowEqual(oldWidget.externalData!, widget.externalData!)) {
|
||||||
|
|||||||
@@ -50,3 +50,7 @@ bool isConnectedToInternet(ConnectivityResult result) {
|
|||||||
ConnectivityResult.wifi,
|
ConnectivityResult.wifi,
|
||||||
].contains(result);
|
].contains(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String getVariable(String queryKey) {
|
||||||
|
return queryKey.split("#").last;
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,13 +4,14 @@
|
|||||||
|
|
||||||
import 'dart:async' as _i7;
|
import 'dart:async' as _i7;
|
||||||
|
|
||||||
import 'package:connectivity_plus/connectivity_plus.dart' as _i10;
|
import 'package:connectivity_plus/connectivity_plus.dart' as _i11;
|
||||||
import 'package:fl_query/src/models/mutation_job.dart' as _i9;
|
import 'package:fl_query/src/models/mutation_job.dart' as _i9;
|
||||||
import 'package:fl_query/src/models/query_job.dart' as _i8;
|
import 'package:fl_query/src/models/query_job.dart' as _i8;
|
||||||
import 'package:fl_query/src/mutation.dart' as _i4;
|
import 'package:fl_query/src/mutation.dart' as _i4;
|
||||||
import 'package:fl_query/src/query.dart' as _i3;
|
import 'package:fl_query/src/query.dart' as _i3;
|
||||||
import 'package:fl_query/src/query_bowl.dart' as _i6;
|
import 'package:fl_query/src/query_bowl.dart' as _i6;
|
||||||
import 'package:flutter/foundation.dart' as _i5;
|
import 'package:flutter/foundation.dart' as _i5;
|
||||||
|
import 'package:flutter/rendering.dart' as _i10;
|
||||||
import 'package:flutter/widgets.dart' as _i2;
|
import 'package:flutter/widgets.dart' as _i2;
|
||||||
import 'package:mockito/mockito.dart' as _i1;
|
import 'package:mockito/mockito.dart' as _i1;
|
||||||
|
|
||||||
@@ -100,6 +101,14 @@ class MockQueryBowl extends _i1.Mock implements _i6.QueryBowl {
|
|||||||
_i2.Widget get child => (super.noSuchMethod(Invocation.getter(#child),
|
_i2.Widget get child => (super.noSuchMethod(Invocation.getter(#child),
|
||||||
returnValue: _FakeWidget_1()) as _i2.Widget);
|
returnValue: _FakeWidget_1()) as _i2.Widget);
|
||||||
@override
|
@override
|
||||||
|
_i7.Future<T?> prefetchQuery<T extends Object, Outside>(
|
||||||
|
_i8.QueryJob<T, Outside>? options,
|
||||||
|
{Outside? externalData}) =>
|
||||||
|
(super.noSuchMethod(
|
||||||
|
Invocation.method(
|
||||||
|
#prefetchQuery, [options], {#externalData: externalData}),
|
||||||
|
returnValue: Future<T?>.value()) as _i7.Future<T?>);
|
||||||
|
@override
|
||||||
_i7.Future<T?> fetchQuery<T extends Object, Outside>(
|
_i7.Future<T?> fetchQuery<T extends Object, Outside>(
|
||||||
_i8.QueryJob<T, Outside>? options,
|
_i8.QueryJob<T, Outside>? options,
|
||||||
{Outside? externalData,
|
{Outside? externalData,
|
||||||
@@ -122,7 +131,8 @@ class MockQueryBowl extends _i1.Mock implements _i6.QueryBowl {
|
|||||||
{Outside? externalData,
|
{Outside? externalData,
|
||||||
_i2.ValueKey<String>? key,
|
_i2.ValueKey<String>? key,
|
||||||
_i3.QueryListener<T>? onData,
|
_i3.QueryListener<T>? onData,
|
||||||
_i3.QueryListener<dynamic>? onError}) =>
|
_i3.QueryListener<dynamic>? onError,
|
||||||
|
T? previousData}) =>
|
||||||
(super.noSuchMethod(
|
(super.noSuchMethod(
|
||||||
Invocation.method(#addQuery, [
|
Invocation.method(#addQuery, [
|
||||||
queryJob
|
queryJob
|
||||||
@@ -130,7 +140,8 @@ class MockQueryBowl extends _i1.Mock implements _i6.QueryBowl {
|
|||||||
#externalData: externalData,
|
#externalData: externalData,
|
||||||
#key: key,
|
#key: key,
|
||||||
#onData: onData,
|
#onData: onData,
|
||||||
#onError: onError
|
#onError: onError,
|
||||||
|
#previousData: previousData
|
||||||
}),
|
}),
|
||||||
returnValue: _FakeQuery_2<T, Outside>()) as _i3.Query<T, Outside>);
|
returnValue: _FakeQuery_2<T, Outside>()) as _i3.Query<T, Outside>);
|
||||||
@override
|
@override
|
||||||
@@ -190,7 +201,7 @@ class MockQueryBowl extends _i1.Mock implements _i6.QueryBowl {
|
|||||||
.noSuchMethod(Invocation.method(#toStringShort, []), returnValue: '')
|
.noSuchMethod(Invocation.method(#toStringShort, []), returnValue: '')
|
||||||
as String);
|
as String);
|
||||||
@override
|
@override
|
||||||
void debugFillProperties(_i5.DiagnosticPropertiesBuilder? properties) =>
|
void debugFillProperties(_i10.DiagnosticPropertiesBuilder? properties) =>
|
||||||
super.noSuchMethod(Invocation.method(#debugFillProperties, [properties]),
|
super.noSuchMethod(Invocation.method(#debugFillProperties, [properties]),
|
||||||
returnValueForMissingStub: null);
|
returnValueForMissingStub: null);
|
||||||
@override
|
@override
|
||||||
@@ -232,22 +243,22 @@ class MockQueryBowl extends _i1.Mock implements _i6.QueryBowl {
|
|||||||
/// A class which mocks [Connectivity].
|
/// A class which mocks [Connectivity].
|
||||||
///
|
///
|
||||||
/// See the documentation for Mockito's code generation for more information.
|
/// See the documentation for Mockito's code generation for more information.
|
||||||
class MockConnectivity extends _i1.Mock implements _i10.Connectivity {
|
class MockConnectivity extends _i1.Mock implements _i11.Connectivity {
|
||||||
MockConnectivity() {
|
MockConnectivity() {
|
||||||
_i1.throwOnMissingStub(this);
|
_i1.throwOnMissingStub(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_i7.Stream<_i10.ConnectivityResult> get onConnectivityChanged =>
|
_i7.Stream<_i11.ConnectivityResult> get onConnectivityChanged =>
|
||||||
(super.noSuchMethod(Invocation.getter(#onConnectivityChanged),
|
(super.noSuchMethod(Invocation.getter(#onConnectivityChanged),
|
||||||
returnValue: Stream<_i10.ConnectivityResult>.empty())
|
returnValue: Stream<_i11.ConnectivityResult>.empty())
|
||||||
as _i7.Stream<_i10.ConnectivityResult>);
|
as _i7.Stream<_i11.ConnectivityResult>);
|
||||||
@override
|
@override
|
||||||
_i7.Future<_i10.ConnectivityResult> checkConnectivity() =>
|
_i7.Future<_i11.ConnectivityResult> checkConnectivity() =>
|
||||||
(super.noSuchMethod(Invocation.method(#checkConnectivity, []),
|
(super.noSuchMethod(Invocation.method(#checkConnectivity, []),
|
||||||
returnValue: Future<_i10.ConnectivityResult>.value(
|
returnValue: Future<_i11.ConnectivityResult>.value(
|
||||||
_i10.ConnectivityResult.bluetooth))
|
_i11.ConnectivityResult.bluetooth))
|
||||||
as _i7.Future<_i10.ConnectivityResult>);
|
as _i7.Future<_i11.ConnectivityResult>);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A class which mocks [QueryJob].
|
/// A class which mocks [QueryJob].
|
||||||
@@ -266,34 +277,16 @@ class MockQueryJobVoidObject extends _i1.Mock
|
|||||||
super.noSuchMethod(Invocation.setter(#task, _task),
|
super.noSuchMethod(Invocation.setter(#task, _task),
|
||||||
returnValueForMissingStub: null);
|
returnValueForMissingStub: null);
|
||||||
@override
|
@override
|
||||||
set refetchOnMount(bool? _refetchOnMount) =>
|
set initialData(Object? _initialData) =>
|
||||||
super.noSuchMethod(Invocation.setter(#refetchOnMount, _refetchOnMount),
|
super.noSuchMethod(Invocation.setter(#initialData, _initialData),
|
||||||
returnValueForMissingStub: null);
|
returnValueForMissingStub: null);
|
||||||
@override
|
@override
|
||||||
set refetchOnReconnect(bool? _refetchOnReconnect) => super.noSuchMethod(
|
bool get isDynamic =>
|
||||||
Invocation.setter(#refetchOnReconnect, _refetchOnReconnect),
|
(super.noSuchMethod(Invocation.getter(#isDynamic), returnValue: false)
|
||||||
returnValueForMissingStub: null);
|
as bool);
|
||||||
@override
|
@override
|
||||||
set refetchOnExternalDataChange(bool? _refetchOnExternalDataChange) =>
|
set isDynamic(bool? _isDynamic) =>
|
||||||
super.noSuchMethod(
|
super.noSuchMethod(Invocation.setter(#isDynamic, _isDynamic),
|
||||||
Invocation.setter(
|
|
||||||
#refetchOnExternalDataChange, _refetchOnExternalDataChange),
|
|
||||||
returnValueForMissingStub: null);
|
|
||||||
@override
|
|
||||||
set staleTime(Duration? _staleTime) =>
|
|
||||||
super.noSuchMethod(Invocation.setter(#staleTime, _staleTime),
|
|
||||||
returnValueForMissingStub: null);
|
|
||||||
@override
|
|
||||||
set cacheTime(Duration? _cacheTime) =>
|
|
||||||
super.noSuchMethod(Invocation.setter(#cacheTime, _cacheTime),
|
|
||||||
returnValueForMissingStub: null);
|
|
||||||
@override
|
|
||||||
set refetchInterval(Duration? _refetchInterval) =>
|
|
||||||
super.noSuchMethod(Invocation.setter(#refetchInterval, _refetchInterval),
|
|
||||||
returnValueForMissingStub: null);
|
|
||||||
@override
|
|
||||||
set connectivity(_i10.Connectivity? _connectivity) =>
|
|
||||||
super.noSuchMethod(Invocation.setter(#connectivity, _connectivity),
|
|
||||||
returnValueForMissingStub: null);
|
returnValueForMissingStub: null);
|
||||||
@override
|
@override
|
||||||
String get queryKey =>
|
String get queryKey =>
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:fl_query_hooks/fl_query_hooks.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
final todoJob = QueryJob.withVariableKey<Map, void>(
|
||||||
|
preQueryKey: "todo",
|
||||||
|
task: (queryKey, _) async {
|
||||||
|
final res = await http.get(
|
||||||
|
Uri.parse(
|
||||||
|
"https://jsonplaceholder.typicode.com/todos/${getVariable(queryKey)}"),
|
||||||
|
);
|
||||||
|
return jsonDecode(res.body);
|
||||||
|
},
|
||||||
|
keepPreviousData: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
class QueryHookPreviousDataExample extends HookWidget {
|
||||||
|
const QueryHookPreviousDataExample({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final id = useState(1);
|
||||||
|
final query =
|
||||||
|
useQuery(job: todoJob(id.value.toString()), externalData: null);
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"# Query Variable Key with keepPreviousData",
|
||||||
|
style: Theme.of(context).textTheme.headline5,
|
||||||
|
),
|
||||||
|
if (query.hasError)
|
||||||
|
Text(query.error.toString())
|
||||||
|
else if (!query.hasData)
|
||||||
|
const CircularProgressIndicator()
|
||||||
|
else
|
||||||
|
Text(jsonEncode(query.data ?? {})),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.remove),
|
||||||
|
onPressed: () {
|
||||||
|
id.value -= 1;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
onPressed: () {
|
||||||
|
id.value += 1;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import 'package:fl_query_hooks_example/components/basic_hook_query.dart';
|
|||||||
import 'package:fl_query_hooks_example/components/lazy_hook_query.dart';
|
import 'package:fl_query_hooks_example/components/lazy_hook_query.dart';
|
||||||
import 'package:fl_query_hooks_example/components/mutation_hook_variable_key.dart';
|
import 'package:fl_query_hooks_example/components/mutation_hook_variable_key.dart';
|
||||||
import 'package:fl_query_hooks_example/components/query_hook_external_data.dart';
|
import 'package:fl_query_hooks_example/components/query_hook_external_data.dart';
|
||||||
|
import 'package:fl_query_hooks_example/components/query_hook_previous_data.dart';
|
||||||
import 'package:fl_query_hooks_example/components/query_hook_variable_key.dart';
|
import 'package:fl_query_hooks_example/components/query_hook_variable_key.dart';
|
||||||
import 'package:fl_query/fl_query.dart';
|
import 'package:fl_query/fl_query.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -53,6 +54,7 @@ class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
|
|||||||
QueryHookExternalDataExample(),
|
QueryHookExternalDataExample(),
|
||||||
LazyHookQueryExample(),
|
LazyHookQueryExample(),
|
||||||
QueryHookVariableKeyExample(),
|
QueryHookVariableKeyExample(),
|
||||||
|
QueryHookPreviousDataExample(),
|
||||||
Divider(),
|
Divider(),
|
||||||
BasicHookMutationExample(),
|
BasicHookMutationExample(),
|
||||||
MutationHookVariableKeyExample(),
|
MutationHookVariableKeyExample(),
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// ignore_for_file: invalid_use_of_protected_member
|
||||||
|
|
||||||
import 'package:fl_query/fl_query.dart';
|
import 'package:fl_query/fl_query.dart';
|
||||||
import 'package:fl_query_hooks/src/utils.dart';
|
import 'package:fl_query_hooks/src/utils.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
@@ -31,10 +33,11 @@ Query<T, Outside> useQuery<T extends Object, Outside>({
|
|||||||
final oldOnData = usePrevious(onData);
|
final oldOnData = usePrevious(onData);
|
||||||
final oldOnError = usePrevious(onError);
|
final oldOnError = usePrevious(onError);
|
||||||
|
|
||||||
final init = useCallback(() {
|
final init = useCallback(([T? previousData]) {
|
||||||
query.value = queryBowl.addQuery<T, Outside>(
|
query.value = queryBowl.addQuery<T, Outside>(
|
||||||
job,
|
job,
|
||||||
externalData: externalData,
|
externalData: externalData,
|
||||||
|
previousData: previousData,
|
||||||
key: uKey,
|
key: uKey,
|
||||||
onData: onData,
|
onData: onData,
|
||||||
onError: onError,
|
onError: onError,
|
||||||
@@ -66,7 +69,17 @@ Query<T, Outside> useQuery<T extends Object, Outside>({
|
|||||||
final hasOnDataChanged = oldOnData != onData && oldOnData != null;
|
final hasOnDataChanged = oldOnData != onData && oldOnData != null;
|
||||||
if (oldJob != null && oldJob.queryKey != job.queryKey) {
|
if (oldJob != null && oldJob.queryKey != job.queryKey) {
|
||||||
disposeQuery();
|
disposeQuery();
|
||||||
init();
|
|
||||||
|
/// setting the new query's initial data as prev query's data
|
||||||
|
/// when [job.keepPreviousData] is true and both are dynamic
|
||||||
|
if (oldJob.isDynamic &&
|
||||||
|
job.isDynamic &&
|
||||||
|
oldJob.keepPreviousData == true &&
|
||||||
|
job.keepPreviousData == true) {
|
||||||
|
init(query.value.data);
|
||||||
|
} else {
|
||||||
|
init();
|
||||||
|
}
|
||||||
} else if (oldExternalData != null &&
|
} else if (oldExternalData != null &&
|
||||||
externalData != null &&
|
externalData != null &&
|
||||||
!isShallowEqual(oldExternalData, externalData)) {
|
!isShallowEqual(oldExternalData, externalData)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user