Depended queries support added
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import 'package:example/main.dart';
|
||||
import 'package:fl_query/fl_query.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
final dependentQueryJob = QueryJob<String, void>(
|
||||
queryKey: "dependent-query-job",
|
||||
task: (queryKey, externalData, query) {
|
||||
final successQuery =
|
||||
query.dependOnQuery<String, void>(successJob, externalData: null);
|
||||
final failedQuery =
|
||||
query.dependOnQuery<String, void>(failedJob, externalData: null);
|
||||
if (failedQuery.hasError) return failedQuery.error.toString();
|
||||
if (successQuery.hasData) return successQuery.data!;
|
||||
return "No data from success query yet";
|
||||
});
|
||||
|
||||
class DependentQueryExample extends StatelessWidget {
|
||||
const DependentQueryExample({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(),
|
||||
body: QueryBuilder<String, void>(
|
||||
job: dependentQueryJob,
|
||||
externalData: null,
|
||||
builder: (context, query) {
|
||||
if (query.isLoading || query.isRefetching || !query.hasData) {
|
||||
return const CircularProgressIndicator();
|
||||
}
|
||||
return Text(query.data!);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
||||
final lazyQueryJob = QueryJob<String, String>(
|
||||
queryKey: "non_enabled_query",
|
||||
enabled: false,
|
||||
task: (queryKey, data) {
|
||||
task: (queryKey, data, _) {
|
||||
return Future.delayed(const Duration(milliseconds: 500),
|
||||
() => "Hello from $queryKey with $data");
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:example/another_component.dart';
|
||||
import 'package:example/dependent_query_example.dart';
|
||||
import 'package:example/hooks_example.dart';
|
||||
import 'package:example/lazy_query.dart';
|
||||
import 'package:example/mutation_example.dart';
|
||||
@@ -34,13 +35,13 @@ class MyApp extends StatelessWidget {
|
||||
|
||||
final successJob = QueryJob<String, void>(
|
||||
queryKey: "greetings",
|
||||
task: (queryKey, _) => Future.delayed(const Duration(seconds: 2),
|
||||
task: (queryKey, _, __) => Future.delayed(const Duration(seconds: 2),
|
||||
() => "Welcome ($queryKey) ${Random.secure().nextInt(100)}"),
|
||||
);
|
||||
|
||||
final failedJob = QueryJob<String, void>(
|
||||
queryKey: "failure",
|
||||
task: (queryKey, _) => Random().nextBool()
|
||||
task: (queryKey, _, __) => Random().nextBool()
|
||||
? Future.error("[$queryKey] Failed for unknown reason")
|
||||
: Future.value(
|
||||
"Success, you'll get slowly ${Random().nextInt(100)}!",
|
||||
@@ -178,6 +179,16 @@ class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
|
||||
);
|
||||
},
|
||||
),
|
||||
ElevatedButton(
|
||||
child: const Text("Dependent Query Example"),
|
||||
onPressed: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const DependentQueryExample(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const AnotherComponent(),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -6,7 +6,7 @@ import 'package:flutter/material.dart';
|
||||
final jobWithExternalData = QueryJob<String, String>(
|
||||
queryKey: "external_data",
|
||||
cacheTime: const Duration(seconds: 10),
|
||||
task: (queryKey, data) {
|
||||
task: (queryKey, data, _) {
|
||||
return Future.delayed(const Duration(milliseconds: 500),
|
||||
() => "Hello from $queryKey with $data");
|
||||
});
|
||||
|
||||
@@ -60,6 +60,6 @@ abstract class BaseOperation<Data, StatusType> extends ChangeNotifier {
|
||||
bool get isLoading;
|
||||
bool get isIdle;
|
||||
bool get isInactive => mounts.isEmpty;
|
||||
bool get hasData => isSuccess && data != null;
|
||||
bool get hasError => isError && error != null;
|
||||
bool get hasData => data != null;
|
||||
bool get hasError => error != null;
|
||||
}
|
||||
|
||||
@@ -17,106 +17,67 @@ Query<T, Outside> useQuery<T extends Object, Outside>({
|
||||
QueryListener<dynamic>? onError,
|
||||
List<Object?>? keys,
|
||||
}) {
|
||||
return use(_UseQuery<T, Outside>(
|
||||
externalData: externalData,
|
||||
job: job,
|
||||
onData: onData,
|
||||
onError: onError,
|
||||
keys: keys,
|
||||
));
|
||||
}
|
||||
final context = useContext();
|
||||
QueryBowl queryBowl = QueryBowl.of(context);
|
||||
final ValueKey<String> uKey = useMemoized(() => ValueKey(uuid.v4()), []);
|
||||
Query<T, Outside> query = useMemoized(
|
||||
() => Query.fromOptions(
|
||||
job,
|
||||
externalData: externalData,
|
||||
queryBowl: queryBowl,
|
||||
onData: onData,
|
||||
onError: onError,
|
||||
),
|
||||
[]);
|
||||
|
||||
class _UseQuery<T extends Object, Outside> extends Hook<Query<T, Outside>> {
|
||||
final QueryJob<T, Outside> job;
|
||||
final Outside externalData;
|
||||
final oldExternalData = usePrevious(externalData);
|
||||
final oldOnData = usePrevious(onData);
|
||||
final oldOnError = usePrevious(onError);
|
||||
|
||||
/// Called when the query returns new data, on query
|
||||
/// refetch or query gets expired
|
||||
final QueryListener<T>? onData;
|
||||
|
||||
/// Called when the query returns error
|
||||
final QueryListener<dynamic>? onError;
|
||||
const _UseQuery({
|
||||
required this.job,
|
||||
required this.externalData,
|
||||
this.onData,
|
||||
this.onError,
|
||||
super.keys,
|
||||
});
|
||||
|
||||
@override
|
||||
HookState<Query<T, Outside>, Hook<Query<T, Outside>>> createState() =>
|
||||
_UseQueryHookState();
|
||||
}
|
||||
|
||||
class _UseQueryHookState<T extends Object, Outside>
|
||||
extends HookState<Query<T, Outside>, _UseQuery<T, Outside>> {
|
||||
late QueryBowl queryBowl;
|
||||
late final ValueKey<String> uKey;
|
||||
late Query<T, Outside> query;
|
||||
|
||||
@override
|
||||
void initHook() {
|
||||
super.initHook();
|
||||
uKey = ValueKey<String>(uuid.v4());
|
||||
query = Query<T, Outside>.fromOptions(
|
||||
hook.job,
|
||||
externalData: hook.externalData,
|
||||
useEffect(() {
|
||||
queryBowl.addQuery<T, Outside>(
|
||||
query,
|
||||
key: uKey,
|
||||
onData: onData,
|
||||
onError: onError,
|
||||
);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
query = QueryBowl.of(context).addQuery<T, Outside>(
|
||||
query,
|
||||
key: uKey,
|
||||
onData: hook.onData,
|
||||
onError: hook.onError,
|
||||
);
|
||||
final hasExternalDataChanged = query.externalData != null &&
|
||||
query.prevUsedExternalData != null &&
|
||||
!isShallowEqual(query.externalData!, query.prevUsedExternalData!);
|
||||
(query.fetched && query.refetchOnMount == true) || hasExternalDataChanged
|
||||
? await query.refetch()
|
||||
: await query.fetch();
|
||||
});
|
||||
}
|
||||
final hasExternalDataChanged = query.externalData != null &&
|
||||
query.prevUsedExternalData != null &&
|
||||
!isShallowEqual(query.externalData!, query.prevUsedExternalData!);
|
||||
(query.fetched && query.refetchOnMount == true) || hasExternalDataChanged
|
||||
? query.refetch()
|
||||
: query.fetch();
|
||||
|
||||
@override
|
||||
void didUpdateHook(_UseQuery<T, Outside> oldHook) {
|
||||
if (oldHook.externalData != null &&
|
||||
hook.externalData != null &&
|
||||
!isShallowEqual(oldHook.externalData!, hook.externalData!)) {
|
||||
return () {
|
||||
query.unmount(uKey);
|
||||
if (onData != null) query.onDataListeners.remove(onData);
|
||||
if (onError != null) query.onErrorListeners.remove(onError);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() {
|
||||
if (oldExternalData != null &&
|
||||
externalData != null &&
|
||||
!isShallowEqual(oldExternalData, externalData)) {
|
||||
QueryBowl.of(context).fetchQuery(
|
||||
hook.job,
|
||||
externalData: hook.externalData,
|
||||
onData: hook.onData,
|
||||
onError: hook.onError,
|
||||
job,
|
||||
externalData: externalData,
|
||||
onData: onData,
|
||||
onError: onError,
|
||||
key: uKey,
|
||||
);
|
||||
} else {
|
||||
if (oldHook.onData != hook.onData && oldHook.onData != null) {
|
||||
query.onDataListeners.remove(oldHook.onData);
|
||||
if (hook.onData != null) query.onDataListeners.add(hook.onData!);
|
||||
if (oldOnData != onData && oldOnData != null) {
|
||||
query.onDataListeners.remove(oldOnData);
|
||||
if (onData != null) query.onDataListeners.add(onData);
|
||||
}
|
||||
if (oldHook.onError != hook.onError && oldHook.onError != null) {
|
||||
query.onErrorListeners.remove(oldHook.onError);
|
||||
if (hook.onError != null) query.onErrorListeners.add(hook.onError!);
|
||||
if (oldOnError != onError && oldOnError != null) {
|
||||
query.onErrorListeners.remove(oldOnError);
|
||||
if (onError != null) query.onErrorListeners.add(onError);
|
||||
}
|
||||
}
|
||||
super.didUpdateHook(oldHook);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
query.unmount(uKey);
|
||||
if (hook.onData != null) query.onDataListeners.remove(hook.onData);
|
||||
if (hook.onError != null) query.onErrorListeners.remove(hook.onError);
|
||||
}
|
||||
|
||||
@override
|
||||
Query<T, Outside> build(BuildContext context) {
|
||||
queryBowl = QueryBowl.of(context);
|
||||
return queryBowl.getQuery<T, Outside>(query.queryKey) ?? query;
|
||||
}
|
||||
|
||||
@override
|
||||
String get debugLabel => 'useQuery';
|
||||
return queryBowl.getQuery<T, Outside>(job.queryKey) ?? query;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
import 'package:fl_query/src/query.dart';
|
||||
|
||||
/// How to make dependent Queries?
|
||||
///
|
||||
/// Pass a [QueryBowl] class/object to [task] that contains a method
|
||||
/// [QueryBowl.dependOnQuery] that takes a [QueryJob] and uses that to get
|
||||
/// the appropriate query for it or if it doesn't exist creates a new
|
||||
/// instance. It listens to the changes of [Query] of the passed
|
||||
/// [QueryJob] & calls the [notifyListener] method of running [Query]
|
||||
///
|
||||
/// If shown briefly
|
||||
///
|
||||
/// ```data
|
||||
/// task: (queryKey, external, queryBowl){
|
||||
/// final dependentQuery = queryBowl.dependOnQuery(dependentQueryJob);
|
||||
/// return someAsyncTask();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
class QueryJob<T extends Object, Outside> {
|
||||
// all params
|
||||
String _queryKey;
|
||||
|
||||
@@ -2,7 +2,10 @@ import 'dart:async';
|
||||
|
||||
import 'package:fl_query/src/base_operation.dart';
|
||||
import 'package:fl_query/src/models/query_job.dart';
|
||||
import 'package:fl_query/src/query_bowl.dart';
|
||||
import 'package:fl_query/src/utils.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
enum QueryStatus {
|
||||
/// in times when an error occurs
|
||||
@@ -23,7 +26,11 @@ enum QueryStatus {
|
||||
refetching;
|
||||
}
|
||||
|
||||
typedef QueryTaskFunction<T, Outside> = FutureOr<T> Function(String, Outside);
|
||||
typedef QueryTaskFunction<T extends Object, Outside> = FutureOr<T> Function(
|
||||
String queryKey,
|
||||
Outside externalData,
|
||||
Query<T, Outside> self,
|
||||
);
|
||||
|
||||
typedef QueryListener<T> = FutureOr<void> Function(T);
|
||||
|
||||
@@ -64,6 +71,8 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
|
||||
|
||||
Timer? _refetchIntervalTimer;
|
||||
|
||||
final QueryBowl queryBowl;
|
||||
|
||||
Query({
|
||||
required this.queryKey,
|
||||
required this.task,
|
||||
@@ -72,6 +81,7 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
|
||||
required Outside externalData,
|
||||
required super.retries,
|
||||
required super.retryDelay,
|
||||
required this.queryBowl,
|
||||
this.refetchOnMount,
|
||||
this.refetchInterval,
|
||||
this.enabled = true,
|
||||
@@ -95,6 +105,7 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
|
||||
|
||||
Query.fromOptions(
|
||||
QueryJob<T, Outside> options, {
|
||||
required this.queryBowl,
|
||||
required Outside externalData,
|
||||
QueryListener<T>? onData,
|
||||
QueryListener<dynamic>? onError,
|
||||
@@ -136,7 +147,11 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
|
||||
Future<void> _execute() async {
|
||||
try {
|
||||
retryAttempts = 0;
|
||||
data = await task(queryKey, _externalData);
|
||||
data = await task(
|
||||
queryKey,
|
||||
_externalData,
|
||||
this,
|
||||
);
|
||||
_prevUsedExternalData = _externalData;
|
||||
updatedAt = DateTime.now();
|
||||
status = QueryStatus.success;
|
||||
@@ -157,7 +172,11 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
|
||||
while (retryAttempts <= retries) {
|
||||
await Future.delayed(retryDelay);
|
||||
try {
|
||||
data = await task(queryKey, _externalData);
|
||||
data = await task(
|
||||
queryKey,
|
||||
_externalData,
|
||||
this,
|
||||
);
|
||||
_prevUsedExternalData = _externalData;
|
||||
status = QueryStatus.success;
|
||||
for (final onData in onDataListeners) {
|
||||
@@ -182,7 +201,9 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
|
||||
}
|
||||
|
||||
Future<T?> fetch() async {
|
||||
if (!enabled) return null;
|
||||
/// if isLoading/isRefetching is true that means its already fetching/
|
||||
/// refetching. So [_execute] again can create a race condition
|
||||
if (!enabled || isLoading || isRefetching) return null;
|
||||
if (hasData) {
|
||||
return data;
|
||||
}
|
||||
@@ -195,13 +216,14 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
|
||||
}
|
||||
|
||||
Future<T?> refetch() async {
|
||||
// cannot let run multiple refetch at the same time. It can cause
|
||||
// race-condition
|
||||
if (isRefetching) return null;
|
||||
/// if isLoading/isRefetching is true that means its already fetching/
|
||||
/// refetching. So [_execute] again can create a race condition
|
||||
if (isRefetching || isLoading) return null;
|
||||
status = QueryStatus.refetching;
|
||||
refetchCount++;
|
||||
// disabling the lazy query bound when query was actually called
|
||||
if (!enabled) enabled = false;
|
||||
if (!enabled) enabled = true;
|
||||
if (enabled && !fetched) await fetch();
|
||||
notifyListeners();
|
||||
return await _execute().then((_) => data);
|
||||
}
|
||||
@@ -239,6 +261,11 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
|
||||
fetched = false;
|
||||
status = QueryStatus.idle;
|
||||
retryAttempts = 0;
|
||||
for (final queryEntry in _dependencyQueries.entries) {
|
||||
queryEntry.value.unmount(queryEntry.key);
|
||||
queryEntry.value.removeListener(refetch);
|
||||
}
|
||||
_dependencyQueries = {};
|
||||
onDataListeners.clear();
|
||||
onErrorListeners.clear();
|
||||
mounts.clear();
|
||||
@@ -268,6 +295,42 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Map<ValueKey<String>, Query> _dependencyQueries = {};
|
||||
|
||||
/// only usable inside any task method
|
||||
Query<T, Outside> dependOnQuery<T extends Object, Outside>(
|
||||
QueryJob<T, Outside> job, {
|
||||
required Outside externalData,
|
||||
}) {
|
||||
final key = ValueKey(uuid.v4());
|
||||
final query = queryBowl.addQuery(
|
||||
Query<T, Outside>.fromOptions(
|
||||
job,
|
||||
externalData: externalData,
|
||||
queryBowl: queryBowl,
|
||||
),
|
||||
key: key,
|
||||
);
|
||||
// removing listener if it was already hooked to it previously
|
||||
query.removeListener(refetch);
|
||||
query.addListener(refetch);
|
||||
final uKey =
|
||||
_dependencyQueries.keys.firstWhereOrNull((k) => k.value == key.value) ??
|
||||
key;
|
||||
_dependencyQueries[uKey] = query;
|
||||
if (!query.fetched) query.fetch();
|
||||
return query;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
for (final queryEntry in _dependencyQueries.entries) {
|
||||
queryEntry.value.unmount(queryEntry.key);
|
||||
queryEntry.value.removeListener(refetch);
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
bool get isStale {
|
||||
/// when [_staleTime] is [Duration.zero], the query will always be
|
||||
/// stale & will never refetch in the background. But can be inactive
|
||||
|
||||
@@ -30,7 +30,7 @@ class QueryBowlScope extends StatefulWidget {
|
||||
const QueryBowlScope({
|
||||
required this.child,
|
||||
this.staleTime = Duration.zero,
|
||||
this.cacheTime = const Duration(minutes: 5),
|
||||
this.cacheTime = const Duration(seconds: 5),
|
||||
this.refetchInterval = Duration.zero,
|
||||
this.refetchOnMount = false,
|
||||
this.refetchOnReconnect = true,
|
||||
@@ -79,9 +79,23 @@ class _QueryBowlScopeState extends State<QueryBowlScope> {
|
||||
}
|
||||
|
||||
void updateQueries(Query query) {
|
||||
// checking & not including inactive queries
|
||||
// basically garbage collecting queries
|
||||
if (query.isInactive) {
|
||||
/// there's a bug currently,
|
||||
/// where if somehow a [Query] (queryA) is depending on another
|
||||
/// [Query] (queryB) & queryA has become inactive so its getting
|
||||
/// disposed but at the same moment queryB got refetched will make
|
||||
/// queryB's [BaseOperation.unmount] to throw [RangeError] for
|
||||
/// calling [ChangeNotifier.notifyListener] after [cacheDelay]
|
||||
/// inside [Timer.periodic] callback
|
||||
///
|
||||
/// To mitigate this, [Query.reset] is used instead of using
|
||||
/// [Query.dispose] as it doesn't call [super.dispose]
|
||||
query.reset();
|
||||
}
|
||||
|
||||
setState(() {
|
||||
// checking & not including inactive queries
|
||||
// basically garbage collecting queries
|
||||
queries = Set.from(
|
||||
query.isInactive
|
||||
? queries.where((el) => el.queryKey != query.queryKey)
|
||||
@@ -240,6 +254,7 @@ class QueryBowl extends InheritedWidget {
|
||||
final query = Query<T, Outside>.fromOptions(
|
||||
options,
|
||||
externalData: externalData,
|
||||
queryBowl: this,
|
||||
onData: onData,
|
||||
onError: onError,
|
||||
);
|
||||
|
||||
@@ -34,29 +34,30 @@ class _QueryBuilderState<T extends Object, Outside>
|
||||
extends State<QueryBuilder<T, Outside>> {
|
||||
late QueryBowl queryBowl;
|
||||
late final ValueKey<String> uKey;
|
||||
late Query<T, Outside> query;
|
||||
Query<T, Outside>? query;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
uKey = ValueKey<String>(uuid.v4());
|
||||
query = Query<T, Outside>.fromOptions(
|
||||
widget.job,
|
||||
externalData: widget.externalData,
|
||||
);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
query = QueryBowl.of(context).addQuery<T, Outside>(
|
||||
query,
|
||||
Query<T, Outside>.fromOptions(
|
||||
widget.job,
|
||||
externalData: widget.externalData,
|
||||
queryBowl: QueryBowl.of(context),
|
||||
),
|
||||
key: uKey,
|
||||
onData: widget.onData,
|
||||
onError: widget.onError,
|
||||
);
|
||||
final hasExternalDataChanged = query.externalData != null &&
|
||||
query.prevUsedExternalData != null &&
|
||||
!isShallowEqual(query.externalData!, query.prevUsedExternalData!);
|
||||
(query.fetched && query.refetchOnMount == true) || hasExternalDataChanged
|
||||
? await query.refetch()
|
||||
: await query.fetch();
|
||||
final hasExternalDataChanged = query!.externalData != null &&
|
||||
query!.prevUsedExternalData != null &&
|
||||
!isShallowEqual(query!.externalData!, query!.prevUsedExternalData!);
|
||||
(query!.fetched && query!.refetchOnMount == true) ||
|
||||
hasExternalDataChanged
|
||||
? await query!.refetch()
|
||||
: await query!.fetch();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -74,12 +75,13 @@ class _QueryBuilderState<T extends Object, Outside>
|
||||
);
|
||||
} else {
|
||||
if (oldWidget.onData != widget.onData && oldWidget.onData != null) {
|
||||
query.onDataListeners.remove(oldWidget.onData);
|
||||
if (widget.onData != null) query.onDataListeners.add(widget.onData!);
|
||||
query?.onDataListeners.remove(oldWidget.onData);
|
||||
if (widget.onData != null) query?.onDataListeners.add(widget.onData!);
|
||||
}
|
||||
if (oldWidget.onError != widget.onError && oldWidget.onError != null) {
|
||||
query.onErrorListeners.remove(oldWidget.onError);
|
||||
if (widget.onError != null) query.onErrorListeners.add(widget.onError!);
|
||||
query?.onErrorListeners.remove(oldWidget.onError);
|
||||
if (widget.onError != null)
|
||||
query?.onErrorListeners.add(widget.onError!);
|
||||
}
|
||||
}
|
||||
super.didUpdateWidget(oldWidget);
|
||||
@@ -87,16 +89,18 @@ class _QueryBuilderState<T extends Object, Outside>
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
query.unmount(uKey);
|
||||
if (widget.onData != null) query.onDataListeners.remove(widget.onData);
|
||||
if (widget.onError != null) query.onErrorListeners.remove(widget.onError);
|
||||
query?.unmount(uKey);
|
||||
if (widget.onData != null) query?.onDataListeners.remove(widget.onData);
|
||||
if (widget.onError != null) query?.onErrorListeners.remove(widget.onError);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
queryBowl = QueryBowl.of(context);
|
||||
final latestQuery = queryBowl.getQuery<T, Outside>(query.queryKey) ?? query;
|
||||
final latestQuery =
|
||||
queryBowl.getQuery<T, Outside>(widget.job.queryKey) ?? query;
|
||||
if (latestQuery == null) return Container();
|
||||
return widget.builder(context, latestQuery);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user