feat: infinite query listenable add and cache event stream
This commit is contained in:
@@ -40,6 +40,15 @@ class _InfiniteQueryPageWidgetState extends State<InfiniteQueryPageWidget> {
|
||||
appBar: AppBar(
|
||||
title: const Text('Infinite Query'),
|
||||
),
|
||||
floatingActionButton: InfiniteQueryListenable(const ValueKey("products"),
|
||||
builder: (context, query) {
|
||||
return FloatingActionButton(
|
||||
onPressed: () {
|
||||
query?.fetchNext();
|
||||
},
|
||||
child: Text(query?.pages.length.toString() ?? "-69"),
|
||||
);
|
||||
}),
|
||||
body: InfiniteQueryBuilder<PagedProducts, ClientException, String, int>(
|
||||
const ValueKey("products"),
|
||||
(page) async {
|
||||
|
||||
@@ -14,3 +14,4 @@ export 'src/core/query.dart';
|
||||
export 'src/widgets/query_builder.dart';
|
||||
export 'src/widgets/query_listenable.dart';
|
||||
export 'src/widgets/infinite_query_builder.dart';
|
||||
export 'src/widgets/infinite_query_listenable.dart';
|
||||
|
||||
@@ -5,12 +5,28 @@ import 'package:fl_query/src/collections/default_configs.dart';
|
||||
import 'package:fl_query/src/core/infinite_query.dart';
|
||||
import 'package:fl_query/src/core/query.dart';
|
||||
|
||||
enum QueryCacheEventType {
|
||||
addQuery,
|
||||
addInfiniteQuery,
|
||||
removeQuery,
|
||||
removeInfiniteQuery,
|
||||
}
|
||||
|
||||
class QueryCacheEvent {
|
||||
final QueryCacheEventType type;
|
||||
final Object data;
|
||||
|
||||
QueryCacheEvent(this.type, this.data);
|
||||
}
|
||||
|
||||
class QueryCache {
|
||||
final Set<Query> _queries;
|
||||
final Set<InfiniteQuery> _infiniteQueries;
|
||||
|
||||
final Duration cacheDuration;
|
||||
|
||||
final _eventController = StreamController<QueryCacheEvent>.broadcast();
|
||||
|
||||
QueryCache({
|
||||
this.cacheDuration = DefaultConstants.cacheDuration,
|
||||
}) : _queries = Set<Query>(),
|
||||
@@ -25,19 +41,33 @@ class QueryCache {
|
||||
UnmodifiableSetView<InfiniteQuery> get infiniteQueries =>
|
||||
UnmodifiableSetView(_infiniteQueries);
|
||||
|
||||
Stream<QueryCacheEvent> get events => _eventController.stream;
|
||||
|
||||
void addQuery(Query query) {
|
||||
_queries.add(query);
|
||||
_eventController.add(
|
||||
QueryCacheEvent(QueryCacheEventType.addQuery, query),
|
||||
);
|
||||
}
|
||||
|
||||
void addInfiniteQuery(InfiniteQuery query) {
|
||||
_infiniteQueries.add(query);
|
||||
_eventController.add(
|
||||
QueryCacheEvent(QueryCacheEventType.addInfiniteQuery, query),
|
||||
);
|
||||
}
|
||||
|
||||
void removeQuery(Query query) {
|
||||
_queries.remove(query);
|
||||
_eventController.add(
|
||||
QueryCacheEvent(QueryCacheEventType.removeQuery, query),
|
||||
);
|
||||
}
|
||||
|
||||
void removeInfiniteQuery(InfiniteQuery query) {
|
||||
_infiniteQueries.remove(query);
|
||||
_eventController.add(
|
||||
QueryCacheEvent(QueryCacheEventType.removeInfiniteQuery, query),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fl_query/fl_query.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fl_query/src/widgets/state_notifier_listenable.dart';
|
||||
|
||||
typedef InfiniteQueryListenableBuilder<DataType, ErrorType, KeyType, PageType>
|
||||
= Widget Function(
|
||||
BuildContext context,
|
||||
InfiniteQuery<DataType, ErrorType, KeyType, PageType>? query,
|
||||
);
|
||||
|
||||
class InfiniteQueryListenable<DataType, ErrorType, KeyType, PageType>
|
||||
extends StatefulWidget {
|
||||
final ValueKey<KeyType> queryKey;
|
||||
final InfiniteQueryListenableBuilder<DataType, ErrorType, KeyType, PageType>
|
||||
builder;
|
||||
const InfiniteQueryListenable(
|
||||
this.queryKey, {
|
||||
required this.builder,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<InfiniteQueryListenable<DataType, ErrorType, KeyType, PageType>>
|
||||
createState() => _InfiniteQueryListenableState<DataType, ErrorType,
|
||||
KeyType, PageType>();
|
||||
}
|
||||
|
||||
class _InfiniteQueryListenableState<DataType, ErrorType, KeyType, PageType>
|
||||
extends State<
|
||||
InfiniteQueryListenable<DataType, ErrorType, KeyType, PageType>> {
|
||||
StreamSubscription<QueryCacheEvent>? _subscription;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
_subscription = QueryClient.of(context).cache.events.listen((event) {
|
||||
switch (event.type) {
|
||||
case QueryCacheEventType.addInfiniteQuery:
|
||||
case QueryCacheEventType.removeInfiniteQuery:
|
||||
if (mounted &&
|
||||
(event.data as InfiniteQuery).key == widget.queryKey) {
|
||||
setState(() {});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_subscription?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final query = QueryClient.of(context)
|
||||
.getInfiniteQuery<DataType, ErrorType, KeyType, PageType>(
|
||||
widget.queryKey);
|
||||
|
||||
if (query == null) {
|
||||
return widget.builder(context, null);
|
||||
}
|
||||
return StateNotifierListenable<
|
||||
InfiniteQueryState<DataType, ErrorType, PageType>>(
|
||||
notifier: query,
|
||||
builder: (context, notifier) {
|
||||
final query =
|
||||
notifier as InfiniteQuery<DataType, ErrorType, KeyType, PageType>;
|
||||
return widget.builder(context, query);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fl_query/fl_query.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fl_query/src/widgets/state_notifier_listenable.dart';
|
||||
@@ -7,7 +9,7 @@ typedef QueryListenableBuilder<DataType, ErrorType, KeyType> = Widget Function(
|
||||
Query<DataType, ErrorType, KeyType>? query,
|
||||
);
|
||||
|
||||
class QueryListenable<DataType, ErrorType, KeyType> extends StatelessWidget {
|
||||
class QueryListenable<DataType, ErrorType, KeyType> extends StatefulWidget {
|
||||
final ValueKey<KeyType> queryKey;
|
||||
final QueryListenableBuilder<DataType, ErrorType, KeyType> builder;
|
||||
const QueryListenable(
|
||||
@@ -16,19 +18,52 @@ class QueryListenable<DataType, ErrorType, KeyType> extends StatelessWidget {
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<QueryListenable<DataType, ErrorType, KeyType>> createState() =>
|
||||
_QueryListenableState<DataType, ErrorType, KeyType>();
|
||||
}
|
||||
|
||||
class _QueryListenableState<DataType, ErrorType, KeyType>
|
||||
extends State<QueryListenable<DataType, ErrorType, KeyType>> {
|
||||
StreamSubscription<QueryCacheEvent>? _subscription;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
_subscription = QueryClient.of(context).cache.events.listen((event) {
|
||||
switch (event.type) {
|
||||
case QueryCacheEventType.addQuery:
|
||||
case QueryCacheEventType.removeQuery:
|
||||
if (mounted && (event.data as Query).key == widget.queryKey) {
|
||||
setState(() {});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_subscription?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final query = QueryClient.of(context)
|
||||
.getQuery<DataType, ErrorType, KeyType>(queryKey);
|
||||
.getQuery<DataType, ErrorType, KeyType>(widget.queryKey);
|
||||
|
||||
if (query == null) {
|
||||
return builder(context, null);
|
||||
return widget.builder(context, null);
|
||||
}
|
||||
return StateNotifierListenable<QueryState<DataType, ErrorType>>(
|
||||
notifier: query,
|
||||
builder: (context, notifier) {
|
||||
final query = notifier as Query<DataType, ErrorType, KeyType>;
|
||||
return builder(context, query);
|
||||
return widget.builder(context, query);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user