forked from zino-hofmann/graphql-flutter
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Re-implement widgets using flutter_hooks and expose said hooks.
- Loading branch information
1 parent
2437565
commit db9305a
Showing
15 changed files
with
402 additions
and
299 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" | ||
#include "Generated.xcconfig" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" | ||
#include "Generated.xcconfig" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 7 additions & 0 deletions
7
packages/graphql_flutter/lib/src/widgets/hooks/graphql_client.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import 'package:flutter_hooks/flutter_hooks.dart'; | ||
import 'package:graphql_flutter/graphql_flutter.dart'; | ||
|
||
GraphQLClient useGraphQLClient() { | ||
final context = useContext(); | ||
return useValueListenable(GraphQLProvider.of(context)); | ||
} |
53 changes: 53 additions & 0 deletions
53
packages/graphql_flutter/lib/src/widgets/hooks/mutation.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
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!, | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
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, | ||
); | ||
} |
129 changes: 129 additions & 0 deletions
129
packages/graphql_flutter/lib/src/widgets/hooks/subscription.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
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; | ||
} | ||
} |
95 changes: 95 additions & 0 deletions
95
packages/graphql_flutter/lib/src/widgets/hooks/watch_query.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
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, | ||
), | ||
); | ||
} |
Oops, something went wrong.