feat: use mutation hook add
This commit is contained in:
@@ -8,7 +8,7 @@ class HomePage extends StatelessWidget {
|
|||||||
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 Hooks Example'),
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: ListView(
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -1,65 +1,46 @@
|
|||||||
import 'package:fl_query/fl_query.dart';
|
import 'package:fl_query_hooks/fl_query_hooks.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
|
||||||
class MutationPage extends StatefulWidget {
|
class MutationPage extends HookWidget {
|
||||||
const MutationPage({super.key});
|
const MutationPage({super.key});
|
||||||
|
|
||||||
@override
|
|
||||||
State<MutationPage> createState() => _MutationPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MutationPageState extends State<MutationPage> {
|
|
||||||
late TextEditingController _nameController;
|
|
||||||
late TextEditingController _emailController;
|
|
||||||
late TextEditingController _passwordController;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_nameController = TextEditingController();
|
|
||||||
_emailController = TextEditingController();
|
|
||||||
_passwordController = TextEditingController();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_nameController.dispose();
|
|
||||||
_emailController.dispose();
|
|
||||||
_passwordController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final nameController = useTextEditingController();
|
||||||
|
final emailController = useTextEditingController();
|
||||||
|
final passwordController = useTextEditingController();
|
||||||
|
|
||||||
|
final mutation = useMutation<Map<String, dynamic>, dynamic,
|
||||||
|
Map<String, dynamic>, dynamic>(
|
||||||
|
'sign-up',
|
||||||
|
(variables) {
|
||||||
|
return Future.delayed(
|
||||||
|
const Duration(seconds: 1),
|
||||||
|
() => {
|
||||||
|
'name': variables['name'],
|
||||||
|
'email': variables['email'],
|
||||||
|
'password': variables['password'],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onMutate: (variables) {
|
||||||
|
print('onMutate: $variables');
|
||||||
|
return "Recover ME";
|
||||||
|
},
|
||||||
|
onData: (data, recoveryData) {
|
||||||
|
print('onData: $data');
|
||||||
|
print('recoveryData: $recoveryData');
|
||||||
|
},
|
||||||
|
refreshQueries: const ['hello'],
|
||||||
|
);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('Mutation'),
|
title: const Text('Mutation'),
|
||||||
),
|
),
|
||||||
body: MutationBuilder<Map<String, dynamic>, dynamic, Map<String, dynamic>,
|
body: mutation.hasData
|
||||||
dynamic>(
|
? ListView(
|
||||||
'sign-up',
|
|
||||||
(variables) {
|
|
||||||
return Future.delayed(
|
|
||||||
const Duration(seconds: 1),
|
|
||||||
() => {
|
|
||||||
'name': variables['name'],
|
|
||||||
'email': variables['email'],
|
|
||||||
'password': variables['password'],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onMutate: (variables) {
|
|
||||||
print('onMutate: $variables');
|
|
||||||
return "Recover ME";
|
|
||||||
},
|
|
||||||
onData: (data, recoveryData) {
|
|
||||||
print('onData: $data');
|
|
||||||
print('recoveryData: $recoveryData');
|
|
||||||
},
|
|
||||||
refreshQueries: const ['hello'],
|
|
||||||
builder: (context, mutation) {
|
|
||||||
if (mutation.hasData) {
|
|
||||||
return ListView(
|
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
children: [
|
children: [
|
||||||
Center(
|
Center(
|
||||||
@@ -77,52 +58,48 @@ class _MutationPageState extends State<MutationPage> {
|
|||||||
child: const Text('Log out'),
|
child: const Text('Log out'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
)
|
||||||
}
|
: ListView(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
return ListView(
|
children: [
|
||||||
padding: const EdgeInsets.all(16),
|
TextField(
|
||||||
children: [
|
controller: nameController,
|
||||||
TextField(
|
keyboardType: TextInputType.name,
|
||||||
controller: _nameController,
|
decoration: const InputDecoration(
|
||||||
keyboardType: TextInputType.name,
|
labelText: 'Name',
|
||||||
decoration: const InputDecoration(
|
),
|
||||||
labelText: 'Name',
|
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 16),
|
||||||
const SizedBox(height: 16),
|
TextField(
|
||||||
TextField(
|
controller: emailController,
|
||||||
controller: _emailController,
|
keyboardType: TextInputType.emailAddress,
|
||||||
keyboardType: TextInputType.emailAddress,
|
decoration: const InputDecoration(
|
||||||
decoration: const InputDecoration(
|
labelText: 'Email',
|
||||||
labelText: 'Email',
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 16),
|
||||||
const SizedBox(height: 16),
|
TextField(
|
||||||
TextField(
|
controller: passwordController,
|
||||||
controller: _passwordController,
|
obscureText: true,
|
||||||
obscureText: true,
|
decoration: const InputDecoration(
|
||||||
decoration: const InputDecoration(
|
labelText: 'Password',
|
||||||
labelText: 'Password',
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 20),
|
||||||
const SizedBox(height: 20),
|
ElevatedButton(
|
||||||
ElevatedButton(
|
onPressed: () async {
|
||||||
onPressed: () async {
|
await mutation.mutate({
|
||||||
await mutation.mutate({
|
'name': nameController.text,
|
||||||
'name': _nameController.text,
|
'email': emailController.text,
|
||||||
'email': _emailController.text,
|
'password': passwordController.text,
|
||||||
'password': _passwordController.text,
|
});
|
||||||
});
|
},
|
||||||
},
|
child: mutation.isMutating
|
||||||
child: mutation.isMutating
|
? const CircularProgressIndicator()
|
||||||
? const CircularProgressIndicator()
|
: const Text('Sign Up'),
|
||||||
: const Text('Sign Up'),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ library fl_query_hooks;
|
|||||||
export 'src/use_query_client.dart';
|
export 'src/use_query_client.dart';
|
||||||
export 'src/use_query.dart';
|
export 'src/use_query.dart';
|
||||||
export 'src/use_infinite_query.dart';
|
export 'src/use_infinite_query.dart';
|
||||||
|
export 'src/use_mutation.dart';
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:fl_query/src/collections/default_configs.dart';
|
import 'package:fl_query/fl_query.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';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/src/foundation/diagnostics.dart';
|
import 'package:flutter/src/foundation/diagnostics.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
@@ -22,6 +17,7 @@ InfiniteQuery<DataType, ErrorType, PageType>
|
|||||||
ValueChanged<PageEvent<DataType, PageType>>? onData,
|
ValueChanged<PageEvent<DataType, PageType>>? onData,
|
||||||
ValueChanged<PageEvent<ErrorType, PageType>>? onError,
|
ValueChanged<PageEvent<ErrorType, PageType>>? onError,
|
||||||
bool enabled = true,
|
bool enabled = true,
|
||||||
|
List<Object?>? keys,
|
||||||
}) {
|
}) {
|
||||||
return use(UseInfiniteQuery(
|
return use(UseInfiniteQuery(
|
||||||
queryKey,
|
queryKey,
|
||||||
@@ -34,6 +30,7 @@ InfiniteQuery<DataType, ErrorType, PageType>
|
|||||||
onData: onData,
|
onData: onData,
|
||||||
onError: onError,
|
onError: onError,
|
||||||
enabled: enabled,
|
enabled: enabled,
|
||||||
|
keys: keys,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,243 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:fl_query/fl_query.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/src/foundation/diagnostics.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
|
||||||
|
Mutation<DataType, ErrorType, VariablesType>
|
||||||
|
useMutation<DataType, ErrorType, VariablesType, RecoveryType>(
|
||||||
|
String mutationKey,
|
||||||
|
MutationFn<DataType, VariablesType> mutationFn, {
|
||||||
|
RetryConfig retryConfig = DefaultConstants.retryConfig,
|
||||||
|
MutationOnDataFn<DataType, RecoveryType>? onData,
|
||||||
|
MutationOnErrorFn<ErrorType, RecoveryType>? onError,
|
||||||
|
MutationOnMutationFn<VariablesType, RecoveryType>? onMutate,
|
||||||
|
List<String>? refreshQueries,
|
||||||
|
List<String>? refreshInfiniteQueries,
|
||||||
|
List<Object?>? keys,
|
||||||
|
}) {
|
||||||
|
return use(
|
||||||
|
UseMutation(
|
||||||
|
mutationKey,
|
||||||
|
mutationFn,
|
||||||
|
retryConfig: retryConfig,
|
||||||
|
onData: onData,
|
||||||
|
onError: onError,
|
||||||
|
onMutate: onMutate,
|
||||||
|
refreshQueries: refreshQueries,
|
||||||
|
refreshInfiniteQueries: refreshInfiniteQueries,
|
||||||
|
keys: keys,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class UseMutation<DataType, ErrorType, VariablesType, RecoveryType>
|
||||||
|
extends Hook<Mutation<DataType, ErrorType, VariablesType>> {
|
||||||
|
final MutationFn<DataType, VariablesType> mutationFn;
|
||||||
|
final String mutationKey;
|
||||||
|
|
||||||
|
final RetryConfig retryConfig;
|
||||||
|
|
||||||
|
final MutationOnDataFn<DataType, RecoveryType>? onData;
|
||||||
|
final MutationOnErrorFn<ErrorType, RecoveryType>? onError;
|
||||||
|
final MutationOnMutationFn<VariablesType, RecoveryType>? onMutate;
|
||||||
|
|
||||||
|
// hook specific
|
||||||
|
final List<String>? refreshQueries;
|
||||||
|
final List<String>? refreshInfiniteQueries;
|
||||||
|
|
||||||
|
const UseMutation(
|
||||||
|
this.mutationKey,
|
||||||
|
this.mutationFn, {
|
||||||
|
this.retryConfig = DefaultConstants.retryConfig,
|
||||||
|
this.onData,
|
||||||
|
this.onError,
|
||||||
|
this.onMutate,
|
||||||
|
this.refreshQueries,
|
||||||
|
this.refreshInfiniteQueries,
|
||||||
|
super.keys,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
createState() =>
|
||||||
|
_UseMutationState<DataType, ErrorType, VariablesType, RecoveryType>();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _UseMutationState<DataType, ErrorType, VariablesType, RecoveryType>
|
||||||
|
extends HookState<Mutation<DataType, ErrorType, VariablesType>,
|
||||||
|
UseMutation<DataType, ErrorType, VariablesType, RecoveryType>> {
|
||||||
|
Mutation<DataType, ErrorType, VariablesType>? mutation;
|
||||||
|
|
||||||
|
VoidCallback? removeListener;
|
||||||
|
|
||||||
|
StreamSubscription<VariablesType>? mutationSubscription;
|
||||||
|
StreamSubscription<DataType>? dataSubscription;
|
||||||
|
StreamSubscription<ErrorType>? errorSubscription;
|
||||||
|
|
||||||
|
RecoveryType? recoveryData;
|
||||||
|
|
||||||
|
void rebuild([_]) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
void subscribeOnMutate() {
|
||||||
|
if (hook.onMutate != null)
|
||||||
|
mutationSubscription = mutation!.mutationStream.listen(
|
||||||
|
(event) async {
|
||||||
|
recoveryData = await hook.onMutate?.call(event);
|
||||||
|
|
||||||
|
if (hook.onData != null) {
|
||||||
|
dataSubscription?.cancel();
|
||||||
|
subscribeOnData();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hook.onError != null) {
|
||||||
|
errorSubscription?.cancel();
|
||||||
|
subscribeOnError();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subscribeOnData() {
|
||||||
|
if (hook.onData != null ||
|
||||||
|
hook.refreshInfiniteQueries != null ||
|
||||||
|
hook.refreshQueries != null)
|
||||||
|
dataSubscription = mutation!.dataStream.listen(
|
||||||
|
(event) {
|
||||||
|
final data = hook.onData?.call(event, recoveryData);
|
||||||
|
if (hook.refreshQueries != null) {
|
||||||
|
QueryClient.of(context).refreshQueries(hook.refreshQueries!);
|
||||||
|
}
|
||||||
|
if (hook.refreshInfiniteQueries != null) {
|
||||||
|
QueryClient.of(context)
|
||||||
|
.refreshInfiniteQueries(hook.refreshInfiniteQueries!);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subscribeOnError() {
|
||||||
|
if (hook.onError != null)
|
||||||
|
errorSubscription = mutation!.errorStream.listen(
|
||||||
|
(event) {
|
||||||
|
return hook.onError?.call(event, recoveryData);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> initialize() async {
|
||||||
|
setState(() {
|
||||||
|
_createMutation();
|
||||||
|
subscribeOnMutate();
|
||||||
|
subscribeOnData();
|
||||||
|
subscribeOnError();
|
||||||
|
removeListener = mutation!.addListener(rebuild);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _createMutation() {
|
||||||
|
mutation = QueryClient.of(context).createMutation(
|
||||||
|
hook.mutationKey,
|
||||||
|
hook.mutationFn,
|
||||||
|
retryConfig: hook.retryConfig,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initHook() {
|
||||||
|
super.initHook();
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
|
await initialize();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
mutationSubscription?.cancel();
|
||||||
|
dataSubscription?.cancel();
|
||||||
|
errorSubscription?.cancel();
|
||||||
|
removeListener?.call();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateHook(oldHook) {
|
||||||
|
super.didUpdateHook(oldHook);
|
||||||
|
|
||||||
|
if (oldHook.mutationKey != hook.mutationKey) {
|
||||||
|
mutationSubscription?.cancel();
|
||||||
|
dataSubscription?.cancel();
|
||||||
|
errorSubscription?.cancel();
|
||||||
|
removeListener?.call();
|
||||||
|
initialize();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (oldHook.mutationFn != hook.mutationFn) {
|
||||||
|
mutation!.updateMutationFn(hook.mutationFn);
|
||||||
|
}
|
||||||
|
if (oldHook.onMutate != hook.onMutate) {
|
||||||
|
mutationSubscription?.cancel();
|
||||||
|
subscribeOnMutate();
|
||||||
|
}
|
||||||
|
if (oldHook.onData != hook.onData ||
|
||||||
|
oldHook.refreshQueries != hook.refreshQueries ||
|
||||||
|
oldHook.refreshInfiniteQueries != hook.refreshInfiniteQueries) {
|
||||||
|
dataSubscription?.cancel();
|
||||||
|
subscribeOnData();
|
||||||
|
mutationSubscription?.cancel();
|
||||||
|
subscribeOnMutate();
|
||||||
|
}
|
||||||
|
if (oldHook.onError != hook.onError) {
|
||||||
|
errorSubscription?.cancel();
|
||||||
|
subscribeOnError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
build(BuildContext context) {
|
||||||
|
if (mutation == null) {
|
||||||
|
_createMutation();
|
||||||
|
}
|
||||||
|
return mutation!;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
|
super.debugFillProperties(properties);
|
||||||
|
properties.add(
|
||||||
|
DiagnosticsProperty<Mutation<DataType, ErrorType, VariablesType>>(
|
||||||
|
'mutation', mutation),
|
||||||
|
);
|
||||||
|
properties.add(
|
||||||
|
DiagnosticsProperty<String>('mutationKey', hook.mutationKey),
|
||||||
|
);
|
||||||
|
properties.add(
|
||||||
|
DiagnosticsProperty<MutationFn<DataType, VariablesType>>(
|
||||||
|
'mutationFn', hook.mutationFn),
|
||||||
|
);
|
||||||
|
properties.add(
|
||||||
|
DiagnosticsProperty<RetryConfig>('retryConfig', hook.retryConfig),
|
||||||
|
);
|
||||||
|
properties.add(
|
||||||
|
DiagnosticsProperty<MutationOnDataFn<DataType, RecoveryType>>(
|
||||||
|
'onData',
|
||||||
|
hook.onData,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
properties.add(
|
||||||
|
DiagnosticsProperty<MutationOnErrorFn<ErrorType, RecoveryType>>(
|
||||||
|
'onError',
|
||||||
|
hook.onError,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
properties.add(
|
||||||
|
DiagnosticsProperty<MutationOnMutationFn<VariablesType, RecoveryType>>(
|
||||||
|
'onMutation',
|
||||||
|
hook.onMutate,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user