From 783a2737b18283478d65082e236629188c124e6b Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Tue, 21 Feb 2023 12:47:34 +0600 Subject: [PATCH] feat: infinite query listenable add and cache event stream --- .../example/lib/pages/infinite_query.dart | 9 +++ packages/fl_query/lib/fl_query.dart | 1 + packages/fl_query/lib/src/core/cache.dart | 30 +++++++ .../widgets/infinite_query_listenable.dart | 79 +++++++++++++++++++ .../lib/src/widgets/query_listenable.dart | 43 +++++++++- 5 files changed, 158 insertions(+), 4 deletions(-) create mode 100644 packages/fl_query/lib/src/widgets/infinite_query_listenable.dart diff --git a/packages/fl_query/example/lib/pages/infinite_query.dart b/packages/fl_query/example/lib/pages/infinite_query.dart index cc13fcd..a9c12b9 100644 --- a/packages/fl_query/example/lib/pages/infinite_query.dart +++ b/packages/fl_query/example/lib/pages/infinite_query.dart @@ -40,6 +40,15 @@ class _InfiniteQueryPageWidgetState extends State { 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( const ValueKey("products"), (page) async { diff --git a/packages/fl_query/lib/fl_query.dart b/packages/fl_query/lib/fl_query.dart index 4210962..38e84a6 100644 --- a/packages/fl_query/lib/fl_query.dart +++ b/packages/fl_query/lib/fl_query.dart @@ -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'; diff --git a/packages/fl_query/lib/src/core/cache.dart b/packages/fl_query/lib/src/core/cache.dart index feffc5a..f0dd9c0 100644 --- a/packages/fl_query/lib/src/core/cache.dart +++ b/packages/fl_query/lib/src/core/cache.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 _queries; final Set _infiniteQueries; final Duration cacheDuration; + final _eventController = StreamController.broadcast(); + QueryCache({ this.cacheDuration = DefaultConstants.cacheDuration, }) : _queries = Set(), @@ -25,19 +41,33 @@ class QueryCache { UnmodifiableSetView get infiniteQueries => UnmodifiableSetView(_infiniteQueries); + Stream 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), + ); } } diff --git a/packages/fl_query/lib/src/widgets/infinite_query_listenable.dart b/packages/fl_query/lib/src/widgets/infinite_query_listenable.dart new file mode 100644 index 0000000..b88bd35 --- /dev/null +++ b/packages/fl_query/lib/src/widgets/infinite_query_listenable.dart @@ -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 + = Widget Function( + BuildContext context, + InfiniteQuery? query, +); + +class InfiniteQueryListenable + extends StatefulWidget { + final ValueKey queryKey; + final InfiniteQueryListenableBuilder + builder; + const InfiniteQueryListenable( + this.queryKey, { + required this.builder, + super.key, + }); + + @override + State> + createState() => _InfiniteQueryListenableState(); +} + +class _InfiniteQueryListenableState + extends State< + InfiniteQueryListenable> { + StreamSubscription? _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( + widget.queryKey); + + if (query == null) { + return widget.builder(context, null); + } + return StateNotifierListenable< + InfiniteQueryState>( + notifier: query, + builder: (context, notifier) { + final query = + notifier as InfiniteQuery; + return widget.builder(context, query); + }, + ); + } +} diff --git a/packages/fl_query/lib/src/widgets/query_listenable.dart b/packages/fl_query/lib/src/widgets/query_listenable.dart index 36f8d7c..72fe308 100644 --- a/packages/fl_query/lib/src/widgets/query_listenable.dart +++ b/packages/fl_query/lib/src/widgets/query_listenable.dart @@ -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 = Widget Function( Query? query, ); -class QueryListenable extends StatelessWidget { +class QueryListenable extends StatefulWidget { final ValueKey queryKey; final QueryListenableBuilder builder; const QueryListenable( @@ -16,19 +18,52 @@ class QueryListenable extends StatelessWidget { super.key, }); + @override + State> createState() => + _QueryListenableState(); +} + +class _QueryListenableState + extends State> { + StreamSubscription? _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(queryKey); + .getQuery(widget.queryKey); if (query == null) { - return builder(context, null); + return widget.builder(context, null); } return StateNotifierListenable>( notifier: query, builder: (context, notifier) { final query = notifier as Query; - return builder(context, query); + return widget.builder(context, query); }, ); }