removed dependOnQuery from beacuse of infinite renders
example created for various usecases with various features hooks now use standard hook API instead of using Hook Class variable query & mutation not working bug fix mutation onMutate listeners not getting called bug fix
This commit is contained in:
@@ -1,17 +0,0 @@
|
|||||||
import 'package:fl_query/fl_query.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class AnotherComponent extends StatelessWidget {
|
|
||||||
const AnotherComponent({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final lol = QueryBowl.of(context).getQuery<String, void>("greetings");
|
|
||||||
final deadQuery =
|
|
||||||
QueryBowl.of(context).getQuery<String, String>("external_data");
|
|
||||||
if (lol?.data == null) return const CircularProgressIndicator();
|
|
||||||
return Text(
|
|
||||||
"${lol!.data!} from AnotherComponent\nDeadQuery (It should be null after 10 seconds): ${deadQuery?.data}",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:example/components/basic_query.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
final basicMutationJob = MutationJob<Map, Map<String, dynamic>>(
|
||||||
|
mutationKey: "basic-mutation-example",
|
||||||
|
task: (key, data) async {
|
||||||
|
final response = await http.post(
|
||||||
|
Uri.parse(
|
||||||
|
// to simulate a failing response environment
|
||||||
|
Random().nextBool()
|
||||||
|
? "https://jsonplaceholder.typicode.com/posts"
|
||||||
|
: "https://google.com",
|
||||||
|
),
|
||||||
|
headers: {'Content-type': 'application/json; charset=UTF-8'},
|
||||||
|
body: jsonEncode(data),
|
||||||
|
);
|
||||||
|
return jsonDecode(response.body);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
class BasicMutationExample extends StatefulWidget {
|
||||||
|
const BasicMutationExample({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<BasicMutationExample> createState() => _BasicMutationExampleState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BasicMutationExampleState extends State<BasicMutationExample> {
|
||||||
|
late TextEditingController titleController;
|
||||||
|
late TextEditingController bodyController;
|
||||||
|
late int id;
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
id = Random().nextInt(2000000);
|
||||||
|
titleController = TextEditingController();
|
||||||
|
bodyController = TextEditingController();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
titleController.dispose();
|
||||||
|
bodyController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"# Basic Mutation Example",
|
||||||
|
style: Theme.of(context).textTheme.headline5,
|
||||||
|
),
|
||||||
|
MutationBuilder<Map, Map<String, dynamic>>(
|
||||||
|
job: basicMutationJob,
|
||||||
|
onMutate: (v) {
|
||||||
|
QueryBowl.of(context)
|
||||||
|
.setQueryData<String, void>(successJob.queryKey, (oldData) {
|
||||||
|
if (oldData?.contains("After Mutate (OPTIMISTIC UPDATE)") ==
|
||||||
|
true) {
|
||||||
|
return "$oldData";
|
||||||
|
}
|
||||||
|
return "$oldData - After Mutate (OPTIMISTIC UPDATE)";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
builder: (context, mutation) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
TextField(
|
||||||
|
controller: titleController,
|
||||||
|
decoration: const InputDecoration(labelText: "Title"),
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: bodyController,
|
||||||
|
decoration: const InputDecoration(labelText: "Body"),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
final title = titleController.value.text;
|
||||||
|
final body = bodyController.value.text;
|
||||||
|
if (body.isEmpty || title.isEmpty) return;
|
||||||
|
mutation.mutate({
|
||||||
|
"title": title,
|
||||||
|
"body": body,
|
||||||
|
"id": id,
|
||||||
|
}, onData: (data) {
|
||||||
|
// resetting the form
|
||||||
|
titleController.text = "";
|
||||||
|
bodyController.text = "";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: const Text("Post"),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
if (mutation.hasData) Text("Response\n${mutation.data}"),
|
||||||
|
if (mutation.hasError) Text(mutation.error.toString()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
final successJob = QueryJob<String, void>(
|
||||||
|
queryKey: "greetings-example",
|
||||||
|
task: (queryKey, _, __) => Future.delayed(
|
||||||
|
const Duration(seconds: 2),
|
||||||
|
() =>
|
||||||
|
"The work successfully executed. Data: key=($queryKey) value=${Random.secure().nextInt(100)}",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final canFailJob = QueryJob<String, void>(
|
||||||
|
queryKey: "failure-example",
|
||||||
|
task: (queryKey, _, __) => Random().nextBool()
|
||||||
|
? Future.error("$queryKey operation failed for unknown reason")
|
||||||
|
: Future.value(
|
||||||
|
"Successful execution. Result: $queryKey=${Random().nextInt(100)}",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
class BasicQueryExample extends StatelessWidget {
|
||||||
|
const BasicQueryExample({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"# Basic Query Example",
|
||||||
|
style: Theme.of(context).textTheme.headline5,
|
||||||
|
),
|
||||||
|
QueryBuilder<String, void>(
|
||||||
|
job: successJob,
|
||||||
|
externalData: null,
|
||||||
|
builder: (context, query) {
|
||||||
|
if (!query.hasData || query.isLoading || query.isRefetching) {
|
||||||
|
return const CircularProgressIndicator();
|
||||||
|
}
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Text(query.data!),
|
||||||
|
ElevatedButton(
|
||||||
|
child: const Text("Refetch"),
|
||||||
|
onPressed: () async {
|
||||||
|
await query.refetch();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
QueryBuilder<String, void>(
|
||||||
|
job: canFailJob,
|
||||||
|
externalData: null,
|
||||||
|
builder: (context, query) {
|
||||||
|
if (!query.hasData || query.isLoading || query.isRefetching) {
|
||||||
|
return const CircularProgressIndicator();
|
||||||
|
}
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
if (query.hasError)
|
||||||
|
Text(
|
||||||
|
"${query.error}. Retrying: ${query.retryAttempts}",
|
||||||
|
),
|
||||||
|
if (query.hasData)
|
||||||
|
Text(
|
||||||
|
"Success after ${query.retryAttempts}\nData: ${query.data}",
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
child: const Text("Refetch"),
|
||||||
|
onPressed: () async {
|
||||||
|
await query.refetch();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:example/components/hooks/basic_hook_query.dart';
|
||||||
|
import 'package:fl_query/fl_query_hooks.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
final basicMutationHookJob = MutationJob<Map, Map<String, dynamic>>(
|
||||||
|
mutationKey: "basic-hook-mutation-example",
|
||||||
|
task: (key, data) async {
|
||||||
|
final response = await http.post(
|
||||||
|
Uri.parse(
|
||||||
|
// to simulate a failing response environment
|
||||||
|
Random().nextBool()
|
||||||
|
? "https://jsonplaceholder.typicode.com/posts"
|
||||||
|
: "https://google.com",
|
||||||
|
),
|
||||||
|
headers: {'Content-type': 'application/json; charset=UTF-8'},
|
||||||
|
body: jsonEncode(data),
|
||||||
|
);
|
||||||
|
return jsonDecode(response.body);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
class BasicHookMutationExample extends HookWidget {
|
||||||
|
const BasicHookMutationExample({super.key});
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final id = useMemoized(() => Random().nextInt(2000000), []);
|
||||||
|
final titleController = useTextEditingController();
|
||||||
|
final bodyController = useTextEditingController();
|
||||||
|
final mutation = useMutation(
|
||||||
|
job: basicMutationHookJob,
|
||||||
|
onMutate: (v) {
|
||||||
|
QueryBowl.of(context)
|
||||||
|
.setQueryData<String, void>(successHookJob.queryKey, (oldData) {
|
||||||
|
if (oldData?.contains("After Mutate (OPTIMISTIC UPDATE)") == true) {
|
||||||
|
return "$oldData";
|
||||||
|
}
|
||||||
|
return "$oldData - After Mutate (OPTIMISTIC UPDATE)";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"# Basic Mutation Hook Example",
|
||||||
|
style: Theme.of(context).textTheme.headline5,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
TextField(
|
||||||
|
controller: titleController,
|
||||||
|
decoration: const InputDecoration(labelText: "Title"),
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: bodyController,
|
||||||
|
decoration: const InputDecoration(labelText: "Body"),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
final title = titleController.value.text;
|
||||||
|
final body = bodyController.value.text;
|
||||||
|
if (body.isEmpty || title.isEmpty) return;
|
||||||
|
mutation.mutate({
|
||||||
|
"title": title,
|
||||||
|
"body": body,
|
||||||
|
"id": id,
|
||||||
|
}, onData: (data) {
|
||||||
|
// resetting the form
|
||||||
|
titleController.text = "";
|
||||||
|
bodyController.text = "";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: const Text("Post"),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
if (mutation.hasData) Text("Response\n${mutation.data}"),
|
||||||
|
if (mutation.hasError) Text(mutation.error.toString()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:fl_query/fl_query_hooks.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
|
||||||
|
final successHookJob = QueryJob<String, void>(
|
||||||
|
queryKey: "greetings-hook-example",
|
||||||
|
task: (queryKey, _, __) => Future.delayed(
|
||||||
|
const Duration(seconds: 2),
|
||||||
|
() =>
|
||||||
|
"The work successfully executed. Data: key=($queryKey) value=${Random.secure().nextInt(100)}",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final canFailHookJob = QueryJob<String, void>(
|
||||||
|
queryKey: "failure-hook-example",
|
||||||
|
task: (queryKey, _, __) => Random().nextBool()
|
||||||
|
? Future.error("$queryKey operation failed for unknown reason")
|
||||||
|
: Future.value(
|
||||||
|
"Successful execution. Result: $queryKey=${Random().nextInt(100)}",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
class BasicHookQueryExample extends HookWidget {
|
||||||
|
const BasicHookQueryExample({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final successQuery = useQuery(job: successHookJob, externalData: null);
|
||||||
|
final canFailQuery = useQuery(job: canFailHookJob, externalData: null);
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"# Basic Query Hook Example",
|
||||||
|
style: Theme.of(context).textTheme.headline5,
|
||||||
|
),
|
||||||
|
!successQuery.hasData ||
|
||||||
|
successQuery.isLoading ||
|
||||||
|
successQuery.isRefetching
|
||||||
|
? const CircularProgressIndicator()
|
||||||
|
: Row(
|
||||||
|
children: [
|
||||||
|
Text(successQuery.data!),
|
||||||
|
ElevatedButton(
|
||||||
|
child: const Text("Refetch"),
|
||||||
|
onPressed: () async {
|
||||||
|
await successQuery.refetch();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
!canFailQuery.hasData ||
|
||||||
|
canFailQuery.isLoading ||
|
||||||
|
canFailQuery.isRefetching
|
||||||
|
? const CircularProgressIndicator()
|
||||||
|
: Row(
|
||||||
|
children: [
|
||||||
|
if (canFailQuery.hasError)
|
||||||
|
Text(
|
||||||
|
"${canFailQuery.error}. Retrying: ${canFailQuery.retryAttempts}",
|
||||||
|
),
|
||||||
|
if (canFailQuery.hasData)
|
||||||
|
Text(
|
||||||
|
"Success after ${canFailQuery.retryAttempts}\nData: ${canFailQuery.data}",
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
child: const Text("Refetch"),
|
||||||
|
onPressed: () async {
|
||||||
|
await canFailQuery.refetch();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:fl_query/fl_query_hooks.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
|
||||||
|
final lazyHookQueryJob = QueryJob<String, String>(
|
||||||
|
queryKey: "lazy-hook-query",
|
||||||
|
enabled: false,
|
||||||
|
task: (queryKey, data, _) {
|
||||||
|
return Future.delayed(const Duration(milliseconds: 500),
|
||||||
|
() => "Result: key=$queryKey value=$data");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
class LazyHookQueryExample extends HookWidget {
|
||||||
|
const LazyHookQueryExample({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final query = useQuery(
|
||||||
|
job: lazyHookQueryJob,
|
||||||
|
externalData: "Love",
|
||||||
|
);
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"# Lazy Hook Query Example",
|
||||||
|
style: Theme.of(context).textTheme.headline5,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text("Current Data: ${query.data ?? "Loading"}"),
|
||||||
|
ElevatedButton(
|
||||||
|
child: const Text("Refetch Query"),
|
||||||
|
onPressed: () => query.refetch(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:fl_query/fl_query_hooks.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
|
||||||
|
final mutationHookVariableKeyJob = MutationJob.withVariableKey<String, double>(
|
||||||
|
task: (queryKey, variables) {
|
||||||
|
return Future.value("$variables");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
class MutationHookVariableKeyExample extends HookWidget {
|
||||||
|
const MutationHookVariableKeyExample({Key? key}) : super(key: key);
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final id = useState(Random().nextDouble());
|
||||||
|
final mutation = useMutation(
|
||||||
|
job: mutationHookVariableKeyJob("mutation-hook-variable-key#${id.value}"),
|
||||||
|
);
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"# Mutation Hook Variable Key Example",
|
||||||
|
style: Theme.of(context).textTheme.headline5,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text("${mutation.mutationKey} Result: ${mutation.data}"),
|
||||||
|
ElevatedButton(
|
||||||
|
child: const Text("Generate Random Data"),
|
||||||
|
onPressed: () {
|
||||||
|
mutation.mutate(Random().nextDouble());
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
child: const Text("New Mutation"),
|
||||||
|
onPressed: () {
|
||||||
|
id.value = Random().nextDouble();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:fl_query/fl_query_hooks.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
|
||||||
|
final queryHookExternalDataJob = QueryJob<String, double>(
|
||||||
|
queryKey: "query-hook-external-data",
|
||||||
|
cacheTime: const Duration(seconds: 10),
|
||||||
|
task: (queryKey, data, _) {
|
||||||
|
return Future.delayed(const Duration(milliseconds: 500),
|
||||||
|
() => "Hello from $queryKey with $data");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
class QueryHookExternalDataExample extends HookWidget {
|
||||||
|
const QueryHookExternalDataExample({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final externalData = useState(Random().nextDouble() * 200);
|
||||||
|
final query = useQuery(
|
||||||
|
job: queryHookExternalDataJob,
|
||||||
|
externalData: externalData.value,
|
||||||
|
);
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"# Query Hook With External Data Example",
|
||||||
|
style: Theme.of(context).textTheme.headline5,
|
||||||
|
),
|
||||||
|
query.isLoading || query.isRefetching || !query.hasData
|
||||||
|
? const CircularProgressIndicator()
|
||||||
|
: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: query.externalData,
|
||||||
|
height: query.externalData,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
child: Center(child: Text(query.externalData.toString())),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
child: const Text("New Id"),
|
||||||
|
onPressed: () {
|
||||||
|
externalData.value = Random().nextDouble() * 200;
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:fl_query/fl_query_hooks.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
|
||||||
|
final queryHookVariableKeyJob = QueryJob.withVariableKey<String, void>(
|
||||||
|
task: (queryKey, externalData, query) {
|
||||||
|
return Future.delayed(
|
||||||
|
const Duration(milliseconds: 500),
|
||||||
|
() => "QueryKey:${queryKey.split("#").last}",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
class QueryHookVariableKeyExample extends HookWidget {
|
||||||
|
const QueryHookVariableKeyExample({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final id = useState(Random().nextDouble() * 200);
|
||||||
|
final query = useQuery<String, void>(
|
||||||
|
job: queryHookVariableKeyJob("hook-variable-query#${id.value}"),
|
||||||
|
externalData: null,
|
||||||
|
);
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"# Query Hook Variable Example",
|
||||||
|
style: Theme.of(context).textTheme.headline5,
|
||||||
|
),
|
||||||
|
query.isLoading || query.isRefetching || !query.hasData
|
||||||
|
? const CircularProgressIndicator()
|
||||||
|
: Row(
|
||||||
|
children: [
|
||||||
|
Text("Query Result: ${query.data}"),
|
||||||
|
ElevatedButton(
|
||||||
|
child: const Text("New Id"),
|
||||||
|
onPressed: () {
|
||||||
|
id.value = Random().nextDouble() * 200;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
final lazyQueryJob = QueryJob<String, String>(
|
||||||
|
queryKey: "lazy-query",
|
||||||
|
enabled: false,
|
||||||
|
task: (queryKey, data, _) {
|
||||||
|
return Future.delayed(const Duration(milliseconds: 500),
|
||||||
|
() => "Result: key=$queryKey value=$data");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
class LazyQueryExample extends StatelessWidget {
|
||||||
|
const LazyQueryExample({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"# Lazy Query Example",
|
||||||
|
style: Theme.of(context).textTheme.headline5,
|
||||||
|
),
|
||||||
|
QueryBuilder<String, String>(
|
||||||
|
job: lazyQueryJob,
|
||||||
|
externalData: "Love",
|
||||||
|
builder: (context, query) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Text("Current Data: ${query.data ?? "Loading"}"),
|
||||||
|
ElevatedButton(
|
||||||
|
child: const Text("Refetch Query"),
|
||||||
|
onPressed: () => query.refetch(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
final mutationVariableKeyJob = MutationJob.withVariableKey<String, double>(
|
||||||
|
task: (queryKey, variables) {
|
||||||
|
return Future.value("$variables");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
class MutationVariableKeyExample extends StatefulWidget {
|
||||||
|
const MutationVariableKeyExample({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MutationVariableKeyExample> createState() =>
|
||||||
|
_MutationVariableKeyExampleState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MutationVariableKeyExampleState
|
||||||
|
extends State<MutationVariableKeyExample> {
|
||||||
|
late double id;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
id = Random().nextDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"# Mutation Variable Key Example",
|
||||||
|
style: Theme.of(context).textTheme.headline5,
|
||||||
|
),
|
||||||
|
MutationBuilder<String, double>(
|
||||||
|
job: mutationVariableKeyJob("mutation-variable-key#$id"),
|
||||||
|
builder: (context, mutation) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Text("${mutation.mutationKey} Result: ${mutation.data}"),
|
||||||
|
ElevatedButton(
|
||||||
|
child: const Text("Generate Random Data"),
|
||||||
|
onPressed: () {
|
||||||
|
mutation.mutate(Random().nextDouble());
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
child: const Text("New Mutation"),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
id = Random().nextDouble();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
final jobWithExternalData = QueryJob<String, double>(
|
||||||
|
queryKey: "query-external-data",
|
||||||
|
cacheTime: const Duration(seconds: 10),
|
||||||
|
task: (queryKey, data, _) {
|
||||||
|
return Future.delayed(const Duration(milliseconds: 500),
|
||||||
|
() => "Hello from $queryKey with $data");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
class QueryExternalDataExample extends StatefulWidget {
|
||||||
|
const QueryExternalDataExample({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<QueryExternalDataExample> createState() =>
|
||||||
|
_QueryExternalDataExampleState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _QueryExternalDataExampleState extends State<QueryExternalDataExample> {
|
||||||
|
late double externalData;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
externalData = Random().nextDouble() * 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"# Query With External Data Example",
|
||||||
|
style: Theme.of(context).textTheme.headline5,
|
||||||
|
),
|
||||||
|
QueryBuilder<String, double>(
|
||||||
|
job: jobWithExternalData,
|
||||||
|
externalData: externalData,
|
||||||
|
builder: (context, query) {
|
||||||
|
if (query.isLoading || query.isRefetching || !query.hasData) {
|
||||||
|
return const CircularProgressIndicator();
|
||||||
|
}
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: query.externalData,
|
||||||
|
height: query.externalData,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
child: Center(child: Text(query.externalData.toString())),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
child: const Text("New Id"),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
externalData = Random().nextDouble() * 200;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
final queryVariableKeyJob = QueryJob.withVariableKey<String, void>(
|
||||||
|
task: (queryKey, externalData, query) {
|
||||||
|
return Future.delayed(
|
||||||
|
const Duration(milliseconds: 500),
|
||||||
|
() => "QueryKey:${queryKey.split("#").last}",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
class QueryVariableKeyExample extends StatefulWidget {
|
||||||
|
const QueryVariableKeyExample({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<QueryVariableKeyExample> createState() =>
|
||||||
|
_QueryVariableKeyExampleState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _QueryVariableKeyExampleState extends State<QueryVariableKeyExample> {
|
||||||
|
late double id;
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
id = Random().nextDouble() * 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"# Query Variable Example",
|
||||||
|
style: Theme.of(context).textTheme.headline5,
|
||||||
|
),
|
||||||
|
QueryBuilder<String, void>(
|
||||||
|
job: queryVariableKeyJob("variable-query#$id"),
|
||||||
|
externalData: null,
|
||||||
|
builder: (context, query) {
|
||||||
|
if (query.isLoading || query.isRefetching || !query.hasData) {
|
||||||
|
return const CircularProgressIndicator();
|
||||||
|
}
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Text("Query Result: ${query.data}"),
|
||||||
|
ElevatedButton(
|
||||||
|
child: const Text("New Id"),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
id = Random().nextDouble() * 200;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
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!);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import 'package:example/main.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:fl_query/fl_query_hooks.dart';
|
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
||||||
|
|
||||||
class HookExample extends HookWidget {
|
|
||||||
const HookExample({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final query = useQuery(job: successJob, externalData: null);
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: const Text("Running the 1st example but with hooks instead"),
|
|
||||||
),
|
|
||||||
body: !query.hasData || query.isLoading || query.isRefetching
|
|
||||||
? const CircularProgressIndicator()
|
|
||||||
: TextButton(
|
|
||||||
child: Text(query.data!),
|
|
||||||
onPressed: () async {
|
|
||||||
await query.refetch();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import 'package:fl_query/fl_query.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
final lazyQueryJob = QueryJob<String, String>(
|
|
||||||
queryKey: "non_enabled_query",
|
|
||||||
enabled: false,
|
|
||||||
task: (queryKey, data, _) {
|
|
||||||
return Future.delayed(const Duration(milliseconds: 500),
|
|
||||||
() => "Hello from $queryKey with $data");
|
|
||||||
});
|
|
||||||
|
|
||||||
class LazyQuery extends StatelessWidget {
|
|
||||||
const LazyQuery({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(),
|
|
||||||
body: QueryBuilder<String, String>(
|
|
||||||
job: lazyQueryJob,
|
|
||||||
externalData: "Love",
|
|
||||||
builder: (context, query) {
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
Text("Query Data::: ${query.data ?? "Loading"}"),
|
|
||||||
ElevatedButton(
|
|
||||||
child: const Text("Fetch Query"),
|
|
||||||
onPressed: () => query.refetch(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+46
-150
@@ -1,11 +1,17 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:example/another_component.dart';
|
import 'package:example/components/basic_mutation.dart';
|
||||||
import 'package:example/dependent_query_example.dart';
|
import 'package:example/components/basic_query.dart';
|
||||||
import 'package:example/hooks_example.dart';
|
import 'package:example/components/hooks/basic_hook_mutation.dart';
|
||||||
import 'package:example/lazy_query.dart';
|
import 'package:example/components/hooks/basic_hook_query.dart';
|
||||||
import 'package:example/mutation_example.dart';
|
import 'package:example/components/hooks/lazy_hook_query.dart';
|
||||||
import 'package:example/query_with_external_data.dart';
|
import 'package:example/components/hooks/mutation_hook_variable_key.dart';
|
||||||
|
import 'package:example/components/hooks/query_hook_external_data.dart';
|
||||||
|
import 'package:example/components/hooks/query_hook_variable_key.dart';
|
||||||
|
import 'package:example/components/lazy_query.dart';
|
||||||
|
import 'package:example/components/mutation_variable_key.dart';
|
||||||
|
import 'package:example/components/query_external_data.dart';
|
||||||
|
import 'package:example/components/query_variable_key.dart';
|
||||||
import 'package:fl_query/fl_query.dart';
|
import 'package:fl_query/fl_query.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
@@ -33,21 +39,6 @@ class MyApp extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final successJob = QueryJob<String, void>(
|
|
||||||
queryKey: "greetings",
|
|
||||||
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()
|
|
||||||
? Future.error("[$queryKey] Failed for unknown reason")
|
|
||||||
: Future.value(
|
|
||||||
"Success, you'll get slowly ${Random().nextInt(100)}!",
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
class MyHomePage extends StatefulWidget {
|
class MyHomePage extends StatefulWidget {
|
||||||
const MyHomePage({Key? key}) : super(key: key);
|
const MyHomePage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@@ -56,142 +47,47 @@ class MyHomePage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
|
class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
WidgetsBinding.instance.addObserver(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
|
||||||
super.didChangeAppLifecycleState(state);
|
|
||||||
print("LIFE CYCLE STATE: $state");
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Fl Query Example"),
|
title: const Text("Fl Query Example"),
|
||||||
),
|
),
|
||||||
body: Column(
|
body: SingleChildScrollView(
|
||||||
children: [
|
child: Padding(
|
||||||
Row(
|
padding: const EdgeInsets.all(8.0),
|
||||||
children: [
|
child: Column(
|
||||||
QueryBuilder<String, void>(
|
children: [
|
||||||
job: successJob,
|
// Regular Flutter Examples
|
||||||
externalData: null,
|
const BasicQueryExample(),
|
||||||
builder: (context, query) {
|
const QueryExternalDataExample(),
|
||||||
if (!query.hasData || query.isLoading || query.isRefetching) {
|
const LazyQueryExample(),
|
||||||
return const CircularProgressIndicator();
|
const QueryVariableKeyExample(),
|
||||||
}
|
const Divider(),
|
||||||
return TextButton(
|
const BasicMutationExample(),
|
||||||
child: Text(query.data!),
|
const MutationVariableKeyExample(),
|
||||||
onPressed: () async {
|
|
||||||
await query.refetch();
|
const Divider(color: Colors.amber, thickness: 5),
|
||||||
},
|
Align(
|
||||||
);
|
alignment: Alignment.topLeft,
|
||||||
},
|
child: Text(
|
||||||
|
"!Warning! Cool people only...\nFlutter Hooks Example",
|
||||||
|
style: Theme.of(context).textTheme.headline3,
|
||||||
),
|
),
|
||||||
QueryBuilder<String, void>(
|
),
|
||||||
job: successJob,
|
const Divider(color: Colors.amber, thickness: 5),
|
||||||
externalData: null,
|
// elite flutter_hooks examples for only elite flutter
|
||||||
builder: (context, query) {
|
// developers
|
||||||
if (!query.hasData || query.isLoading || query.isRefetching) {
|
const BasicHookQueryExample(),
|
||||||
return const CircularProgressIndicator();
|
const QueryHookExternalDataExample(),
|
||||||
}
|
const LazyHookQueryExample(),
|
||||||
return ElevatedButton(
|
const QueryHookVariableKeyExample(),
|
||||||
child: Text(query.data!),
|
const Divider(),
|
||||||
onPressed: () async {
|
const BasicHookMutationExample(),
|
||||||
await query.refetch();
|
const MutationHookVariableKeyExample(),
|
||||||
},
|
],
|
||||||
);
|
),
|
||||||
},
|
)),
|
||||||
),
|
|
||||||
QueryBuilder<String, void>(
|
|
||||||
job: failedJob,
|
|
||||||
externalData: null,
|
|
||||||
builder: (context, query) {
|
|
||||||
return Row(
|
|
||||||
children: [
|
|
||||||
if (query.hasError)
|
|
||||||
Text(
|
|
||||||
"${query.error}. Retrying: ${query.retryAttempts}",
|
|
||||||
),
|
|
||||||
if (query.hasData)
|
|
||||||
Text(
|
|
||||||
"Success after ${query.retryAttempts}. Data: ${query.data}"),
|
|
||||||
ElevatedButton(
|
|
||||||
child: Text("Refetch ${query.queryKey}"),
|
|
||||||
onPressed: () => query.refetch(),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
ElevatedButton(
|
|
||||||
child: const Text("External Data Example"),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => const QueryWithExternalData(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
ElevatedButton(
|
|
||||||
child: const Text("Non Enabled Query Example"),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => const LazyQuery(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
ElevatedButton(
|
|
||||||
child: const Text("Mutation Example"),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => const MutationExample(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ElevatedButton(
|
|
||||||
child: const Text("flutter_hooks Example"),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => const HookExample(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ElevatedButton(
|
|
||||||
child: const Text("Dependent Query Example"),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => const DependentQueryExample(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const AnotherComponent(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
import 'dart:convert';
|
|
||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:fl_query/fl_query.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
|
|
||||||
final postSomethingJob = MutationJob<Map, Map<String, dynamic>>(
|
|
||||||
mutationKey: "post-something-job",
|
|
||||||
task: (key, data) async {
|
|
||||||
final response = await http.post(
|
|
||||||
Uri.parse(
|
|
||||||
"https://jsonplaceholder.typicode.com/posts",
|
|
||||||
),
|
|
||||||
headers: {'Content-type': 'application/json; charset=UTF-8'},
|
|
||||||
body: jsonEncode(data),
|
|
||||||
);
|
|
||||||
return jsonDecode(response.body);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
class MutationExample extends StatefulWidget {
|
|
||||||
const MutationExample({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<MutationExample> createState() => _MutationExampleState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MutationExampleState extends State<MutationExample> {
|
|
||||||
late TextEditingController titleController;
|
|
||||||
late TextEditingController bodyController;
|
|
||||||
late int id;
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
id = Random().nextInt(2000000);
|
|
||||||
titleController = TextEditingController();
|
|
||||||
bodyController = TextEditingController();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
titleController.dispose();
|
|
||||||
bodyController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(title: const Text("Post Something")),
|
|
||||||
body: MutationBuilder<Map, Map<String, dynamic>>(
|
|
||||||
job: postSomethingJob,
|
|
||||||
builder: (context, mutation) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
TextField(
|
|
||||||
controller: titleController,
|
|
||||||
decoration: const InputDecoration(labelText: "Title"),
|
|
||||||
),
|
|
||||||
TextField(
|
|
||||||
controller: bodyController,
|
|
||||||
decoration: const InputDecoration(labelText: "Body"),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
final title = titleController.value.text;
|
|
||||||
final body = bodyController.value.text;
|
|
||||||
if (body.isEmpty || title.isEmpty) return;
|
|
||||||
mutation.mutate({
|
|
||||||
"title": title,
|
|
||||||
"body": body,
|
|
||||||
"id": id,
|
|
||||||
}, onData: (data) {
|
|
||||||
// resetting the form
|
|
||||||
titleController.text = "";
|
|
||||||
bodyController.text = "";
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: const Text("Post"),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
if (mutation.hasData) Text("Response\n${mutation.data}")
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:fl_query/fl_query.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
final jobWithExternalData = QueryJob<String, String>(
|
|
||||||
queryKey: "external_data",
|
|
||||||
cacheTime: const Duration(seconds: 10),
|
|
||||||
task: (queryKey, data, _) {
|
|
||||||
return Future.delayed(const Duration(milliseconds: 500),
|
|
||||||
() => "Hello from $queryKey with $data");
|
|
||||||
});
|
|
||||||
|
|
||||||
class QueryWithExternalData extends StatelessWidget {
|
|
||||||
const QueryWithExternalData({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(),
|
|
||||||
body: QueryBuilder<String, String>(
|
|
||||||
job: jobWithExternalData,
|
|
||||||
externalData: (Random().nextDouble() * 200).toString(),
|
|
||||||
builder: (context, query) {
|
|
||||||
if (query.isLoading || query.isRefetching || !query.hasData) {
|
|
||||||
return const CircularProgressIndicator();
|
|
||||||
}
|
|
||||||
return Container(
|
|
||||||
width: double.parse(query.externalData),
|
|
||||||
height: double.parse(query.externalData),
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
color: Colors.blue,
|
|
||||||
),
|
|
||||||
child: Text(query.externalData),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:fl_query/src/query_bowl.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
abstract class BaseOperation<Data, StatusType> extends ChangeNotifier {
|
abstract class BaseOperation<Data, StatusType> extends ChangeNotifier {
|
||||||
@@ -28,11 +29,14 @@ abstract class BaseOperation<Data, StatusType> extends ChangeNotifier {
|
|||||||
/// storage/cache
|
/// storage/cache
|
||||||
Set<ValueKey<String>> _mounts = {};
|
Set<ValueKey<String>> _mounts = {};
|
||||||
|
|
||||||
|
final QueryBowl queryBowl;
|
||||||
|
|
||||||
BaseOperation({
|
BaseOperation({
|
||||||
required this.cacheTime,
|
required this.cacheTime,
|
||||||
required this.retries,
|
required this.retries,
|
||||||
required this.retryDelay,
|
required this.retryDelay,
|
||||||
required this.status,
|
required this.status,
|
||||||
|
required this.queryBowl,
|
||||||
this.data,
|
this.data,
|
||||||
}) : updatedAt = DateTime.now();
|
}) : updatedAt = DateTime.now();
|
||||||
|
|
||||||
|
|||||||
@@ -21,96 +21,63 @@ Mutation<T, V> useMutation<T extends Object, V>({
|
|||||||
MutationListener<V>? onMutate,
|
MutationListener<V>? onMutate,
|
||||||
List<Object?>? keys,
|
List<Object?>? keys,
|
||||||
}) {
|
}) {
|
||||||
return use(_UseMutation<T, V>(
|
final context = useContext();
|
||||||
job: job,
|
final QueryBowl queryBowl = QueryBowl.of(context);
|
||||||
onData: onData,
|
final ValueKey<String> uKey = useMemoized(() => ValueKey(uuid.v4()), []);
|
||||||
onError: onError,
|
final mutation =
|
||||||
onMutate: onMutate,
|
useRef(Mutation<T, V>.fromOptions(job, queryBowl: queryBowl));
|
||||||
keys: keys,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
class _UseMutation<T extends Object, V> extends Hook<Mutation<T, V>> {
|
final init = useCallback(() {
|
||||||
final MutationJob<T, V> job;
|
mutation.value = queryBowl.addMutation<T, V>(
|
||||||
|
mutation.value,
|
||||||
|
onData: onData,
|
||||||
|
onError: onError,
|
||||||
|
onMutate: onMutate,
|
||||||
|
key: uKey,
|
||||||
|
);
|
||||||
|
}, [mutation.value, job, onData, onError, onMutate, uKey]);
|
||||||
|
|
||||||
/// Called when the query returns new data, on query
|
final disposeMutation = useCallback(() {
|
||||||
/// refetch or query gets expired
|
mutation.value.unmount(uKey);
|
||||||
final MutationListener<T>? onData;
|
if (onData != null) mutation.value.onDataListeners.remove(onData);
|
||||||
|
if (onError != null) mutation.value.onErrorListeners.remove(onError);
|
||||||
|
if (onMutate != null) mutation.value.onMutateListeners.remove(onMutate);
|
||||||
|
}, [mutation.value, onData, onError, onMutate]);
|
||||||
|
|
||||||
/// Called when the query returns error
|
final oldJob = usePrevious(job);
|
||||||
final MutationListener<dynamic>? onError;
|
final oldOnData = usePrevious(onData);
|
||||||
|
final oldOnError = usePrevious(onError);
|
||||||
|
final oldOnMutate = usePrevious(onMutate);
|
||||||
|
|
||||||
/// called right before the mutation is about to run
|
useEffect(() {
|
||||||
///
|
init();
|
||||||
/// perfect scenario for doing optimistic updates
|
return disposeMutation;
|
||||||
final MutationListener<V>? onMutate;
|
}, []);
|
||||||
const _UseMutation({
|
|
||||||
required this.job,
|
useEffect(() {
|
||||||
this.onData,
|
if (oldJob != null && oldJob.mutationKey != job.mutationKey) {
|
||||||
this.onError,
|
disposeMutation();
|
||||||
this.onMutate,
|
mutation.value = Mutation<T, V>.fromOptions(
|
||||||
super.keys,
|
job,
|
||||||
|
queryBowl: queryBowl,
|
||||||
|
);
|
||||||
|
init();
|
||||||
|
} else {
|
||||||
|
if (oldOnData != onData && oldOnData != null) {
|
||||||
|
mutation.value.onDataListeners.remove(oldOnData);
|
||||||
|
if (onData != null) mutation.value.onDataListeners.add(onData);
|
||||||
|
}
|
||||||
|
if (oldOnError != onError && oldOnError != null) {
|
||||||
|
mutation.value.onErrorListeners.remove(oldOnError);
|
||||||
|
if (onError != null) mutation.value.onErrorListeners.add(onError);
|
||||||
|
}
|
||||||
|
if (oldOnMutate != onMutate && oldOnMutate != null) {
|
||||||
|
mutation.value.onMutateListeners.remove(oldOnMutate);
|
||||||
|
if (onMutate != null) mutation.value.onMutateListeners.add(onMutate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
return queryBowl.getMutation<T, V>(job.mutationKey) ?? mutation.value;
|
||||||
HookState<Mutation<T, V>, Hook<Mutation<T, V>>> createState() =>
|
|
||||||
_UseMutationHookState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _UseMutationHookState<T extends Object, V>
|
|
||||||
extends HookState<Mutation<T, V>, _UseMutation<T, V>> {
|
|
||||||
late QueryBowl queryBowl;
|
|
||||||
late final ValueKey<String> uKey;
|
|
||||||
late Mutation<T, V> mutation;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initHook() {
|
|
||||||
super.initHook();
|
|
||||||
uKey = ValueKey<String>(uuid.v4());
|
|
||||||
mutation = Mutation<T, V>.fromOptions(hook.job);
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
queryBowl = QueryBowl.of(context);
|
|
||||||
mutation = queryBowl.addMutation<T, V>(
|
|
||||||
mutation,
|
|
||||||
onData: hook.onData,
|
|
||||||
onError: hook.onError,
|
|
||||||
onMutate: hook.onMutate,
|
|
||||||
key: uKey,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didUpdateHook(_UseMutation<T, V> oldHook) {
|
|
||||||
if (oldHook.onData != hook.onData && oldHook.onData != null) {
|
|
||||||
mutation.onDataListeners.remove(oldHook.onData);
|
|
||||||
if (hook.onData != null) mutation.onDataListeners.add(hook.onData!);
|
|
||||||
}
|
|
||||||
if (oldHook.onError != hook.onError && oldHook.onError != null) {
|
|
||||||
mutation.onErrorListeners.remove(oldHook.onError);
|
|
||||||
if (hook.onError != null) mutation.onErrorListeners.add(hook.onError!);
|
|
||||||
}
|
|
||||||
if (oldHook.onMutate != hook.onMutate && oldHook.onMutate != null) {
|
|
||||||
mutation.onMutateListeners.remove(oldHook.onMutate);
|
|
||||||
if (hook.onMutate != null) mutation.onMutateListeners.add(hook.onMutate!);
|
|
||||||
}
|
|
||||||
super.didUpdateHook(oldHook);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
mutation.unmount(uKey);
|
|
||||||
if (hook.onData != null) mutation.onDataListeners.remove(hook.onData);
|
|
||||||
if (hook.onError != null) mutation.onErrorListeners.remove(hook.onError);
|
|
||||||
if (hook.onMutate != null) mutation.onMutateListeners.remove(hook.onMutate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Mutation<T, V> build(BuildContext context) {
|
|
||||||
queryBowl = QueryBowl.of(context);
|
|
||||||
return queryBowl.getMutation<T, V>(mutation.mutationKey) ?? mutation;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get debugLabel => 'useQuery';
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,45 +18,59 @@ Query<T, Outside> useQuery<T extends Object, Outside>({
|
|||||||
List<Object?>? keys,
|
List<Object?>? keys,
|
||||||
}) {
|
}) {
|
||||||
final context = useContext();
|
final context = useContext();
|
||||||
QueryBowl queryBowl = QueryBowl.of(context);
|
final QueryBowl queryBowl = QueryBowl.of(context);
|
||||||
final ValueKey<String> uKey = useMemoized(() => ValueKey(uuid.v4()), []);
|
final ValueKey<String> uKey = useMemoized(() => ValueKey(uuid.v4()), []);
|
||||||
Query<T, Outside> query = useMemoized(
|
final query = useRef(
|
||||||
() => Query.fromOptions(
|
Query.fromOptions(
|
||||||
job,
|
job,
|
||||||
externalData: externalData,
|
externalData: externalData,
|
||||||
queryBowl: queryBowl,
|
queryBowl: queryBowl,
|
||||||
onData: onData,
|
),
|
||||||
onError: onError,
|
);
|
||||||
),
|
|
||||||
[]);
|
|
||||||
|
|
||||||
|
final oldJob = usePrevious(job);
|
||||||
final oldExternalData = usePrevious(externalData);
|
final oldExternalData = usePrevious(externalData);
|
||||||
final oldOnData = usePrevious(onData);
|
final oldOnData = usePrevious(onData);
|
||||||
final oldOnError = usePrevious(onError);
|
final oldOnError = usePrevious(onError);
|
||||||
|
|
||||||
useEffect(() {
|
final init = useCallback(() {
|
||||||
queryBowl.addQuery<T, Outside>(
|
query.value = queryBowl.addQuery<T, Outside>(
|
||||||
query,
|
query.value,
|
||||||
key: uKey,
|
key: uKey,
|
||||||
onData: onData,
|
onData: onData,
|
||||||
onError: onError,
|
onError: onError,
|
||||||
);
|
);
|
||||||
final hasExternalDataChanged = query.externalData != null &&
|
final hasExternalDataChanged = query.value.externalData != null &&
|
||||||
query.prevUsedExternalData != null &&
|
query.value.prevUsedExternalData != null &&
|
||||||
!isShallowEqual(query.externalData!, query.prevUsedExternalData!);
|
!isShallowEqual(
|
||||||
(query.fetched && query.refetchOnMount == true) || hasExternalDataChanged
|
query.value.externalData!, query.value.prevUsedExternalData!);
|
||||||
? query.refetch()
|
(query.value.fetched && query.value.refetchOnMount == true) ||
|
||||||
: query.fetch();
|
hasExternalDataChanged
|
||||||
|
? query.value.refetch()
|
||||||
|
: query.value.fetch();
|
||||||
|
}, [queryBowl, query.value, uKey, onData, onError, job]);
|
||||||
|
|
||||||
return () {
|
final disposeQuery = useCallback(() {
|
||||||
query.unmount(uKey);
|
query.value.unmount(uKey);
|
||||||
if (onData != null) query.onDataListeners.remove(onData);
|
if (onData != null) query.value.onDataListeners.remove(onData);
|
||||||
if (onError != null) query.onErrorListeners.remove(onError);
|
if (onError != null) query.value.onErrorListeners.remove(onError);
|
||||||
};
|
}, [query.value, onData, onError, uKey]);
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
init();
|
||||||
|
return disposeQuery;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
if (oldExternalData != null &&
|
if (oldJob != null && oldJob.queryKey != job.queryKey) {
|
||||||
|
disposeQuery();
|
||||||
|
query.value = Query.fromOptions(
|
||||||
|
job,
|
||||||
|
externalData: externalData,
|
||||||
|
queryBowl: queryBowl,
|
||||||
|
);
|
||||||
|
init();
|
||||||
|
} else if (oldExternalData != null &&
|
||||||
externalData != null &&
|
externalData != null &&
|
||||||
!isShallowEqual(oldExternalData, externalData)) {
|
!isShallowEqual(oldExternalData, externalData)) {
|
||||||
QueryBowl.of(context).fetchQuery(
|
QueryBowl.of(context).fetchQuery(
|
||||||
@@ -68,16 +82,16 @@ Query<T, Outside> useQuery<T extends Object, Outside>({
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if (oldOnData != onData && oldOnData != null) {
|
if (oldOnData != onData && oldOnData != null) {
|
||||||
query.onDataListeners.remove(oldOnData);
|
query.value.onDataListeners.remove(oldOnData);
|
||||||
if (onData != null) query.onDataListeners.add(onData);
|
if (onData != null) query.value.onDataListeners.add(onData);
|
||||||
}
|
}
|
||||||
if (oldOnError != onError && oldOnError != null) {
|
if (oldOnError != onError && oldOnError != null) {
|
||||||
query.onErrorListeners.remove(oldOnError);
|
query.value.onErrorListeners.remove(oldOnError);
|
||||||
if (onError != null) query.onErrorListeners.add(onError);
|
if (onError != null) query.value.onErrorListeners.add(onError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
return queryBowl.getQuery<T, Outside>(job.queryKey) ?? query;
|
return queryBowl.getQuery<T, Outside>(job.queryKey) ?? query.value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,11 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:fl_query/src/base_operation.dart';
|
import 'package:fl_query/src/base_operation.dart';
|
||||||
import 'package:fl_query/src/models/mutation_job.dart';
|
import 'package:fl_query/src/models/mutation_job.dart';
|
||||||
|
import 'package:fl_query/src/models/query_job.dart';
|
||||||
|
import 'package:fl_query/src/query.dart';
|
||||||
|
import 'package:fl_query/src/utils.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
enum MutationStatus {
|
enum MutationStatus {
|
||||||
error,
|
error,
|
||||||
@@ -13,7 +17,8 @@ enum MutationStatus {
|
|||||||
|
|
||||||
typedef MutationListener<T> = FutureOr<void> Function(T);
|
typedef MutationListener<T> = FutureOr<void> Function(T);
|
||||||
|
|
||||||
typedef MutationTaskFunction<T, V> = FutureOr<T> Function(String, V);
|
typedef MutationTaskFunction<T, V> = FutureOr<T> Function(
|
||||||
|
String queryKey, V variables);
|
||||||
|
|
||||||
class Mutation<T extends Object, V> extends BaseOperation<T, MutationStatus> {
|
class Mutation<T extends Object, V> extends BaseOperation<T, MutationStatus> {
|
||||||
// all params
|
// all params
|
||||||
@@ -32,6 +37,7 @@ class Mutation<T extends Object, V> extends BaseOperation<T, MutationStatus> {
|
|||||||
required this.task,
|
required this.task,
|
||||||
required super.retries,
|
required super.retries,
|
||||||
required super.retryDelay,
|
required super.retryDelay,
|
||||||
|
required super.queryBowl,
|
||||||
required Duration cacheTime,
|
required Duration cacheTime,
|
||||||
MutationListener<T>? onData,
|
MutationListener<T>? onData,
|
||||||
MutationListener<dynamic>? onError,
|
MutationListener<dynamic>? onError,
|
||||||
@@ -47,6 +53,7 @@ class Mutation<T extends Object, V> extends BaseOperation<T, MutationStatus> {
|
|||||||
MutationListener<T>? onData,
|
MutationListener<T>? onData,
|
||||||
MutationListener<dynamic>? onError,
|
MutationListener<dynamic>? onError,
|
||||||
MutationListener<V>? onMutate,
|
MutationListener<V>? onMutate,
|
||||||
|
required super.queryBowl,
|
||||||
}) : mutationKey = options.mutationKey,
|
}) : mutationKey = options.mutationKey,
|
||||||
task = options.task,
|
task = options.task,
|
||||||
super(
|
super(
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import 'package:fl_query/src/utils.dart';
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
class MutationBuilder<T extends Object, V> extends StatefulWidget {
|
class MutationBuilder<T extends Object, V> extends StatefulWidget {
|
||||||
final Function(BuildContext, Mutation<T, V>) builder;
|
final Function(BuildContext context, Mutation<T, V> mutation) builder;
|
||||||
final MutationJob<T, V> job;
|
final MutationJob<T, V> job;
|
||||||
|
|
||||||
/// Called when the query returns new data, on query
|
/// Called when the query returns new data, on query
|
||||||
@@ -39,60 +39,72 @@ class _MutationBuilderState<T extends Object, V>
|
|||||||
|
|
||||||
late ValueKey<String> uKey;
|
late ValueKey<String> uKey;
|
||||||
|
|
||||||
late Mutation<T, V> mutation;
|
Mutation<T, V>? mutation;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
uKey = ValueKey<String>(uuid.v4());
|
uKey = ValueKey<String>(uuid.v4());
|
||||||
mutation = Mutation<T, V>.fromOptions(widget.job);
|
WidgetsBinding.instance.addPostFrameCallback(init);
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
}
|
||||||
queryBowl = QueryBowl.of(context);
|
|
||||||
mutation = queryBowl.addMutation<T, V>(
|
void init([_]) {
|
||||||
mutation,
|
queryBowl = QueryBowl.of(context);
|
||||||
onData: widget.onData,
|
mutation = queryBowl.addMutation<T, V>(
|
||||||
onError: widget.onError,
|
Mutation<T, V>.fromOptions(widget.job, queryBowl: queryBowl),
|
||||||
onMutate: widget.onMutate,
|
onData: widget.onData,
|
||||||
key: uKey,
|
onError: widget.onError,
|
||||||
);
|
onMutate: widget.onMutate,
|
||||||
});
|
key: uKey,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(covariant MutationBuilder<T, V> oldWidget) {
|
void didUpdateWidget(covariant MutationBuilder<T, V> oldWidget) {
|
||||||
if (oldWidget.onData != widget.onData && oldWidget.onData != null) {
|
if (oldWidget.job.mutationKey != widget.job.mutationKey) {
|
||||||
mutation.onDataListeners.remove(oldWidget.onData);
|
_mutationDispose();
|
||||||
if (widget.onData != null) mutation.onDataListeners.add(widget.onData!);
|
init();
|
||||||
}
|
} else {
|
||||||
if (oldWidget.onError != widget.onError && oldWidget.onError != null) {
|
if (oldWidget.onData != widget.onData && oldWidget.onData != null) {
|
||||||
mutation.onErrorListeners.remove(oldWidget.onError);
|
mutation?.onDataListeners.remove(oldWidget.onData);
|
||||||
if (widget.onError != null)
|
if (widget.onData != null)
|
||||||
mutation.onErrorListeners.add(widget.onError!);
|
mutation?.onDataListeners.add(widget.onData!);
|
||||||
}
|
}
|
||||||
if (oldWidget.onMutate != widget.onMutate && oldWidget.onMutate != null) {
|
if (oldWidget.onError != widget.onError && oldWidget.onError != null) {
|
||||||
mutation.onMutateListeners.remove(oldWidget.onMutate);
|
mutation?.onErrorListeners.remove(oldWidget.onError);
|
||||||
if (widget.onMutate != null)
|
if (widget.onError != null)
|
||||||
mutation.onMutateListeners.add(widget.onMutate!);
|
mutation?.onErrorListeners.add(widget.onError!);
|
||||||
|
}
|
||||||
|
if (oldWidget.onMutate != widget.onMutate && oldWidget.onMutate != null) {
|
||||||
|
mutation?.onMutateListeners.remove(oldWidget.onMutate);
|
||||||
|
if (widget.onMutate != null)
|
||||||
|
mutation?.onMutateListeners.add(widget.onMutate!);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
mutation.unmount(uKey);
|
_mutationDispose();
|
||||||
if (widget.onData != null) mutation.onDataListeners.remove(widget.onData);
|
|
||||||
if (widget.onError != null)
|
|
||||||
mutation.onErrorListeners.remove(widget.onError);
|
|
||||||
if (widget.onMutate != null)
|
|
||||||
mutation.onMutateListeners.remove(widget.onMutate);
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _mutationDispose() {
|
||||||
|
mutation?.unmount(uKey);
|
||||||
|
if (widget.onData != null) mutation?.onDataListeners.remove(widget.onData);
|
||||||
|
if (widget.onError != null)
|
||||||
|
mutation?.onErrorListeners.remove(widget.onError);
|
||||||
|
if (widget.onMutate != null)
|
||||||
|
mutation?.onMutateListeners.remove(widget.onMutate);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
queryBowl = QueryBowl.of(context);
|
queryBowl = QueryBowl.of(context);
|
||||||
final latestMutation =
|
final latestMutation =
|
||||||
queryBowl.getMutation<T, V>(mutation.mutationKey) ?? mutation;
|
queryBowl.getMutation<T, V>(widget.job.mutationKey) ?? mutation;
|
||||||
|
if (latestMutation == null) return Container();
|
||||||
return widget.builder(context, latestMutation);
|
return widget.builder(context, latestMutation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,8 +73,6 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
|
|||||||
|
|
||||||
Timer? _refetchIntervalTimer;
|
Timer? _refetchIntervalTimer;
|
||||||
|
|
||||||
final QueryBowl queryBowl;
|
|
||||||
|
|
||||||
Query({
|
Query({
|
||||||
required this.queryKey,
|
required this.queryKey,
|
||||||
required this.task,
|
required this.task,
|
||||||
@@ -83,7 +81,7 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
|
|||||||
required Outside externalData,
|
required Outside externalData,
|
||||||
required super.retries,
|
required super.retries,
|
||||||
required super.retryDelay,
|
required super.retryDelay,
|
||||||
required this.queryBowl,
|
required super.queryBowl,
|
||||||
this.refetchOnMount,
|
this.refetchOnMount,
|
||||||
this.refetchOnReconnect,
|
this.refetchOnReconnect,
|
||||||
this.refetchInterval,
|
this.refetchInterval,
|
||||||
@@ -108,7 +106,7 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
|
|||||||
|
|
||||||
Query.fromOptions(
|
Query.fromOptions(
|
||||||
QueryJob<T, Outside> options, {
|
QueryJob<T, Outside> options, {
|
||||||
required this.queryBowl,
|
required super.queryBowl,
|
||||||
required Outside externalData,
|
required Outside externalData,
|
||||||
QueryListener<T>? onData,
|
QueryListener<T>? onData,
|
||||||
QueryListener<dynamic>? onError,
|
QueryListener<dynamic>? onError,
|
||||||
@@ -209,12 +207,11 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<T?> fetch() async {
|
Future<T?> fetch() async {
|
||||||
|
if (!enabled) return null;
|
||||||
|
|
||||||
/// if isLoading/isRefetching is true that means its already fetching/
|
/// if isLoading/isRefetching is true that means its already fetching/
|
||||||
/// refetching. So [_execute] again can create a race condition
|
/// refetching. So [_execute] again can create a race condition
|
||||||
if (!enabled || isLoading || isRefetching) return null;
|
if (isLoading || isRefetching || hasData) return data;
|
||||||
if (hasData) {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
status = QueryStatus.loading;
|
status = QueryStatus.loading;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
return _execute().then((_) {
|
return _execute().then((_) {
|
||||||
@@ -226,12 +223,12 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
|
|||||||
Future<T?> refetch() async {
|
Future<T?> refetch() async {
|
||||||
/// if isLoading/isRefetching is true that means its already fetching/
|
/// if isLoading/isRefetching is true that means its already fetching/
|
||||||
/// refetching. So [_execute] again can create a race condition
|
/// refetching. So [_execute] again can create a race condition
|
||||||
if (isRefetching || isLoading) return null;
|
if (isRefetching || isLoading) return data;
|
||||||
|
if (enabled && !fetched) await fetch();
|
||||||
status = QueryStatus.refetching;
|
status = QueryStatus.refetching;
|
||||||
refetchCount++;
|
refetchCount++;
|
||||||
// disabling the lazy query bound when query was actually called
|
// disabling the lazy query bound when query was actually called
|
||||||
if (!enabled) enabled = true;
|
if (!enabled) enabled = true;
|
||||||
if (enabled && !fetched) await fetch();
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
return await _execute().then((_) => data);
|
return await _execute().then((_) => data);
|
||||||
}
|
}
|
||||||
@@ -247,11 +244,7 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
|
|||||||
/// immutability
|
/// immutability
|
||||||
void setQueryData(QueryUpdateFunction<T> updateFn) async {
|
void setQueryData(QueryUpdateFunction<T> updateFn) async {
|
||||||
final newData = await updateFn(data);
|
final newData = await updateFn(data);
|
||||||
if (data == newData) {
|
if (data == newData) return;
|
||||||
// TODO: Better Error handling & Error structure
|
|
||||||
throw Exception(
|
|
||||||
"[fl_query] new instance of data should be returned because of immutability");
|
|
||||||
}
|
|
||||||
data = newData;
|
data = newData;
|
||||||
status = QueryStatus.success;
|
status = QueryStatus.success;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
@@ -269,11 +262,6 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
|
|||||||
fetched = false;
|
fetched = false;
|
||||||
status = QueryStatus.idle;
|
status = QueryStatus.idle;
|
||||||
retryAttempts = 0;
|
retryAttempts = 0;
|
||||||
for (final queryEntry in _dependencyQueries.entries) {
|
|
||||||
queryEntry.value.unmount(queryEntry.key);
|
|
||||||
queryEntry.value.removeListener(refetch);
|
|
||||||
}
|
|
||||||
_dependencyQueries = {};
|
|
||||||
onDataListeners.clear();
|
onDataListeners.clear();
|
||||||
onErrorListeners.clear();
|
onErrorListeners.clear();
|
||||||
mounts.clear();
|
mounts.clear();
|
||||||
@@ -306,40 +294,8 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<ValueKey<String>, Query> _dependencyQueries = {};
|
Future<T?> _internalRefetch<X>(X dataOrError) {
|
||||||
|
return refetch();
|
||||||
/// only usable inside [QueryJob.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 {
|
bool get isStale {
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ class _QueryBowlScopeState extends State<QueryBowlScope> {
|
|||||||
.listen((ConnectivityResult result) async {
|
.listen((ConnectivityResult result) async {
|
||||||
if (isConnectedToInternet(result)) {
|
if (isConnectedToInternet(result)) {
|
||||||
for (final query in queries) {
|
for (final query in queries) {
|
||||||
if (query.refetchOnReconnect == false) continue;
|
if (query.refetchOnReconnect == false || !query.enabled) continue;
|
||||||
await query.refetch();
|
await query.refetch();
|
||||||
await Future.delayed(widget.refetchOnReconnectDelay);
|
await Future.delayed(widget.refetchOnReconnectDelay);
|
||||||
}
|
}
|
||||||
@@ -108,75 +108,73 @@ class _QueryBowlScopeState extends State<QueryBowlScope> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void updateQueries(Query query) {
|
void updateQueries(Query query) {
|
||||||
// checking & not including inactive queries
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
// basically garbage collecting queries
|
// checking & not including inactive queries
|
||||||
if (query.isInactive) {
|
// basically garbage collecting queries
|
||||||
/// there's a bug currently,
|
setState(() {
|
||||||
/// where if somehow a [Query] (queryA) is depending on another
|
queries = Set.from(
|
||||||
/// [Query] (queryB) & queryA has become inactive so its getting
|
query.isInactive
|
||||||
/// disposed but at the same moment queryB got refetched will make
|
? queries.where((el) => el.queryKey != query.queryKey)
|
||||||
/// queryB's [BaseOperation.unmount] to throw [RangeError] for
|
: queries,
|
||||||
/// 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(() {
|
|
||||||
queries = Set.from(
|
|
||||||
query.isInactive
|
|
||||||
? queries.where((el) => el.queryKey != query.queryKey)
|
|
||||||
: queries,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateMutations(Mutation mutation) {
|
void updateMutations(Mutation mutation) {
|
||||||
setState(() {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
// checking & not including inactive mutations
|
setState(() {
|
||||||
// basically garbage collecting mutations
|
// checking & not including inactive mutations
|
||||||
mutations = Set.from(
|
// basically garbage collecting mutations
|
||||||
mutation.isInactive
|
mutations = Set.from(
|
||||||
? mutations.where(
|
mutation.isInactive
|
||||||
(el) => el.mutationKey != mutation.mutationKey,
|
? mutations.where(
|
||||||
)
|
(el) => el.mutationKey != mutation.mutationKey,
|
||||||
: mutations,
|
)
|
||||||
);
|
: mutations,
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void addQuery<T extends Object, Outside>(Query<T, Outside> query) {
|
void addQuery<T extends Object, Outside>(Query<T, Outside> query) {
|
||||||
setState(() {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
queries = Set.from({...queries, query});
|
setState(() {
|
||||||
|
queries = Set.from({...queries, query});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void addMutation<T extends Object, V>(Mutation<T, V> mutation) {
|
void addMutation<T extends Object, V>(Mutation<T, V> mutation) {
|
||||||
setState(() {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
mutations = Set.from({...mutations, mutation});
|
setState(() {
|
||||||
|
mutations = Set.from({...mutations, mutation});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int removeQueries(List<String> queryKeys) {
|
int removeQueries(List<String> queryKeys) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
setState(() {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
mutations = Set.from(
|
setState(() {
|
||||||
queries.whereNot((query) {
|
mutations = Set.from(
|
||||||
final isAboutToRip = queryKeys.contains(query.queryKey);
|
queries.whereNot((query) {
|
||||||
if (isAboutToRip) count++;
|
final isAboutToRip = queryKeys.contains(query.queryKey);
|
||||||
return isAboutToRip;
|
if (isAboutToRip) count++;
|
||||||
}),
|
return isAboutToRip;
|
||||||
);
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear() {
|
void clear() {
|
||||||
setState(() {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
queries = Set<Query>();
|
setState(() {
|
||||||
mutations = Set<Mutation>();
|
queries = Set<Query>();
|
||||||
|
mutations = Set<Mutation>();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,9 +268,7 @@ class QueryBowl extends InheritedWidget {
|
|||||||
if (onError != null) prevQuery.onErrorListeners.add(onError);
|
if (onError != null) prevQuery.onErrorListeners.add(onError);
|
||||||
if (!prevQuery.hasData || hasExternalDataChanged) {
|
if (!prevQuery.hasData || hasExternalDataChanged) {
|
||||||
if (hasExternalDataChanged) prevQuery.setExternalData(externalData);
|
if (hasExternalDataChanged) prevQuery.setExternalData(externalData);
|
||||||
return prevQuery.fetched
|
return await prevQuery.refetch();
|
||||||
? await prevQuery.refetch()
|
|
||||||
: await prevQuery.fetch();
|
|
||||||
}
|
}
|
||||||
// mounting the widget that is using the query in the prevQuery
|
// mounting the widget that is using the query in the prevQuery
|
||||||
return prevQuery.data;
|
return prevQuery.data;
|
||||||
@@ -353,6 +349,9 @@ class QueryBowl extends InheritedWidget {
|
|||||||
prevMutation.mount(key);
|
prevMutation.mount(key);
|
||||||
return prevMutation;
|
return prevMutation;
|
||||||
} else {
|
} else {
|
||||||
|
if (onData != null) mutation.onDataListeners.add(onData);
|
||||||
|
if (onError != null) mutation.onErrorListeners.add(onError);
|
||||||
|
if (onMutate != null) mutation.onMutateListeners.add(onMutate);
|
||||||
mutation.updateDefaultOptions(cacheTime: cacheTime);
|
mutation.updateDefaultOptions(cacheTime: cacheTime);
|
||||||
mutation.mount(key);
|
mutation.mount(key);
|
||||||
_addMutation(mutation);
|
_addMutation(mutation);
|
||||||
@@ -413,8 +412,9 @@ class QueryBowl extends InheritedWidget {
|
|||||||
|
|
||||||
Future<void> refetchQueries(List<String> queryKeys) async {
|
Future<void> refetchQueries(List<String> queryKeys) async {
|
||||||
for (final query in _queries) {
|
for (final query in _queries) {
|
||||||
if (!queryKeys.contains(query.queryKey)) continue;
|
if (queryKeys.contains(query.queryKey)) {
|
||||||
await query.refetch();
|
await query.refetch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import 'package:fl_query/src/utils.dart';
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
class QueryBuilder<T extends Object, Outside> extends StatefulWidget {
|
class QueryBuilder<T extends Object, Outside> extends StatefulWidget {
|
||||||
final Function(BuildContext, Query<T, Outside>) builder;
|
final Function(BuildContext context, Query<T, Outside> query) builder;
|
||||||
final QueryJob<T, Outside> job;
|
final QueryJob<T, Outside> job;
|
||||||
final Outside externalData;
|
final Outside externalData;
|
||||||
|
|
||||||
@@ -40,30 +40,36 @@ class _QueryBuilderState<T extends Object, Outside>
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
uKey = ValueKey<String>(uuid.v4());
|
uKey = ValueKey<String>(uuid.v4());
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
WidgetsBinding.instance.addPostFrameCallback((_) => init());
|
||||||
query = QueryBowl.of(context).addQuery<T, Outside>(
|
}
|
||||||
Query<T, Outside>.fromOptions(
|
|
||||||
widget.job,
|
void init([QueryBowl? bowl]) async {
|
||||||
externalData: widget.externalData,
|
bowl ??= QueryBowl.of(context);
|
||||||
queryBowl: QueryBowl.of(context),
|
query = bowl.addQuery<T, Outside>(
|
||||||
),
|
Query<T, Outside>.fromOptions(
|
||||||
key: uKey,
|
widget.job,
|
||||||
onData: widget.onData,
|
externalData: widget.externalData,
|
||||||
onError: widget.onError,
|
queryBowl: QueryBowl.of(context),
|
||||||
);
|
),
|
||||||
final hasExternalDataChanged = query!.externalData != null &&
|
key: uKey,
|
||||||
query!.prevUsedExternalData != null &&
|
onData: widget.onData,
|
||||||
!isShallowEqual(query!.externalData!, query!.prevUsedExternalData!);
|
onError: widget.onError,
|
||||||
(query!.fetched && query!.refetchOnMount == true) ||
|
);
|
||||||
hasExternalDataChanged
|
final hasExternalDataChanged = query!.externalData != null &&
|
||||||
? await query!.refetch()
|
query!.prevUsedExternalData != null &&
|
||||||
: await query!.fetch();
|
!isShallowEqual(query!.externalData!, query!.prevUsedExternalData!);
|
||||||
});
|
(query!.fetched && query!.refetchOnMount == true) || hasExternalDataChanged
|
||||||
|
? await query!.refetch()
|
||||||
|
: await query!.fetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(covariant oldWidget) {
|
void didUpdateWidget(covariant oldWidget) {
|
||||||
if (oldWidget.externalData != null &&
|
// re-init the query-builder when new queryJob is appended
|
||||||
|
if (oldWidget.job.queryKey != widget.job.queryKey) {
|
||||||
|
_queryDispose();
|
||||||
|
init();
|
||||||
|
} else if (oldWidget.externalData != null &&
|
||||||
widget.externalData != null &&
|
widget.externalData != null &&
|
||||||
!isShallowEqual(oldWidget.externalData!, widget.externalData!)) {
|
!isShallowEqual(oldWidget.externalData!, widget.externalData!)) {
|
||||||
QueryBowl.of(context).fetchQuery(
|
QueryBowl.of(context).fetchQuery(
|
||||||
@@ -87,11 +93,15 @@ class _QueryBuilderState<T extends Object, Outside>
|
|||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
_queryDispose() {
|
||||||
void dispose() {
|
|
||||||
query?.unmount(uKey);
|
query?.unmount(uKey);
|
||||||
if (widget.onData != null) query?.onDataListeners.remove(widget.onData);
|
if (widget.onData != null) query?.onDataListeners.remove(widget.onData);
|
||||||
if (widget.onError != null) query?.onErrorListeners.remove(widget.onError);
|
if (widget.onError != null) query?.onErrorListeners.remove(widget.onError);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_queryDispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user