feat: add infinite query builder with example
fix(query): updateQueryFn refetch when stale and update state of builders when widget is mounted
This commit is contained in:
@@ -1,11 +1,11 @@
|
|||||||
# This is a generated file; do not edit or check into version control.
|
# This is a generated file; do not edit or check into version control.
|
||||||
connectivity_plus=/home/krtirtho/.pub-cache/hosted/pub.dev/connectivity_plus-2.3.9/
|
connectivity_plus=/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus-2.3.9/
|
||||||
connectivity_plus_linux=/home/krtirtho/.pub-cache/hosted/pub.dev/connectivity_plus_linux-1.3.1/
|
connectivity_plus_linux=/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_linux-1.3.1/
|
||||||
connectivity_plus_macos=/home/krtirtho/.pub-cache/hosted/pub.dev/connectivity_plus_macos-1.2.6/
|
connectivity_plus_macos=/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_macos-1.2.6/
|
||||||
connectivity_plus_web=/home/krtirtho/.pub-cache/hosted/pub.dev/connectivity_plus_web-1.2.5/
|
connectivity_plus_web=/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_web-1.2.5/
|
||||||
connectivity_plus_windows=/home/krtirtho/.pub-cache/hosted/pub.dev/connectivity_plus_windows-1.2.2/
|
connectivity_plus_windows=/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_windows-1.2.2/
|
||||||
path_provider=/home/krtirtho/.pub-cache/hosted/pub.dev/path_provider-2.0.12/
|
path_provider=/home/kingkor/.pub-cache/hosted/pub.dev/path_provider-2.0.12/
|
||||||
path_provider_android=/home/krtirtho/.pub-cache/hosted/pub.dev/path_provider_android-2.0.22/
|
path_provider_android=/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_android-2.0.22/
|
||||||
path_provider_foundation=/home/krtirtho/.pub-cache/hosted/pub.dev/path_provider_foundation-2.1.1/
|
path_provider_foundation=/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_foundation-2.1.1/
|
||||||
path_provider_linux=/home/krtirtho/.pub-cache/hosted/pub.dev/path_provider_linux-2.1.7/
|
path_provider_linux=/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_linux-2.1.8/
|
||||||
path_provider_windows=/home/krtirtho/.pub-cache/hosted/pub.dev/path_provider_windows-2.1.3/
|
path_provider_windows=/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_windows-2.1.3/
|
||||||
|
|||||||
@@ -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.dev/connectivity_plus-2.3.9/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/home/krtirtho/.pub-cache/hosted/pub.dev/path_provider_foundation-2.1.1/","native_build":true,"dependencies":[]}],"android":[{"name":"connectivity_plus","path":"/home/krtirtho/.pub-cache/hosted/pub.dev/connectivity_plus-2.3.9/","native_build":true,"dependencies":[]},{"name":"path_provider_android","path":"/home/krtirtho/.pub-cache/hosted/pub.dev/path_provider_android-2.0.22/","native_build":true,"dependencies":[]}],"macos":[{"name":"connectivity_plus_macos","path":"/home/krtirtho/.pub-cache/hosted/pub.dev/connectivity_plus_macos-1.2.6/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/home/krtirtho/.pub-cache/hosted/pub.dev/path_provider_foundation-2.1.1/","native_build":true,"dependencies":[]}],"linux":[{"name":"connectivity_plus_linux","path":"/home/krtirtho/.pub-cache/hosted/pub.dev/connectivity_plus_linux-1.3.1/","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"/home/krtirtho/.pub-cache/hosted/pub.dev/path_provider_linux-2.1.7/","native_build":false,"dependencies":[]}],"windows":[{"name":"connectivity_plus_windows","path":"/home/krtirtho/.pub-cache/hosted/pub.dev/connectivity_plus_windows-1.2.2/","native_build":true,"dependencies":[]},{"name":"path_provider_windows","path":"/home/krtirtho/.pub-cache/hosted/pub.dev/path_provider_windows-2.1.3/","native_build":false,"dependencies":[]}],"web":[{"name":"connectivity_plus_web","path":"/home/krtirtho/.pub-cache/hosted/pub.dev/connectivity_plus_web-1.2.5/","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":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]}],"date_created":"2023-02-14 13:29:41.761045","version":"3.7.0"}
|
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity_plus","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus-2.3.9/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_foundation-2.1.1/","native_build":true,"dependencies":[]}],"android":[{"name":"connectivity_plus","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus-2.3.9/","native_build":true,"dependencies":[]},{"name":"path_provider_android","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_android-2.0.22/","native_build":true,"dependencies":[]}],"macos":[{"name":"connectivity_plus_macos","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_macos-1.2.6/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_foundation-2.1.1/","native_build":true,"dependencies":[]}],"linux":[{"name":"connectivity_plus_linux","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_linux-1.3.1/","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_linux-2.1.8/","native_build":false,"dependencies":[]}],"windows":[{"name":"connectivity_plus_windows","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_windows-1.2.2/","native_build":true,"dependencies":[]},{"name":"path_provider_windows","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_windows-2.1.3/","native_build":false,"dependencies":[]}],"web":[{"name":"connectivity_plus_web","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_web-1.2.5/","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":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]}],"date_created":"2023-02-21 09:32:52.281806","version":"3.7.3"}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'dart:math';
|
import 'package:example/router.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';
|
||||||
|
|
||||||
@@ -18,54 +17,10 @@ class MainApp extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final value = Random().nextInt(200000);
|
return MaterialApp.router(
|
||||||
return MaterialApp(
|
title: 'FL Query Example',
|
||||||
home: Scaffold(
|
showPerformanceOverlay: true,
|
||||||
floatingActionButton: QueryListenable<String, dynamic, String>(
|
routerConfig: router,
|
||||||
const ValueKey('hello'), builder: (context, query) {
|
|
||||||
if (query == null) {
|
|
||||||
return const SizedBox();
|
|
||||||
}
|
|
||||||
return FloatingActionButton(
|
|
||||||
onPressed: () {
|
|
||||||
query.refresh();
|
|
||||||
},
|
|
||||||
child: Text(query.data ?? 'No Data'),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
body: QueryBuilder<String, dynamic, String>(
|
|
||||||
const ValueKey('hello'),
|
|
||||||
() {
|
|
||||||
return Future.delayed(
|
|
||||||
const Duration(seconds: 6), () => 'Hello World! $value');
|
|
||||||
},
|
|
||||||
initial: 'Hello',
|
|
||||||
jsonConfig: JsonConfig(
|
|
||||||
fromJson: (json) => json['data'],
|
|
||||||
toJson: (data) => {'data': data},
|
|
||||||
),
|
|
||||||
onData: (value) {
|
|
||||||
print('onData: $value');
|
|
||||||
},
|
|
||||||
onError: (error) {
|
|
||||||
print('onError: $error');
|
|
||||||
},
|
|
||||||
builder: (context, query) {
|
|
||||||
if (query.isLoading) {
|
|
||||||
return const Center(
|
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
);
|
|
||||||
} else if (query.hasError) {
|
|
||||||
return Center(
|
|
||||||
child: Text(query.error.toString()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return Center(
|
|
||||||
child: Text(query.data),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
class Product {
|
||||||
|
int id;
|
||||||
|
String title;
|
||||||
|
String description;
|
||||||
|
double price;
|
||||||
|
double discountPercentage;
|
||||||
|
double rating;
|
||||||
|
double stock;
|
||||||
|
String brand;
|
||||||
|
String category;
|
||||||
|
String thumbnail;
|
||||||
|
List<String> images;
|
||||||
|
|
||||||
|
Product({
|
||||||
|
required this.id,
|
||||||
|
required this.title,
|
||||||
|
required this.description,
|
||||||
|
required this.price,
|
||||||
|
required this.discountPercentage,
|
||||||
|
required this.rating,
|
||||||
|
required this.stock,
|
||||||
|
required this.brand,
|
||||||
|
required this.category,
|
||||||
|
required this.thumbnail,
|
||||||
|
required this.images,
|
||||||
|
});
|
||||||
|
|
||||||
|
Product.fromJson(Map<String, dynamic> json)
|
||||||
|
: id = json['id'],
|
||||||
|
title = json['title'],
|
||||||
|
description = json['description'],
|
||||||
|
price = json['price'].toDouble(),
|
||||||
|
discountPercentage = json['discountPercentage'].toDouble(),
|
||||||
|
rating = json['rating'].toDouble(),
|
||||||
|
stock = json['stock'].toDouble(),
|
||||||
|
brand = json['brand'],
|
||||||
|
category = json['category'],
|
||||||
|
thumbnail = json['thumbnail'],
|
||||||
|
images = json['images'].cast<String>();
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = <String, dynamic>{
|
||||||
|
'id': id,
|
||||||
|
'title': title,
|
||||||
|
'description': description,
|
||||||
|
'price': price,
|
||||||
|
'discountPercentage': discountPercentage,
|
||||||
|
'rating': rating,
|
||||||
|
'stock': stock,
|
||||||
|
'brand': brand,
|
||||||
|
'category': category,
|
||||||
|
'thumbnail': thumbnail,
|
||||||
|
'images': images,
|
||||||
|
};
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PagedProducts {
|
||||||
|
List<Product> products;
|
||||||
|
int total;
|
||||||
|
int skip;
|
||||||
|
int limit;
|
||||||
|
|
||||||
|
PagedProducts({
|
||||||
|
this.products = const [],
|
||||||
|
required this.total,
|
||||||
|
required this.skip,
|
||||||
|
required this.limit,
|
||||||
|
});
|
||||||
|
|
||||||
|
PagedProducts.fromJson(Map<String, dynamic> json)
|
||||||
|
: products = json['products']
|
||||||
|
?.map<Product>((v) => Product.fromJson(
|
||||||
|
Map.castFrom<dynamic, dynamic, String, dynamic>(v),
|
||||||
|
))
|
||||||
|
.toList() ??
|
||||||
|
[],
|
||||||
|
total = json['total'],
|
||||||
|
skip = json['skip'],
|
||||||
|
limit = json['limit'];
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = <String, dynamic>{
|
||||||
|
'products': products.map((v) => v.toJson()).toList(),
|
||||||
|
'total': total,
|
||||||
|
'skip': skip,
|
||||||
|
'limit': limit,
|
||||||
|
};
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
class HomePage extends StatelessWidget {
|
||||||
|
const HomePage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('FL Query Example'),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
title: const Text('Query'),
|
||||||
|
onTap: () => GoRouter.of(context).push('/query'),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: const Text('Infinite Query'),
|
||||||
|
onTap: () => GoRouter.of(context).push('/infinite-query'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:example/models/product.dart';
|
||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:http/http.dart';
|
||||||
|
|
||||||
|
class InfiniteQueryPageWidget extends StatefulWidget {
|
||||||
|
const InfiniteQueryPageWidget({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<InfiniteQueryPageWidget> createState() =>
|
||||||
|
_InfiniteQueryPageWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _InfiniteQueryPageWidgetState extends State<InfiniteQueryPageWidget> {
|
||||||
|
final controller = ScrollController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
controller.addListener(() async {
|
||||||
|
if (controller.position.pixels == controller.position.maxScrollExtent) {
|
||||||
|
final query = QueryClient.of(context)
|
||||||
|
.getInfiniteQuery(const ValueKey("products"));
|
||||||
|
await query?.fetchNext();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Infinite Query'),
|
||||||
|
),
|
||||||
|
body: InfiniteQueryBuilder<PagedProducts, ClientException, String, int>(
|
||||||
|
const ValueKey("products"),
|
||||||
|
(page) async {
|
||||||
|
final res = await get(Uri.parse(
|
||||||
|
"https://dummyjson.com/products?limit=10&skip=${page * 10}",
|
||||||
|
));
|
||||||
|
|
||||||
|
if (res.statusCode == 200) {
|
||||||
|
return PagedProducts.fromJson(jsonDecode(res.body));
|
||||||
|
} else {
|
||||||
|
throw ClientException(res.statusCode.toString(), res.request?.url);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nextPage: (lastPage, pages) {
|
||||||
|
if (pages.isNotEmpty &&
|
||||||
|
pages.last.products.length < pages[lastPage].limit) {
|
||||||
|
/// returning [null] will set [hasNextPage] to [false]
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return lastPage + 1;
|
||||||
|
},
|
||||||
|
initialPage: 0,
|
||||||
|
jsonConfig: JsonConfig(
|
||||||
|
fromJson: (json) => PagedProducts.fromJson(json),
|
||||||
|
toJson: (data) => data.toJson(),
|
||||||
|
),
|
||||||
|
builder: (context, query) {
|
||||||
|
final products = query.pages.map((e) => e.products).expand((e) => e);
|
||||||
|
if (!query.hasPages) {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return ListView(
|
||||||
|
controller: controller,
|
||||||
|
children: [
|
||||||
|
for (final product in products)
|
||||||
|
ListTile(
|
||||||
|
title: Text(product.title),
|
||||||
|
subtitle: Text(product.description),
|
||||||
|
leading: Image.network(product.thumbnail),
|
||||||
|
),
|
||||||
|
if (query.hasNextPage)
|
||||||
|
const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
if (query.hasErrors)
|
||||||
|
...query.errors.map((e) => Text(e.message)).toList(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class QueryPage extends StatelessWidget {
|
||||||
|
const QueryPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final value = Random().nextInt(200000);
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Query'),
|
||||||
|
),
|
||||||
|
floatingActionButton: QueryListenable<String, dynamic, String>(
|
||||||
|
const ValueKey('hello'), builder: (context, query) {
|
||||||
|
if (query == null) {
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
return FloatingActionButton(
|
||||||
|
onPressed: () {
|
||||||
|
query.refresh();
|
||||||
|
},
|
||||||
|
child: Text(query.data ?? 'No Data'),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
body: QueryBuilder<String, dynamic, String>(
|
||||||
|
const ValueKey('hello'),
|
||||||
|
() {
|
||||||
|
return Future.delayed(
|
||||||
|
const Duration(seconds: 6), () => 'Hello World! $value');
|
||||||
|
},
|
||||||
|
initial: 'Hello',
|
||||||
|
jsonConfig: JsonConfig(
|
||||||
|
fromJson: (json) => json['data'],
|
||||||
|
toJson: (data) => {'data': data},
|
||||||
|
),
|
||||||
|
onData: (value) {
|
||||||
|
debugPrint('onData: $value');
|
||||||
|
},
|
||||||
|
onError: (error) {
|
||||||
|
debugPrint('onError: $error');
|
||||||
|
},
|
||||||
|
builder: (context, query) {
|
||||||
|
if (query.isLoading) {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
} else if (query.hasError) {
|
||||||
|
return Center(
|
||||||
|
child: Text(query.error.toString()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Center(
|
||||||
|
child: Text(query.data ?? "Unfortunately, there's no data"),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import "package:example/pages/home.dart";
|
||||||
|
import "package:example/pages/infinite_query.dart";
|
||||||
|
import "package:example/pages/query.dart";
|
||||||
|
import "package:go_router/go_router.dart";
|
||||||
|
|
||||||
|
final router = GoRouter(
|
||||||
|
initialLocation: '/',
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: '/',
|
||||||
|
builder: (context, state) => const HomePage(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/query',
|
||||||
|
builder: (context, state) => const QueryPage(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/infinite-query',
|
||||||
|
builder: (context, state) => const InfiniteQueryPageWidget(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
@@ -167,6 +167,14 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
go_router:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: go_router
|
||||||
|
sha256: "7a79232827c851f6b9f08c3c7254a1eaff7bc6aa147e103e51f951c5c10b0ccc"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.0.9"
|
||||||
hive:
|
hive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -183,6 +191,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
|
http:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: http
|
||||||
|
sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.13.5"
|
||||||
|
http_parser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_parser
|
||||||
|
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.2"
|
||||||
js:
|
js:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -199,6 +223,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
|
logging:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: logging
|
||||||
|
sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -445,5 +477,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "6.2.2"
|
version: "6.2.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.19.0 <4.0.0"
|
dart: ">=2.19.0 <3.0.0"
|
||||||
flutter: ">=3.0.0"
|
flutter: ">=3.3.0"
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
fl_query: ^0.3.1
|
fl_query: ^0.3.1
|
||||||
|
go_router: ^6.0.9
|
||||||
|
http: ^0.13.5
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
@@ -13,3 +13,4 @@ export 'src/core/query.dart';
|
|||||||
|
|
||||||
export 'src/widgets/query_builder.dart';
|
export 'src/widgets/query_builder.dart';
|
||||||
export 'src/widgets/query_listenable.dart';
|
export 'src/widgets/query_listenable.dart';
|
||||||
|
export 'src/widgets/infinite_query_builder.dart';
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ abstract class DefaultConstants {
|
|||||||
);
|
);
|
||||||
|
|
||||||
static const RefreshConfig refreshConfig = RefreshConfig(
|
static const RefreshConfig refreshConfig = RefreshConfig(
|
||||||
staleDuration: Duration(seconds: 10),
|
staleDuration: Duration(minutes: 2, milliseconds: 250),
|
||||||
refreshInterval: Duration.zero,
|
refreshInterval: Duration.zero,
|
||||||
refreshOnMount: true,
|
refreshOnMount: false,
|
||||||
refreshOnQueryFnChange: false,
|
refreshOnQueryFnChange: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:fl_query/src/collections/json_config.dart';
|
|||||||
import 'package:fl_query/src/collections/refresh_config.dart';
|
import 'package:fl_query/src/collections/refresh_config.dart';
|
||||||
import 'package:fl_query/src/collections/retry_config.dart';
|
import 'package:fl_query/src/collections/retry_config.dart';
|
||||||
import 'package:fl_query/src/core/cache.dart';
|
import 'package:fl_query/src/core/cache.dart';
|
||||||
|
import 'package:fl_query/src/core/infinite_query.dart';
|
||||||
import 'package:fl_query/src/core/provider.dart';
|
import 'package:fl_query/src/core/provider.dart';
|
||||||
import 'package:fl_query/src/core/query.dart';
|
import 'package:fl_query/src/core/query.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -83,6 +84,98 @@ class QueryClient {
|
|||||||
return await Future.wait(queries.map((query) => query.refresh()));
|
return await Future.wait(queries.map((query) => query.refresh()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InfiniteQuery<DataType, ErrorType, KeyType, PageType>
|
||||||
|
createInfiniteQuery<DataType, ErrorType, KeyType, PageType>(
|
||||||
|
ValueKey<KeyType> key,
|
||||||
|
InfiniteQueryFn<DataType, PageType> queryFn, {
|
||||||
|
required InfiniteQueryNextPage<DataType, PageType> nextPage,
|
||||||
|
required PageType initialParam,
|
||||||
|
RetryConfig retryConfig = DefaultConstants.retryConfig,
|
||||||
|
RefreshConfig refreshConfig = DefaultConstants.refreshConfig,
|
||||||
|
JsonConfig<DataType>? jsonConfig,
|
||||||
|
}) {
|
||||||
|
final query = cache.infiniteQueries
|
||||||
|
.firstWhere(
|
||||||
|
(query) => query.key == key,
|
||||||
|
orElse: () => InfiniteQuery<DataType, ErrorType, KeyType, PageType>(
|
||||||
|
key,
|
||||||
|
queryFn,
|
||||||
|
nextPage: nextPage,
|
||||||
|
initialParam: initialParam,
|
||||||
|
retryConfig: retryConfig,
|
||||||
|
refreshConfig: refreshConfig,
|
||||||
|
jsonConfig: jsonConfig,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.cast<DataType, ErrorType, KeyType, PageType>();
|
||||||
|
query.updateQueryFn(queryFn);
|
||||||
|
cache.addInfiniteQuery(query);
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<DataType?> fetchInfiniteQuery<DataType, ErrorType, KeyType, PageType>(
|
||||||
|
ValueKey<KeyType> key, InfiniteQueryFn<DataType, PageType> queryFn,
|
||||||
|
{required InfiniteQueryNextPage<DataType, PageType> nextPage,
|
||||||
|
required PageType initialParam,
|
||||||
|
RetryConfig retryConfig = DefaultConstants.retryConfig,
|
||||||
|
RefreshConfig refreshConfig = DefaultConstants.refreshConfig,
|
||||||
|
JsonConfig<DataType>? jsonConfig}) async {
|
||||||
|
final query = createInfiniteQuery<DataType, ErrorType, KeyType, PageType>(
|
||||||
|
key,
|
||||||
|
queryFn,
|
||||||
|
nextPage: nextPage,
|
||||||
|
initialParam: initialParam,
|
||||||
|
retryConfig: retryConfig,
|
||||||
|
refreshConfig: refreshConfig,
|
||||||
|
jsonConfig: jsonConfig,
|
||||||
|
);
|
||||||
|
return await query.fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
InfiniteQuery<DataType, ErrorType, KeyType, PageType>?
|
||||||
|
getInfiniteQuery<DataType, ErrorType, KeyType, PageType>(
|
||||||
|
ValueKey<KeyType> key) {
|
||||||
|
return cache.infiniteQueries
|
||||||
|
.firstWhereOrNull((query) => query.key == key)
|
||||||
|
?.cast<DataType, ErrorType, KeyType, PageType>();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<InfiniteQuery> getInfiniteQueries(List<ValueKey> keys) {
|
||||||
|
return cache.infiniteQueries
|
||||||
|
.where((query) => keys.contains(query.key))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<DataType?>
|
||||||
|
refreshInfiniteQuery<DataType, ErrorType, KeyType, PageType>(
|
||||||
|
ValueKey<KeyType> key,
|
||||||
|
[PageType? page]) async {
|
||||||
|
final query = getInfiniteQuery<DataType, ErrorType, KeyType, PageType>(key);
|
||||||
|
if (query == null) return null;
|
||||||
|
return await query.refresh(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<DataType>?>
|
||||||
|
refreshInfiniteQueryAllPages<DataType, ErrorType, KeyType, PageType>(
|
||||||
|
ValueKey<KeyType> key) async {
|
||||||
|
final query = getInfiniteQuery<DataType, ErrorType, KeyType, PageType>(key);
|
||||||
|
if (query == null) return [];
|
||||||
|
return await query.refreshAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List> refreshInfiniteQueries(List<ValueKey> keys) async {
|
||||||
|
final queries = getInfiniteQueries(keys);
|
||||||
|
return await Future.wait(queries.map((query) => query.refresh()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<ValueKey, List?>> refreshInfiniteQueriesAllPages(
|
||||||
|
List<ValueKey> keys) async {
|
||||||
|
final queries = getInfiniteQueries(keys);
|
||||||
|
return await Future.wait(queries.map(
|
||||||
|
(query) async => MapEntry(query.key, await query.refreshAll())))
|
||||||
|
.then((qs) => Map.fromEntries(qs));
|
||||||
|
}
|
||||||
|
|
||||||
static QueryClient of(BuildContext context) {
|
static QueryClient of(BuildContext context) {
|
||||||
return context
|
return context
|
||||||
.dependOnInheritedWidgetOfExactType<QueryClientProvider>()!
|
.dependOnInheritedWidgetOfExactType<QueryClientProvider>()!
|
||||||
|
|||||||
@@ -64,17 +64,20 @@ class InfiniteQueryPage<DataType, ErrorType, PageType> {
|
|||||||
class InfiniteQueryState<DataType, ErrorType, PageType> {
|
class InfiniteQueryState<DataType, ErrorType, PageType> {
|
||||||
final Set<InfiniteQueryPage<DataType, ErrorType, PageType>> pages;
|
final Set<InfiniteQueryPage<DataType, ErrorType, PageType>> pages;
|
||||||
final InfiniteQueryFn<DataType, PageType> queryFn;
|
final InfiniteQueryFn<DataType, PageType> queryFn;
|
||||||
final InfiniteQueryNextPage<DataType, PageType> nextPage;
|
final InfiniteQueryNextPage<DataType, PageType> _nextPage;
|
||||||
|
|
||||||
const InfiniteQueryState({
|
const InfiniteQueryState({
|
||||||
required this.pages,
|
required this.pages,
|
||||||
required this.queryFn,
|
required this.queryFn,
|
||||||
required this.nextPage,
|
required InfiniteQueryNextPage<DataType, PageType> nextPage,
|
||||||
});
|
}) : _nextPage = nextPage;
|
||||||
|
|
||||||
PageType get lastPage => pages.last.page;
|
PageType get lastPage => pages.last.page;
|
||||||
PageType? get getNextPage =>
|
PageType? get getNextPage => _nextPage(
|
||||||
nextPage(lastPage, pages.map((e) => e.data!).toList());
|
lastPage,
|
||||||
|
pages.map((e) => e.data).whereType<DataType>().toList(),
|
||||||
|
);
|
||||||
|
|
||||||
bool get hasNextPage => getNextPage != null;
|
bool get hasNextPage => getNextPage != null;
|
||||||
|
|
||||||
InfiniteQueryState<DataType, ErrorType, PageType> copyWith({
|
InfiniteQueryState<DataType, ErrorType, PageType> copyWith({
|
||||||
@@ -85,11 +88,23 @@ class InfiniteQueryState<DataType, ErrorType, PageType> {
|
|||||||
return InfiniteQueryState<DataType, ErrorType, PageType>(
|
return InfiniteQueryState<DataType, ErrorType, PageType>(
|
||||||
pages: pages ?? this.pages,
|
pages: pages ?? this.pages,
|
||||||
queryFn: queryFn ?? this.queryFn,
|
queryFn: queryFn ?? this.queryFn,
|
||||||
nextPage: nextPage ?? this.nextPage,
|
nextPage: nextPage ?? this._nextPage,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PageEvent<T, P> {
|
||||||
|
final P page;
|
||||||
|
final T data;
|
||||||
|
const PageEvent(this.page, this.data);
|
||||||
|
|
||||||
|
factory PageEvent.fromPage(
|
||||||
|
InfiniteQueryPage page,
|
||||||
|
) {
|
||||||
|
return PageEvent(page.page as P, page.data as T);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class InfiniteQuery<DataType, ErrorType, KeyType, PageType>
|
class InfiniteQuery<DataType, ErrorType, KeyType, PageType>
|
||||||
extends StateNotifier<InfiniteQueryState<DataType, ErrorType, PageType>>
|
extends StateNotifier<InfiniteQueryState<DataType, ErrorType, PageType>>
|
||||||
with Retryer<DataType, ErrorType> {
|
with Retryer<DataType, ErrorType> {
|
||||||
@@ -106,7 +121,9 @@ class InfiniteQuery<DataType, ErrorType, KeyType, PageType>
|
|||||||
this.retryConfig = DefaultConstants.retryConfig,
|
this.retryConfig = DefaultConstants.retryConfig,
|
||||||
this.refreshConfig = DefaultConstants.refreshConfig,
|
this.refreshConfig = DefaultConstants.refreshConfig,
|
||||||
this.jsonConfig,
|
this.jsonConfig,
|
||||||
}) : super(InfiniteQueryState<DataType, ErrorType, PageType>(
|
}) : _dataController = StreamController.broadcast(),
|
||||||
|
_errorController = StreamController.broadcast(),
|
||||||
|
super(InfiniteQueryState<DataType, ErrorType, PageType>(
|
||||||
pages: {
|
pages: {
|
||||||
InfiniteQueryPage<DataType, ErrorType, PageType>(
|
InfiniteQueryPage<DataType, ErrorType, PageType>(
|
||||||
page: initialParam,
|
page: initialParam,
|
||||||
@@ -119,17 +136,30 @@ class InfiniteQuery<DataType, ErrorType, KeyType, PageType>
|
|||||||
)) {
|
)) {
|
||||||
if (jsonConfig != null) {
|
if (jsonConfig != null) {
|
||||||
_mutex.protect(() async {
|
_mutex.protect(() async {
|
||||||
final json = await _box.get(key.toString());
|
final Map? json = await _box.get(key.value);
|
||||||
if (json != null) {
|
if (json != null) {
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
pages: json.map(
|
pages: json.entries
|
||||||
(key, value) => MapEntry(
|
.map(
|
||||||
key as PageType,
|
(entry) => InfiniteQueryPage<DataType, ErrorType, PageType>(
|
||||||
jsonConfig!.fromJson(value),
|
page: entry.key as PageType,
|
||||||
),
|
data: jsonConfig!.fromJson(
|
||||||
),
|
Map.castFrom<dynamic, dynamic, String, dynamic>(
|
||||||
|
entry.value,
|
||||||
|
)),
|
||||||
|
// this makes the page loaded from cache `stale`
|
||||||
|
updatedAt:
|
||||||
|
DateTime.now().subtract(refreshConfig.staleDuration),
|
||||||
|
staleDuration: refreshConfig.staleDuration,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toSet(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}).then((_) {
|
||||||
|
if (hasListeners) {
|
||||||
|
return fetch();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (refreshConfig.refreshInterval > Duration.zero)
|
if (refreshConfig.refreshInterval > Duration.zero)
|
||||||
@@ -146,12 +176,18 @@ class InfiniteQuery<DataType, ErrorType, KeyType, PageType>
|
|||||||
|
|
||||||
final _mutex = Mutex();
|
final _mutex = Mutex();
|
||||||
final _box = Hive.lazyBox("cache");
|
final _box = Hive.lazyBox("cache");
|
||||||
|
final StreamController<PageEvent<DataType, PageType>> _dataController;
|
||||||
|
final StreamController<PageEvent<ErrorType, PageType>> _errorController;
|
||||||
|
|
||||||
List<DataType> get pages =>
|
List<DataType> get pages =>
|
||||||
state.pages.map((e) => e.data).whereType<DataType>().toList();
|
state.pages.map((e) => e.data).whereType<DataType>().toList();
|
||||||
List<ErrorType> get errors =>
|
List<ErrorType> get errors =>
|
||||||
state.pages.map((e) => e.error).whereType<ErrorType>().toList();
|
state.pages.map((e) => e.error).whereType<ErrorType>().toList();
|
||||||
PageType get lastPage => state.lastPage;
|
PageType get lastPage => state.lastPage;
|
||||||
|
Stream<PageEvent<DataType, PageType>> get dataStream =>
|
||||||
|
_dataController.stream;
|
||||||
|
Stream<PageEvent<ErrorType, PageType>> get errorStream =>
|
||||||
|
_errorController.stream;
|
||||||
|
|
||||||
bool get isLoadingPage => !hasPageData && !hasPageError && _mutex.isLocked;
|
bool get isLoadingPage => !hasPageData && !hasPageError && _mutex.isLocked;
|
||||||
bool get isRefreshingPage => (hasPageData || hasPageError) && _mutex.isLocked;
|
bool get isRefreshingPage => (hasPageData || hasPageError) && _mutex.isLocked;
|
||||||
@@ -160,8 +196,8 @@ class InfiniteQuery<DataType, ErrorType, KeyType, PageType>
|
|||||||
bool get hasPages => pages.isNotEmpty;
|
bool get hasPages => pages.isNotEmpty;
|
||||||
bool get hasErrors => errors.isNotEmpty;
|
bool get hasErrors => errors.isNotEmpty;
|
||||||
|
|
||||||
bool get hasPageData => state.pages.last.data != null;
|
bool get hasPageData => !hasPages ? false : state.pages.last.data != null;
|
||||||
bool get hasPageError => state.pages.last.error != null;
|
bool get hasPageError => !hasPages ? false : state.pages.last.error != null;
|
||||||
|
|
||||||
bool get hasNextPage => state.hasNextPage;
|
bool get hasNextPage => state.hasNextPage;
|
||||||
|
|
||||||
@@ -171,51 +207,54 @@ class InfiniteQuery<DataType, ErrorType, KeyType, PageType>
|
|||||||
() => state.queryFn(page),
|
() => state.queryFn(page),
|
||||||
config: retryConfig,
|
config: retryConfig,
|
||||||
onSuccessful: (data) async {
|
onSuccessful: (data) async {
|
||||||
|
final dataPage = state.pages
|
||||||
|
.firstWhere(
|
||||||
|
(e) => e.page == page,
|
||||||
|
orElse: () => InfiniteQueryPage<DataType, ErrorType, PageType>(
|
||||||
|
page: page,
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
staleDuration: refreshConfig.staleDuration,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.copyWith(data: data);
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
pages: {
|
pages: {...state.pages..remove(dataPage), dataPage},
|
||||||
...state.pages,
|
|
||||||
state.pages
|
|
||||||
.firstWhere(
|
|
||||||
(e) => e.page == page,
|
|
||||||
orElse: () =>
|
|
||||||
InfiniteQueryPage<DataType, ErrorType, PageType>(
|
|
||||||
page: page,
|
|
||||||
updatedAt: DateTime.now(),
|
|
||||||
staleDuration: refreshConfig.staleDuration,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.copyWith(data: data),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
if (dataPage.data != null)
|
||||||
|
_dataController.add(PageEvent.fromPage(dataPage));
|
||||||
if (jsonConfig != null) {
|
if (jsonConfig != null) {
|
||||||
await _box.put(
|
await _box.put(
|
||||||
key.toString(),
|
key.value,
|
||||||
state.pages.map(
|
Map.fromEntries(
|
||||||
(e) => MapEntry(
|
state.pages.map(
|
||||||
e.page,
|
(e) => MapEntry(
|
||||||
e.data != null ? jsonConfig!.toJson(e.data!) : null,
|
e.page,
|
||||||
|
e.data != null ? jsonConfig!.toJson(e.data!) : null,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onFailed: (error) {
|
onFailed: (error) {
|
||||||
|
final errorPage = state.pages
|
||||||
|
.firstWhere(
|
||||||
|
(e) => e.page == page,
|
||||||
|
orElse: () => InfiniteQueryPage<DataType, ErrorType, PageType>(
|
||||||
|
page: page,
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
staleDuration: refreshConfig.staleDuration,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.copyWith(error: error);
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
pages: {
|
pages: {
|
||||||
...state.pages,
|
...state.pages..remove(errorPage),
|
||||||
state.pages
|
errorPage,
|
||||||
.firstWhere(
|
|
||||||
(e) => e.page == page,
|
|
||||||
orElse: () =>
|
|
||||||
InfiniteQueryPage<DataType, ErrorType, PageType>(
|
|
||||||
page: page,
|
|
||||||
updatedAt: DateTime.now(),
|
|
||||||
staleDuration: refreshConfig.staleDuration,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.copyWith(error: error),
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
if (errorPage.error != null)
|
||||||
|
_errorController.add(PageEvent.fromPage(errorPage));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -246,14 +285,54 @@ class InfiniteQuery<DataType, ErrorType, KeyType, PageType>
|
|||||||
|
|
||||||
Future<DataType?> fetchNext() async {
|
Future<DataType?> fetchNext() async {
|
||||||
final nextPage = state.getNextPage;
|
final nextPage = state.getNextPage;
|
||||||
if (_mutex.isLocked || nextPage != null) {
|
if (_mutex.isLocked || nextPage == null) {
|
||||||
return state.pages.firstWhereOrNull((e) => e.page == nextPage)?.data;
|
return state.pages.lastOrNull?.data;
|
||||||
}
|
}
|
||||||
return await _operation(nextPage!).then((_) {
|
return await _operation(nextPage).then((_) {
|
||||||
return state.pages.firstWhereOrNull((e) => e.page == nextPage)?.data;
|
return state.pages.firstWhereOrNull((e) => e.page == nextPage)?.data;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateQueryFn(InfiniteQueryFn<DataType, PageType> queryFn) {
|
||||||
|
if (state.queryFn == queryFn) return;
|
||||||
|
state = state.copyWith(queryFn: queryFn);
|
||||||
|
if (refreshConfig.refreshOnQueryFnChange) {
|
||||||
|
refreshAll();
|
||||||
|
} else {
|
||||||
|
Future.wait(
|
||||||
|
state.pages.map((page) async {
|
||||||
|
if (page.isStale) {
|
||||||
|
return await refresh(page.page);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateNextPageFn(InfiniteQueryNextPage<DataType, PageType> nextPage) {
|
||||||
|
state = state.copyWith(nextPage: nextPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPageData(PageType page, DataType data) {
|
||||||
|
final newPage = state.pages
|
||||||
|
.firstWhere(
|
||||||
|
(e) => e.page == page,
|
||||||
|
orElse: () => InfiniteQueryPage<DataType, ErrorType, PageType>(
|
||||||
|
page: page,
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
staleDuration: refreshConfig.staleDuration,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.copyWith(data: data);
|
||||||
|
|
||||||
|
state = state.copyWith(
|
||||||
|
pages: {
|
||||||
|
...state.pages..remove(newPage),
|
||||||
|
newPage,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RemoveListener addListener(
|
RemoveListener addListener(
|
||||||
Listener<InfiniteQueryState<DataType, ErrorType, PageType>> listener, {
|
Listener<InfiniteQueryState<DataType, ErrorType, PageType>> listener, {
|
||||||
@@ -276,4 +355,13 @@ class InfiniteQuery<DataType, ErrorType, KeyType, PageType>
|
|||||||
@override
|
@override
|
||||||
operator ==(Object other) =>
|
operator ==(Object other) =>
|
||||||
identical(this, other) || other is InfiniteQuery && key == other.key;
|
identical(this, other) || other is InfiniteQuery && key == other.key;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => key.hashCode;
|
||||||
|
|
||||||
|
InfiniteQuery<NewDataType, NewErrorType, NewKeyType, NewPageType>
|
||||||
|
cast<NewDataType, NewErrorType, NewKeyType, NewPageType>() {
|
||||||
|
return this
|
||||||
|
as InfiniteQuery<NewDataType, NewErrorType, NewKeyType, NewPageType>;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,10 +35,11 @@ class QueryState<DataType, ErrorType> {
|
|||||||
QueryState<DataType, ErrorType> copyWith({
|
QueryState<DataType, ErrorType> copyWith({
|
||||||
DataType? data,
|
DataType? data,
|
||||||
ErrorType? error,
|
ErrorType? error,
|
||||||
|
DateTime? updatedAt,
|
||||||
QueryFn<DataType>? queryFn,
|
QueryFn<DataType>? queryFn,
|
||||||
}) {
|
}) {
|
||||||
return QueryState<DataType, ErrorType>(
|
return QueryState<DataType, ErrorType>(
|
||||||
updatedAt: DateTime.now(),
|
updatedAt: updatedAt ?? this.updatedAt,
|
||||||
staleDuration: staleDuration,
|
staleDuration: staleDuration,
|
||||||
data: data ?? this.data,
|
data: data ?? this.data,
|
||||||
error: error ?? this.error,
|
error: error ?? this.error,
|
||||||
@@ -80,7 +81,7 @@ class Query<DataType, ErrorType, KeyType>
|
|||||||
_initial = jsonConfig!.fromJson(
|
_initial = jsonConfig!.fromJson(
|
||||||
Map.castFrom<dynamic, dynamic, String, dynamic>(json),
|
Map.castFrom<dynamic, dynamic, String, dynamic>(json),
|
||||||
);
|
);
|
||||||
state = state.copyWith(data: _initial);
|
state = state.copyWith(data: _initial, updatedAt: DateTime.now());
|
||||||
}
|
}
|
||||||
}).then((_) {
|
}).then((_) {
|
||||||
if (hasListeners) {
|
if (hasListeners) {
|
||||||
@@ -124,7 +125,10 @@ class Query<DataType, ErrorType, KeyType>
|
|||||||
state.queryFn,
|
state.queryFn,
|
||||||
config: retryConfig,
|
config: retryConfig,
|
||||||
onSuccessful: (DataType? data) {
|
onSuccessful: (DataType? data) {
|
||||||
state = state.copyWith(data: data);
|
state = state.copyWith(
|
||||||
|
data: data,
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
);
|
||||||
if (data != null) _dataController.add(data);
|
if (data != null) _dataController.add(data);
|
||||||
if (jsonConfig != null && data != null) {
|
if (jsonConfig != null && data != null) {
|
||||||
_box.put(
|
_box.put(
|
||||||
@@ -134,7 +138,7 @@ class Query<DataType, ErrorType, KeyType>
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onFailed: (ErrorType? error) {
|
onFailed: (ErrorType? error) {
|
||||||
state = state.copyWith(error: error);
|
state = state.copyWith(error: error, updatedAt: DateTime.now());
|
||||||
if (error != null) _errorController.add(error);
|
if (error != null) _errorController.add(error);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -154,17 +158,14 @@ class Query<DataType, ErrorType, KeyType>
|
|||||||
|
|
||||||
void updateQueryFn(QueryFn<DataType> queryFn) {
|
void updateQueryFn(QueryFn<DataType> queryFn) {
|
||||||
if (state.queryFn == queryFn) return;
|
if (state.queryFn == queryFn) return;
|
||||||
// updatedAt is updated with copyWith so storing it
|
|
||||||
// here to check if the query is stale later
|
|
||||||
final stale = state.isStale;
|
|
||||||
state = state.copyWith(queryFn: queryFn);
|
state = state.copyWith(queryFn: queryFn);
|
||||||
if (stale || refreshConfig.refreshOnQueryFnChange) {
|
if (state.isStale || refreshConfig.refreshOnQueryFnChange) {
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setData(DataType data) {
|
void setData(DataType data) {
|
||||||
state = state.copyWith(data: data);
|
state = state.copyWith(data: data, updatedAt: DateTime.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:fl_query/src/collections/retry_config.dart';
|
import 'package:fl_query/src/collections/retry_config.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
mixin Retryer<T, E> {
|
mixin Retryer<T, E> {
|
||||||
void retryOperation(
|
void retryOperation(
|
||||||
@@ -19,9 +20,20 @@ mixin Retryer<T, E> {
|
|||||||
final result = await completer.future;
|
final result = await completer.future;
|
||||||
onSuccessful(result);
|
onSuccessful(result);
|
||||||
break;
|
break;
|
||||||
} catch (e) {
|
} catch (e, stack) {
|
||||||
if (attempts == config.maxRetries - 1 && e is E?) {
|
if (attempts == config.maxRetries - 1) {
|
||||||
onFailed(e as E?);
|
if (e is E?) {
|
||||||
|
onFailed(e as E?);
|
||||||
|
} else {
|
||||||
|
FlutterError.reportError(
|
||||||
|
FlutterErrorDetails(
|
||||||
|
exception: e,
|
||||||
|
library: 'fl_query',
|
||||||
|
context: ErrorDescription('retryOperation'),
|
||||||
|
stack: stack,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,148 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:fl_query/src/collections/default_configs.dart';
|
||||||
|
import 'package:fl_query/src/collections/json_config.dart';
|
||||||
|
import 'package:fl_query/src/collections/refresh_config.dart';
|
||||||
|
import 'package:fl_query/src/collections/retry_config.dart';
|
||||||
|
import 'package:fl_query/src/core/client.dart';
|
||||||
|
import 'package:fl_query/src/core/infinite_query.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
typedef InfiniteQueryBuilderFn<DataType, ErrorType, KeyType, PageType> = Widget
|
||||||
|
Function(
|
||||||
|
BuildContext context,
|
||||||
|
InfiniteQuery<DataType, ErrorType, KeyType, PageType> query,
|
||||||
|
);
|
||||||
|
|
||||||
|
class InfiniteQueryBuilder<DataType, ErrorType, KeyType, PageType>
|
||||||
|
extends StatefulWidget {
|
||||||
|
final InfiniteQueryFn<DataType, PageType> queryFn;
|
||||||
|
final ValueKey<KeyType> queryKey;
|
||||||
|
|
||||||
|
final PageType initialPage;
|
||||||
|
final InfiniteQueryNextPage<DataType, PageType> nextPage;
|
||||||
|
|
||||||
|
final RetryConfig retryConfig;
|
||||||
|
final RefreshConfig refreshConfig;
|
||||||
|
final JsonConfig<DataType>? jsonConfig;
|
||||||
|
|
||||||
|
final ValueChanged<PageEvent<DataType, PageType>>? onData;
|
||||||
|
final ValueChanged<PageEvent<ErrorType, PageType>>? onError;
|
||||||
|
|
||||||
|
// widget specific
|
||||||
|
final bool enabled;
|
||||||
|
final InfiniteQueryBuilderFn<DataType, ErrorType, KeyType, PageType> builder;
|
||||||
|
|
||||||
|
const InfiniteQueryBuilder(
|
||||||
|
this.queryKey,
|
||||||
|
this.queryFn, {
|
||||||
|
required this.nextPage,
|
||||||
|
required this.builder,
|
||||||
|
required this.initialPage,
|
||||||
|
this.retryConfig = DefaultConstants.retryConfig,
|
||||||
|
this.refreshConfig = DefaultConstants.refreshConfig,
|
||||||
|
this.jsonConfig,
|
||||||
|
this.onData,
|
||||||
|
this.onError,
|
||||||
|
this.enabled = true,
|
||||||
|
super.key,
|
||||||
|
}) : assert(
|
||||||
|
(jsonConfig != null && enabled) || jsonConfig == null,
|
||||||
|
'jsonConfig is only supported when enabled is true',
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<InfiniteQueryBuilder<DataType, ErrorType, KeyType, PageType>>
|
||||||
|
createState() =>
|
||||||
|
_InfiniteQueryBuilderState<DataType, ErrorType, KeyType, PageType>();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _InfiniteQueryBuilderState<DataType, ErrorType, KeyType, PageType>
|
||||||
|
extends State<
|
||||||
|
InfiniteQueryBuilder<DataType, ErrorType, KeyType, PageType>> {
|
||||||
|
InfiniteQuery<DataType, ErrorType, KeyType, PageType>? query;
|
||||||
|
|
||||||
|
VoidCallback? removeListener;
|
||||||
|
|
||||||
|
StreamSubscription<PageEvent<DataType, PageType>>? dataSubscription;
|
||||||
|
StreamSubscription<PageEvent<ErrorType, PageType>>? errorSubscription;
|
||||||
|
|
||||||
|
void update(_) {
|
||||||
|
if (mounted) setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> initialize() async {
|
||||||
|
setState(() {
|
||||||
|
query = QueryClient.of(context).createInfiniteQuery(
|
||||||
|
widget.queryKey,
|
||||||
|
widget.queryFn,
|
||||||
|
initialParam: widget.initialPage,
|
||||||
|
nextPage: widget.nextPage,
|
||||||
|
retryConfig: widget.retryConfig,
|
||||||
|
refreshConfig: widget.refreshConfig,
|
||||||
|
jsonConfig: widget.jsonConfig,
|
||||||
|
);
|
||||||
|
|
||||||
|
dataSubscription = query!.dataStream.listen(widget.onData);
|
||||||
|
errorSubscription = query!.errorStream.listen(widget.onError);
|
||||||
|
|
||||||
|
removeListener = query!.addListener(update);
|
||||||
|
});
|
||||||
|
if (widget.enabled) {
|
||||||
|
await query!.fetch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
initialize();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
dataSubscription?.cancel();
|
||||||
|
errorSubscription?.cancel();
|
||||||
|
removeListener?.call();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
|
||||||
|
if (oldWidget.queryKey != widget.queryKey) {
|
||||||
|
dataSubscription?.cancel();
|
||||||
|
errorSubscription?.cancel();
|
||||||
|
removeListener?.call();
|
||||||
|
initialize();
|
||||||
|
return;
|
||||||
|
} else if (oldWidget.enabled != widget.enabled && widget.enabled) {
|
||||||
|
query!.fetch();
|
||||||
|
}
|
||||||
|
if (oldWidget.queryFn != widget.queryFn) {
|
||||||
|
query!.updateQueryFn(widget.queryFn);
|
||||||
|
}
|
||||||
|
if (oldWidget.nextPage != widget.nextPage) {
|
||||||
|
query!.updateNextPageFn(widget.nextPage);
|
||||||
|
}
|
||||||
|
if (oldWidget.onData != widget.onData) {
|
||||||
|
dataSubscription?.cancel();
|
||||||
|
dataSubscription = query!.dataStream.listen(widget.onData);
|
||||||
|
}
|
||||||
|
if (oldWidget.onError != widget.onError) {
|
||||||
|
errorSubscription?.cancel();
|
||||||
|
errorSubscription = query!.errorStream.listen(widget.onError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (query == null) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
return widget.builder(context, query!);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,7 +18,6 @@ class QueryBuilder<DataType, ErrorType, KeyType> extends StatefulWidget {
|
|||||||
final QueryFn<DataType> queryFn;
|
final QueryFn<DataType> queryFn;
|
||||||
final ValueKey<KeyType> queryKey;
|
final ValueKey<KeyType> queryKey;
|
||||||
|
|
||||||
final QueryBuilderFn builder;
|
|
||||||
final DataType? initial;
|
final DataType? initial;
|
||||||
|
|
||||||
final RetryConfig retryConfig;
|
final RetryConfig retryConfig;
|
||||||
@@ -30,6 +29,7 @@ class QueryBuilder<DataType, ErrorType, KeyType> extends StatefulWidget {
|
|||||||
|
|
||||||
// widget specific
|
// widget specific
|
||||||
final bool enabled;
|
final bool enabled;
|
||||||
|
final QueryBuilderFn<DataType, ErrorType, KeyType> builder;
|
||||||
|
|
||||||
const QueryBuilder(
|
const QueryBuilder(
|
||||||
this.queryKey,
|
this.queryKey,
|
||||||
@@ -44,7 +44,7 @@ class QueryBuilder<DataType, ErrorType, KeyType> extends StatefulWidget {
|
|||||||
this.enabled = true,
|
this.enabled = true,
|
||||||
super.key,
|
super.key,
|
||||||
}) : assert(
|
}) : assert(
|
||||||
enabled && jsonConfig != null,
|
(jsonConfig != null && enabled) || jsonConfig == null,
|
||||||
'jsonConfig is only supported when enabled is true',
|
'jsonConfig is only supported when enabled is true',
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ class _QueryBuilderState<DataType, ErrorType, KeyType>
|
|||||||
StreamSubscription<ErrorType>? errorSubscription;
|
StreamSubscription<ErrorType>? errorSubscription;
|
||||||
|
|
||||||
void update(_) {
|
void update(_) {
|
||||||
setState(() {});
|
if (mounted) setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initialize() async {
|
Future<void> initialize() async {
|
||||||
@@ -115,6 +115,8 @@ class _QueryBuilderState<DataType, ErrorType, KeyType>
|
|||||||
removeListener?.call();
|
removeListener?.call();
|
||||||
initialize();
|
initialize();
|
||||||
return;
|
return;
|
||||||
|
} else if (oldWidget.enabled != widget.enabled && widget.enabled) {
|
||||||
|
query!.fetch();
|
||||||
}
|
}
|
||||||
if (oldWidget.queryFn != widget.queryFn) {
|
if (oldWidget.queryFn != widget.queryFn) {
|
||||||
query!.updateQueryFn(widget.queryFn);
|
query!.updateQueryFn(widget.queryFn);
|
||||||
@@ -147,7 +149,8 @@ class _QueryBuilderState<DataType, ErrorType, KeyType>
|
|||||||
DiagnosticsProperty<ValueKey<KeyType>>('queryKey', widget.queryKey),
|
DiagnosticsProperty<ValueKey<KeyType>>('queryKey', widget.queryKey),
|
||||||
);
|
);
|
||||||
properties.add(
|
properties.add(
|
||||||
DiagnosticsProperty<QueryBuilderFn>('builder', widget.builder),
|
DiagnosticsProperty<QueryBuilderFn<DataType, ErrorType, KeyType>>(
|
||||||
|
'builder', widget.builder),
|
||||||
);
|
);
|
||||||
properties.add(DiagnosticsProperty<DataType>('initial', widget.initial));
|
properties.add(DiagnosticsProperty<DataType>('initial', widget.initial));
|
||||||
properties.add(
|
properties.add(
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class _StateNotifierListenableState<T>
|
|||||||
|
|
||||||
void initialize() {
|
void initialize() {
|
||||||
removeListener = widget.notifier.addListener((_) {
|
removeListener = widget.notifier.addListener((_) {
|
||||||
setState(() {});
|
if (mounted) setState(() {});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# This is a generated file; do not edit or check into version control.
|
# This is a generated file; do not edit or check into version control.
|
||||||
connectivity_plus=/home/krtirtho/.pub-cache/hosted/pub.dev/connectivity_plus-2.3.9/
|
connectivity_plus=/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus-2.3.9/
|
||||||
connectivity_plus_linux=/home/krtirtho/.pub-cache/hosted/pub.dev/connectivity_plus_linux-1.3.1/
|
connectivity_plus_linux=/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_linux-1.3.1/
|
||||||
connectivity_plus_macos=/home/krtirtho/.pub-cache/hosted/pub.dev/connectivity_plus_macos-1.2.6/
|
connectivity_plus_macos=/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_macos-1.2.6/
|
||||||
connectivity_plus_web=/home/krtirtho/.pub-cache/hosted/pub.dev/connectivity_plus_web-1.2.5/
|
connectivity_plus_web=/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_web-1.2.5/
|
||||||
connectivity_plus_windows=/home/krtirtho/.pub-cache/hosted/pub.dev/connectivity_plus_windows-1.2.2/
|
connectivity_plus_windows=/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_windows-1.2.2/
|
||||||
path_provider=/home/krtirtho/.pub-cache/hosted/pub.dev/path_provider-2.0.12/
|
path_provider=/home/kingkor/.pub-cache/hosted/pub.dev/path_provider-2.0.12/
|
||||||
path_provider_android=/home/krtirtho/.pub-cache/hosted/pub.dev/path_provider_android-2.0.22/
|
path_provider_android=/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_android-2.0.22/
|
||||||
path_provider_foundation=/home/krtirtho/.pub-cache/hosted/pub.dev/path_provider_foundation-2.1.1/
|
path_provider_foundation=/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_foundation-2.1.1/
|
||||||
path_provider_linux=/home/krtirtho/.pub-cache/hosted/pub.dev/path_provider_linux-2.1.7/
|
path_provider_linux=/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_linux-2.1.8/
|
||||||
path_provider_windows=/home/krtirtho/.pub-cache/hosted/pub.dev/path_provider_windows-2.1.3/
|
path_provider_windows=/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_windows-2.1.3/
|
||||||
|
|||||||
@@ -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.dev/connectivity_plus-2.3.9/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/home/krtirtho/.pub-cache/hosted/pub.dev/path_provider_foundation-2.1.1/","native_build":true,"dependencies":[]}],"android":[{"name":"connectivity_plus","path":"/home/krtirtho/.pub-cache/hosted/pub.dev/connectivity_plus-2.3.9/","native_build":true,"dependencies":[]},{"name":"path_provider_android","path":"/home/krtirtho/.pub-cache/hosted/pub.dev/path_provider_android-2.0.22/","native_build":true,"dependencies":[]}],"macos":[{"name":"connectivity_plus_macos","path":"/home/krtirtho/.pub-cache/hosted/pub.dev/connectivity_plus_macos-1.2.6/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/home/krtirtho/.pub-cache/hosted/pub.dev/path_provider_foundation-2.1.1/","native_build":true,"dependencies":[]}],"linux":[{"name":"connectivity_plus_linux","path":"/home/krtirtho/.pub-cache/hosted/pub.dev/connectivity_plus_linux-1.3.1/","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"/home/krtirtho/.pub-cache/hosted/pub.dev/path_provider_linux-2.1.7/","native_build":false,"dependencies":[]}],"windows":[{"name":"connectivity_plus_windows","path":"/home/krtirtho/.pub-cache/hosted/pub.dev/connectivity_plus_windows-1.2.2/","native_build":true,"dependencies":[]},{"name":"path_provider_windows","path":"/home/krtirtho/.pub-cache/hosted/pub.dev/path_provider_windows-2.1.3/","native_build":false,"dependencies":[]}],"web":[{"name":"connectivity_plus_web","path":"/home/krtirtho/.pub-cache/hosted/pub.dev/connectivity_plus_web-1.2.5/","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":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]}],"date_created":"2023-02-14 13:29:34.103321","version":"3.7.0"}
|
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity_plus","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus-2.3.9/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_foundation-2.1.1/","native_build":true,"dependencies":[]}],"android":[{"name":"connectivity_plus","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus-2.3.9/","native_build":true,"dependencies":[]},{"name":"path_provider_android","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_android-2.0.22/","native_build":true,"dependencies":[]}],"macos":[{"name":"connectivity_plus_macos","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_macos-1.2.6/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_foundation-2.1.1/","native_build":true,"dependencies":[]}],"linux":[{"name":"connectivity_plus_linux","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_linux-1.3.1/","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_linux-2.1.8/","native_build":false,"dependencies":[]}],"windows":[{"name":"connectivity_plus_windows","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_windows-1.2.2/","native_build":true,"dependencies":[]},{"name":"path_provider_windows","path":"/home/kingkor/.pub-cache/hosted/pub.dev/path_provider_windows-2.1.3/","native_build":false,"dependencies":[]}],"web":[{"name":"connectivity_plus_web","path":"/home/kingkor/.pub-cache/hosted/pub.dev/connectivity_plus_web-1.2.5/","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":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]}],"date_created":"2023-02-21 09:32:49.296117","version":"3.7.3"}
|
||||||
Reference in New Issue
Block a user