feat: Use hooks
Re-implement widgets using flutter_hooks and expose said hooks.
MODIFY
packages/graphql_flutter/example/ios/Flutter/Debug.xcconfig
packages/graphql_flutter/example/ios/Flutter/Debug.xcconfig
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"

MODIFY
packages/graphql_flutter/example/ios/Flutter/Release.xcconfig
packages/graphql_flutter/example/ios/Flutter/Release.xcconfig
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"

ADD
packages/graphql_flutter/example/ios/Podfile
+# Uncomment this line to define a global platform for your project
+# platform :ios, '9.0'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
+ end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
+ end
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_ios_podfile_setup
+
+target 'Runner' do
+ use_frameworks!
+ use_modular_headers!
+
+ flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ flutter_additional_ios_build_settings(target)
+ end
+end

MODIFY
packages/graphql_flutter/lib/graphql_flutter.dart
packages/graphql_flutter/lib/graphql_flutter.dart
@@ -10,4 +10,9 @@ export 'package:graphql_flutter/src/widgets/query.dart';
export 'package:graphql_flutter/src/widgets/subscription.dart';
export 'package:graphql_flutter/src/widgets/result_accumulator.dart';
+export 'package:graphql_flutter/src/widgets/hooks/mutation.dart';
+export 'package:graphql_flutter/src/widgets/hooks/query.dart';
+export 'package:graphql_flutter/src/widgets/hooks/subscription.dart';
+export 'package:graphql_flutter/src/widgets/hooks/watch_query.dart';
+
export 'package:graphql_flutter/src/hive_init.dart';

MODIFY
packages/graphql_flutter/lib/src/widgets/graphql_provider.dart
packages/graphql_flutter/lib/src/widgets/graphql_provider.dart
import 'package:flutter/widgets.dart';
import 'package:graphql/client.dart';
+export 'package:graphql_flutter/src/widgets/hooks/query.dart';
class GraphQLProvider extends StatefulWidget {
const GraphQLProvider({

ADD
packages/graphql_flutter/lib/src/widgets/hooks/graphql_client.dart
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:graphql_flutter/graphql_flutter.dart';
+
+GraphQLClient useGraphQLClient() {
+ final context = useContext();
+ return useValueListenable(GraphQLProvider.of(context));
+}

ADD
packages/graphql_flutter/lib/src/widgets/hooks/mutation.dart
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:graphql_flutter/src/widgets/hooks/graphql_client.dart';
+import 'package:graphql_flutter/src/widgets/query.dart';
+
+typedef RunMutation<TParsed> = MultiSourceResult<TParsed> Function(
+ Map<String, dynamic> variables, {
+ Object? optimisticResult,
+});
+
+class MutationHookResult<TParsed> {
+ final RunMutation<TParsed> runMutation;
+ final QueryResult<TParsed> result;
+
+ MutationHookResult({
+ required this.runMutation,
+ required this.result,
+ });
+}
+
+MutationHookResult<TParsed> useMutation<TParsed>(
+ MutationOptions<TParsed> options,
+) {
+ final watchOptions = useMemoized(
+ () => options.asWatchQueryOptions(),
+ [options],
+ );
+ final client = useGraphQLClient();
+ final query = useWatchMutation<TParsed>(watchOptions);
+ final snapshot = useStream(
+ query.stream,
+ initialData: query.latestResult ?? QueryResult.unexecuted,
+ );
+ final runMutation = useCallback((
+ Map<String, dynamic> variables, {
+ Object? optimisticResult,
+ }) {
+ final mutationCallbacks = MutationCallbackHandler(
+ cache: client.cache,
+ queryId: query.queryId,
+ options: options,
+ );
+ return (query
+ ..variables = variables
+ ..optimisticResult = optimisticResult
+ ..onData(mutationCallbacks.callbacks) // add callbacks to observable
+ )
+ .fetchResults();
+ }, [client, query, options]);
+ return MutationHookResult(
+ runMutation: runMutation,
+ result: snapshot.data!,
+ );
+}

ADD
packages/graphql_flutter/lib/src/widgets/hooks/query.dart
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:graphql_flutter/graphql_flutter.dart';
+import 'package:graphql_flutter/src/widgets/hooks/watch_query.dart';
+
+// method to call from widget to fetchmore queries
+typedef FetchMore<TParsed> = Future<QueryResult<TParsed>> Function(
+ FetchMoreOptions options);
+
+typedef Refetch<TParsed> = Future<QueryResult<TParsed>?> Function();
+
+class QueryHookResult<TParsed> {
+ final QueryResult<TParsed> result;
+ final Refetch<TParsed> refetch;
+ final FetchMore<TParsed> fetchMore;
+
+ QueryHookResult({
+ required this.result,
+ required this.refetch,
+ required this.fetchMore,
+ });
+}
+
+QueryHookResult<TParsed> useQuery<TParsed>(QueryOptions<TParsed> options) {
+ final watchQueryOptions = useMemoized(
+ () => options.asWatchQueryOptions(),
+ [options],
+ );
+ final query = useWatchQuery(watchQueryOptions);
+ final snapshot = useStream(
+ query.stream,
+ initialData: query.latestResult,
+ );
+ return QueryHookResult(
+ result: snapshot.data!,
+ refetch: query.refetch,
+ fetchMore: query.fetchMore,
+ );
+}

ADD
packages/graphql_flutter/lib/src/widgets/hooks/subscription.dart
+import 'dart:async';
+import 'dart:io';
+
+import 'package:connectivity_plus/connectivity_plus.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:graphql_flutter/graphql_flutter.dart';
+import 'package:graphql_flutter/src/widgets/hooks/graphql_client.dart';
+
+typedef OnSubscriptionResult<TParsed> = void Function(
+ QueryResult<TParsed> subscriptionResult,
+ GraphQLClient? client,
+);
+
+typedef SubscriptionBuilder<TParsed> = Widget Function(
+ QueryResult<TParsed> result);
+
+QueryResult<TParsed> useSubscription<TParsed>(
+ SubscriptionOptions<TParsed> options, {
+ OnSubscriptionResult<TParsed>? onSubscriptionResult,
+}) {
+ final client = useGraphQLClient();
+ final stream = use(_SubscriptionHook(
+ client: client,
+ onSubscriptionResult: onSubscriptionResult,
+ options: options,
+ ));
+ final snapshot = useStream(
+ stream,
+ initialData: options.optimisticResult != null
+ ? QueryResult.optimistic(
+ data: options.optimisticResult as Map<String, dynamic>?,
+ parserFn: options.parserFn,
+ )
+ : QueryResult.loading(parserFn: options.parserFn),
+ );
+ return snapshot.data!;
+}
+
+class _SubscriptionHook<TParsed> extends Hook<Stream<QueryResult<TParsed>>> {
+ final SubscriptionOptions<TParsed> options;
+ final GraphQLClient client;
+ final OnSubscriptionResult<TParsed>? onSubscriptionResult;
+ _SubscriptionHook({
+ required this.options,
+ required this.client,
+ required this.onSubscriptionResult,
+ });
+ @override
+ HookState<Stream<QueryResult<TParsed>>, Hook<Stream<QueryResult<TParsed>>>>
+ createState() {
+ return _SubscriptionHookState();
+ }
+}
+
+class _SubscriptionHookState<TParsed> extends HookState<
+ Stream<QueryResult<TParsed>>, _SubscriptionHook<TParsed>> {
+ late Stream<QueryResult<TParsed>> stream;
+ GraphQLClient? client;
+
+ ConnectivityResult? _currentConnectivityResult;
+ StreamSubscription<ConnectivityResult>? _networkSubscription;
+
+ void _initSubscription() {
+ stream = client!.subscribe(hook.options);
+ final onSubscriptionResult = hook.onSubscriptionResult;
+ if (onSubscriptionResult != null) {
+ stream = stream.map((result) {
+ onSubscriptionResult(result, client);
+ return result;
+ });
+ }
+ }
+
+ @override
+ void initHook() {
+ super.initHook();
+ _networkSubscription =
+ Connectivity().onConnectivityChanged.listen(_onNetworkChange);
+ }
+
+ @override
+ void didUpdateHook(_SubscriptionHook<TParsed> oldHook) {
+ super.didUpdateHook(oldHook);
+
+ if (hook.options != oldHook.options || hook.client != oldHook.client) {
+ _initSubscription();
+ }
+ }
+
+ @override
+ void dispose() {
+ _networkSubscription?.cancel();
+ super.dispose();
+ }
+
+ Future _onNetworkChange(ConnectivityResult result) async {
+ //if from offline to online
+ if (_currentConnectivityResult == ConnectivityResult.none &&
+ (result == ConnectivityResult.mobile ||
+ result == ConnectivityResult.wifi)) {
+ _currentConnectivityResult = result;
+
+ // android connectivitystate cannot be trusted
+ // validate with nslookup
+ if (Platform.isAndroid) {
+ try {
+ final nsLookupResult = await InternetAddress.lookup('google.com');
+ if (nsLookupResult.isNotEmpty &&
+ nsLookupResult[0].rawAddress.isNotEmpty) {
+ _initSubscription();
+ }
+ // on exception -> no real connection, set current state to none
+ } on SocketException catch (_) {
+ _currentConnectivityResult = ConnectivityResult.none;
+ }
+ } else {
+ _initSubscription();
+ }
+ } else {
+ _currentConnectivityResult = result;
+ }
+ }
+
+ @override
+ Stream<QueryResult<TParsed>> build(BuildContext context) {
+ return stream;
+ }
+}

ADD
packages/graphql_flutter/lib/src/widgets/hooks/watch_query.dart
+import 'package:flutter/widgets.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:graphql_flutter/graphql_flutter.dart';
+import 'package:graphql_flutter/src/widgets/hooks/graphql_client.dart';
+
+class _WatchQueryHook<TParsed> extends Hook<ObservableQuery<TParsed>> {
+ final GraphQLClient client;
+ final WatchQueryOptions<TParsed> options;
+
+ _WatchQueryHook({
+ required this.options,
+ required this.client,
+ });
+
+ @override
+ HookState<ObservableQuery<TParsed>, Hook<ObservableQuery<TParsed>>>
+ createState() {
+ return _WatchQueryHookState();
+ }
+}
+
+class _WatchQueryHookState<TParsed>
+ extends HookState<ObservableQuery<TParsed>, _WatchQueryHook<TParsed>> {
+ late ObservableQuery<TParsed> _observableQuery;
+
+ @override
+ initHook() {
+ super.initHook();
+ _connect();
+ }
+
+ @override
+ dispose() {
+ _close();
+ super.dispose();
+ }
+
+ _connect() {
+ _observableQuery = hook.client.queryManager.watchQuery(hook.options);
+ }
+
+ _close() {
+ _observableQuery.close();
+ }
+
+ _reconnect() {
+ _close();
+ _connect();
+ }
+
+ @override
+ didUpdateHook(oldHook) {
+ super.didUpdateHook(oldHook);
+ if (oldHook.client == hook.client && oldHook.options == hook.options) {
+ return;
+ }
+ _reconnect();
+ }
+
+ ObservableQuery<TParsed> build(BuildContext context) {
+ return _observableQuery;
+ }
+}
+
+ObservableQuery<TParsed> useWatchQuery<TParsed>(
+ WatchQueryOptions<TParsed> options,
+) {
+ final client = useGraphQLClient();
+ final overwrittenOptions = useMemoized(() {
+ final policies =
+ client.defaultPolicies.query.withOverrides(options.policies);
+ return options.copyWithPolicies(policies);
+ }, [options]);
+
+ return use(_WatchQueryHook(
+ options: overwrittenOptions,
+ client: client,
+ ));
+}
+
+ObservableQuery<TParsed> useWatchMutation<TParsed>(
+ WatchQueryOptions<TParsed> options) {
+ final client = useGraphQLClient();
+ final overwrittenOptions = useMemoized(() {
+ final policies =
+ client.defaultPolicies.mutate.withOverrides(options.policies);
+ return options.copyWithPolicies(policies);
+ }, [options]);
+ return use(
+ _WatchQueryHook(
+ options: overwrittenOptions,
+ client: client,
+ ),
+ );
+}

MODIFY
packages/graphql_flutter/lib/src/widgets/mutation.dart
packages/graphql_flutter/lib/src/widgets/mutation.dart
import 'package:flutter/widgets.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:graphql/client.dart';
+import 'package:graphql_flutter/src/widgets/hooks/mutation.dart';
-import 'package:graphql_flutter/src/widgets/graphql_provider.dart';
-
-typedef RunMutation<TParsed> = MultiSourceResult<TParsed> Function(
- Map<String, dynamic> variables, {
- Object? optimisticResult,
-});
+export 'package:graphql_flutter/src/widgets/hooks/mutation.dart';
typedef MutationBuilder<TParsed> = Widget Function(
RunMutation<TParsed> runMutation,
@@ -16,7 +13,7 @@ typedef MutationBuilder<TParsed> = Widget Function(
/// Builds a [Mutation] widget based on the a given set of [MutationOptions]
/// that streams [QueryResult]s into the [QueryBuilder].
-class Mutation<TParsed> extends StatefulWidget {
+class Mutation<TParsed> extends HookWidget {
const Mutation({
final Key? key,
required this.options,
@@ -26,109 +23,9 @@ class Mutation<TParsed> extends StatefulWidget {
final MutationOptions<TParsed> options;
final MutationBuilder<TParsed> builder;
- @override
- MutationState<TParsed> createState() => MutationState();
-}
-
-class MutationState<TParsed> extends State<Mutation<TParsed>> {
- GraphQLClient? client;
- ObservableQuery<TParsed>? observableQuery;
-
- WatchQueryOptions<TParsed>? __cachedOptions;
-
- WatchQueryOptions<TParsed> get _providedOptions {
- final _options = WatchQueryOptions<TParsed>(
- document: widget.options.document,
- operationName: widget.options.operationName,
- variables: widget.options.variables,
- fetchPolicy: widget.options.fetchPolicy,
- errorPolicy: widget.options.errorPolicy,
- cacheRereadPolicy: widget.options.cacheRereadPolicy,
- fetchResults: false,
- context: widget.options.context,
- parserFn: widget.options.parserFn,
- );
- __cachedOptions ??= _options;
- return _options;
- }
-
- /// sets new options, returning true if they didn't equal the old
- bool _setNewOptions() {
- final _cached = __cachedOptions;
- final _new = _providedOptions;
- if (_cached == null || !_new.equal(_cached)) {
- __cachedOptions = _new;
- return true;
- }
- return false;
- }
-
- // TODO is it possible to extract shared logic into mixin
- void _initQuery() {
- observableQuery?.close();
- observableQuery = client!.watchMutation(_providedOptions.copy());
- }
-
- @override
- void didChangeDependencies() {
- super.didChangeDependencies();
- final GraphQLClient client = GraphQLProvider.of(context).value;
- if (client != this.client) {
- this.client = client;
- _initQuery();
- }
- }
-
- @override
- void didUpdateWidget(Mutation<TParsed> oldWidget) {
- super.didUpdateWidget(oldWidget);
-
- // TODO @micimize - investigate why/if this was causing issues
- if (_setNewOptions()) {
- _initQuery();
- }
- }
-
- /// Run the mutation with the given `variables` and `optimisticResult`,
- /// returning a [MultiSourceResult] for handling both the eager and network results
- MultiSourceResult<TParsed> runMutation(
- Map<String, dynamic> variables, {
- Object? optimisticResult,
- }) {
- final mutationCallbacks = MutationCallbackHandler(
- cache: client!.cache,
- queryId: observableQuery!.queryId,
- options: widget.options,
- );
-
- return (observableQuery!
- ..variables = variables
- ..options.optimisticResult = optimisticResult
- ..onData(mutationCallbacks.callbacks) // add callbacks to observable
- )
- .fetchResults();
- }
-
- @override
- void dispose() {
- observableQuery?.close(force: false);
- super.dispose();
- }
-
@override
Widget build(BuildContext context) {
- return StreamBuilder<QueryResult<TParsed>?>(
- initialData: observableQuery?.latestResult ?? QueryResult.unexecuted,
- stream: observableQuery?.stream,
- builder: (
- BuildContext buildContext,
- AsyncSnapshot<QueryResult<TParsed>?> snapshot,
- ) {
- return widget.builder(
- runMutation,
- snapshot.data,
- );
- },
- );
+ final result = useMutation(options);
+ return builder(result.runMutation, result.result);
}
}

MODIFY
packages/graphql_flutter/lib/src/widgets/query.dart
packages/graphql_flutter/lib/src/widgets/query.dart
import 'package:flutter/widgets.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
-import 'package:graphql/client.dart';
-
-import 'package:graphql_flutter/src/widgets/graphql_provider.dart';
-
-// method to call from widget to fetchmore queries
-typedef FetchMore<TParsed> = Future<QueryResult<TParsed>> Function(
- FetchMoreOptions options);
-
-typedef Refetch<TParsed> = Future<QueryResult<TParsed>?> Function();
+import 'package:graphql_flutter/graphql_flutter.dart';
+export 'package:graphql_flutter/graphql_flutter.dart';
typedef QueryBuilder<TParsed> = Widget Function(
QueryResult<TParsed> result, {
@@ -18,7 +12,7 @@ typedef QueryBuilder<TParsed> = Widget Function(
/// Builds a [Query] widget based on the a given set of [QueryOptions]
/// that streams [QueryResult]s into the [QueryBuilder].
-class Query<TParsed> extends StatefulWidget {
+class Query<TParsed> extends HookWidget {
const Query({
final Key? key,
required this.options,
@@ -28,71 +22,13 @@ class Query<TParsed> extends StatefulWidget {
final QueryOptions<TParsed> options;
final QueryBuilder<TParsed> builder;
- @override
- QueryState<TParsed> createState() => QueryState();
-}
-
-class QueryState<TParsed> extends State<Query<TParsed>> {
- ObservableQuery<TParsed>? observableQuery;
- GraphQLClient? _client;
-
- WatchQueryOptions<TParsed> get _options =>
- widget.options.asWatchQueryOptions();
-
- void _initQuery() {
- observableQuery?.close();
- observableQuery = _client!.watchQuery(_options);
- }
-
- @override
- void didChangeDependencies() {
- super.didChangeDependencies();
- final GraphQLClient client = GraphQLProvider.of(context).value;
- if (client != _client) {
- _client = client;
- _initQuery();
- }
- }
-
- @override
- void didUpdateWidget(Query<TParsed> oldWidget) {
- super.didUpdateWidget(oldWidget);
-
- final GraphQLClient client = GraphQLProvider.of(context).value;
-
- final optionsWithOverrides = _options;
- optionsWithOverrides.policies = client.defaultPolicies.watchQuery
- .withOverrides(optionsWithOverrides.policies);
-
- if (!observableQuery!.options.equal(optionsWithOverrides)) {
- _initQuery();
- }
- }
-
- @override
- void dispose() {
- observableQuery?.close();
- super.dispose();
- }
-
@override
Widget build(BuildContext context) {
- return StreamBuilder<QueryResult<TParsed>>(
- initialData: observableQuery?.latestResult ??
- QueryResult.loading(
- parserFn: widget.options.parserFn,
- ),
- stream: observableQuery!.stream,
- builder: (
- BuildContext buildContext,
- AsyncSnapshot<QueryResult<TParsed>> snapshot,
- ) {
- return widget.builder(
- snapshot.data!,
- refetch: observableQuery!.refetch,
- fetchMore: observableQuery!.fetchMore,
- );
- },
+ final result = useQuery(options);
+ return builder(
+ result.result,
+ fetchMore: result.fetchMore,
+ refetch: result.refetch,
);
}
}

MODIFY
packages/graphql_flutter/lib/src/widgets/subscription.dart
packages/graphql_flutter/lib/src/widgets/subscription.dart
-import 'dart:async';
-import 'dart:io';
-
-import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/widgets.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:graphql_flutter/graphql_flutter.dart';
-typedef OnSubscriptionResult<TParsed> = void Function(
- QueryResult<TParsed> subscriptionResult,
- GraphQLClient? client,
-);
-
-typedef SubscriptionBuilder<TParsed> = Widget Function(
- QueryResult<TParsed> result);
-
/// Creats a subscription with [GraphQLClient.subscribe].
///
/// The [builder] is passed a [QueryResult] with only the **most recent**
@@ -63,7 +52,7 @@ typedef SubscriptionBuilder<TParsed> = Widget Function(
/// }
/// ```
/// {@end-tool}
-class Subscription<TParsed> extends StatefulWidget {
+class Subscription<TParsed> extends HookWidget {
const Subscription({
required this.options,
required this.builder,
@@ -75,105 +64,12 @@ class Subscription<TParsed> extends StatefulWidget {
final SubscriptionBuilder<TParsed> builder;
final OnSubscriptionResult<TParsed>? onSubscriptionResult;
- @override
- _SubscriptionState<TParsed> createState() => _SubscriptionState();
-}
-
-class _SubscriptionState<TParsed> extends State<Subscription<TParsed>> {
- Stream<QueryResult<TParsed>>? stream;
- GraphQLClient? client;
-
- ConnectivityResult? _currentConnectivityResult;
- StreamSubscription<ConnectivityResult>? _networkSubscription;
-
- void _initSubscription() {
- stream = client!.subscribe(widget.options);
-
- if (widget.onSubscriptionResult != null) {
- stream = stream!.map((result) {
- widget.onSubscriptionResult!(result, client);
- return result;
- });
- }
- }
-
- @override
- void initState() {
- _networkSubscription =
- Connectivity().onConnectivityChanged.listen(_onNetworkChange);
-
- super.initState();
- }
-
- @override
- void didChangeDependencies() {
- super.didChangeDependencies();
- final GraphQLClient newClient = GraphQLProvider.of(context).value;
- if (client != newClient) {
- client = newClient;
- _initSubscription();
- }
- }
-
- @override
- void didUpdateWidget(Subscription<TParsed> oldWidget) {
- super.didUpdateWidget(oldWidget);
-
- if (!widget.options.equal(oldWidget.options)) {
- _initSubscription();
- }
- }
-
- @override
- void dispose() {
- _networkSubscription?.cancel();
- super.dispose();
- }
-
- Future _onNetworkChange(ConnectivityResult result) async {
- //if from offline to online
- if (_currentConnectivityResult == ConnectivityResult.none &&
- (result == ConnectivityResult.mobile ||
- result == ConnectivityResult.wifi)) {
- _currentConnectivityResult = result;
-
- // android connectivitystate cannot be trusted
- // validate with nslookup
- if (Platform.isAndroid) {
- try {
- final nsLookupResult = await InternetAddress.lookup('google.com');
- if (nsLookupResult.isNotEmpty &&
- nsLookupResult[0].rawAddress.isNotEmpty) {
- _initSubscription();
- }
- // on exception -> no real connection, set current state to none
- } on SocketException catch (_) {
- _currentConnectivityResult = ConnectivityResult.none;
- }
- } else {
- _initSubscription();
- }
- } else {
- _currentConnectivityResult = result;
- }
- }
-
@override
Widget build(BuildContext context) {
- return StreamBuilder<QueryResult<TParsed>>(
- initialData: widget.options.optimisticResult != null
- ? QueryResult.optimistic(
- data: widget.options.optimisticResult as Map<String, dynamic>?,
- parserFn: widget.options.parserFn,
- )
- : QueryResult.loading(parserFn: widget.options.parserFn),
- stream: stream,
- builder: (
- BuildContext buildContext,
- AsyncSnapshot<QueryResult<TParsed>> snapshot,
- ) {
- return widget.builder(snapshot.data!);
- },
+ final result = useSubscription<TParsed>(
+ options,
+ onSubscriptionResult: onSubscriptionResult,
);
+ return builder(result);
}
}

MODIFY
packages/graphql_flutter/pubspec.yaml
packages/graphql_flutter/pubspec.yaml
@@ -13,6 +13,7 @@ dependencies:
connectivity_plus: ^2.0.3
hive: ^2.0.0
plugin_platform_interface: ^2.0.0
+ flutter_hooks: ^0.18.2
dev_dependencies:
pedantic: ^1.11.0
mockito: ^5.0.0

MODIFY
packages/graphql_flutter/test/widgets/query_test.dart
packages/graphql_flutter/test/widgets/query_test.dart
@@ -32,17 +32,19 @@ final query = gql("""
Future<void> mockApplicationDocumentsDirectory() async {
// Create a temporary directory.
final directory = await Directory.systemTemp.createTemp();
-
- // Mock out the MethodChannel for the path_provider plugin.
- const MethodChannel('plugins.flutter.io/path_provider')
- .setMockMethodCallHandler((MethodCall methodCall) async {
+ final handler = (MethodCall methodCall) async {
// If you're getting the apps documents directory, return the path to the
// temp directory on the test environment instead.
if (methodCall.method == 'getApplicationDocumentsDirectory') {
return directory.path;
}
return null;
- });
+ };
+ // Mock out the MethodChannel for the path_provider plugin.
+ const MethodChannel('plugins.flutter.io/path_provider')
+ .setMockMethodCallHandler(handler);
+ const MethodChannel('plugins.flutter.io/path_provider_macos')
+ .setMockMethodCallHandler(handler);
}
class Page extends StatefulWidget {
@@ -339,7 +341,7 @@ void main() {
'does not issues new network request when policies are effectively unchanged',
(WidgetTester tester) async {
final page = Page(
- fetchPolicy: FetchPolicy.cacheAndNetwork,
+ fetchPolicy: client!.value.defaultPolicies.query.fetch,
errorPolicy: null,
);

feat: Use hooks

Re-implement widgets using flutter_hooks and expose said hooks.