diff --git a/.gitignore b/.gitignore index ee8bd6f..65167e9 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ ios/.generated/ ios/Flutter/Generated.xcconfig ios/Runner/GeneratedPluginRegistrant.* .flutter-plugins +example/.flutter-plugins-dependencies +example/ios/Flutter/flutter_export_environment.sh diff --git a/analysis_options.yaml b/analysis_options.yaml index b1e1c9f..2db889d 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -19,7 +19,6 @@ linter: - always_specify_types - annotate_overrides # - avoid_annotating_with_dynamic # conflicts with always_specify_types - - avoid_as # - avoid_bool_literals_in_conditional_expressions # not yet tested # - avoid_catches_without_on_clauses # we do this commonly # - avoid_catching_errors # we do this commonly @@ -78,7 +77,6 @@ linter: # - parameter_assignments # we do this commonly - prefer_adjacent_string_concatenation - prefer_asserts_in_initializer_lists - - prefer_bool_in_asserts - prefer_collection_literals - prefer_conditional_assignment - prefer_const_constructors @@ -105,7 +103,6 @@ linter: - slash_for_doc_comments - sort_constructors_first - sort_unnamed_constructors_first - - super_goes_last - test_types_in_equals - throw_in_finally # - type_annotate_public_apis # subset of always_specify_types diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml index b1e1c9f..2db889d 100644 --- a/example/analysis_options.yaml +++ b/example/analysis_options.yaml @@ -19,7 +19,6 @@ linter: - always_specify_types - annotate_overrides # - avoid_annotating_with_dynamic # conflicts with always_specify_types - - avoid_as # - avoid_bool_literals_in_conditional_expressions # not yet tested # - avoid_catches_without_on_clauses # we do this commonly # - avoid_catching_errors # we do this commonly @@ -78,7 +77,6 @@ linter: # - parameter_assignments # we do this commonly - prefer_adjacent_string_concatenation - prefer_asserts_in_initializer_lists - - prefer_bool_in_asserts - prefer_collection_literals - prefer_conditional_assignment - prefer_const_constructors @@ -105,7 +103,6 @@ linter: - slash_for_doc_comments - sort_constructors_first - sort_unnamed_constructors_first - - super_goes_last - test_types_in_equals - throw_in_finally # - type_annotate_public_apis # subset of always_specify_types diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 449d114..26f37f9 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -15,3 +15,6 @@ dev_dependencies: flutter: uses-material-design: true + +environment: + sdk: ">=2.10.0 <3.0.0" \ No newline at end of file diff --git a/lib/flutter_graphql.dart b/lib/flutter_graphql.dart index 37038ed..0b49865 100644 --- a/lib/flutter_graphql.dart +++ b/lib/flutter_graphql.dart @@ -1,26 +1,21 @@ library flutter_graphql; -export 'package:flutter_graphql/src/graphql_client.dart'; -export 'package:flutter_graphql/src/socket_client.dart'; - +export 'package:flutter_graphql/src/cache/in_memory.dart'; +export 'package:flutter_graphql/src/cache/normalized_in_memory.dart'; +export 'package:flutter_graphql/src/core/graphql_error.dart'; export 'package:flutter_graphql/src/core/query_options.dart'; export 'package:flutter_graphql/src/core/query_result.dart'; -export 'package:flutter_graphql/src/core/graphql_error.dart'; - -export 'package:flutter_graphql/src/link/link.dart'; +export 'package:flutter_graphql/src/graphql_client.dart'; +export 'package:flutter_graphql/src/link/fetch_result.dart'; export 'package:flutter_graphql/src/link/http/link_http.dart'; +export 'package:flutter_graphql/src/link/link.dart'; export 'package:flutter_graphql/src/link/operation.dart'; -export 'package:flutter_graphql/src/link/fetch_result.dart'; - -export 'package:flutter_graphql/src/cache/in_memory.dart'; -export 'package:flutter_graphql/src/cache/normalized_in_memory.dart'; - +export 'package:flutter_graphql/src/socket_client.dart'; export 'package:flutter_graphql/src/websocket/messages.dart'; export 'package:flutter_graphql/src/websocket/socket.dart'; - -export 'package:flutter_graphql/src/widgets/graphql_provider.dart'; -export 'package:flutter_graphql/src/widgets/graphql_consumer.dart'; export 'package:flutter_graphql/src/widgets/cache_provider.dart'; -export 'package:flutter_graphql/src/widgets/query.dart'; +export 'package:flutter_graphql/src/widgets/graphql_consumer.dart'; +export 'package:flutter_graphql/src/widgets/graphql_provider.dart'; export 'package:flutter_graphql/src/widgets/mutation.dart'; +export 'package:flutter_graphql/src/widgets/query.dart'; export 'package:flutter_graphql/src/widgets/subscription.dart'; diff --git a/lib/src/cache/cache.dart b/lib/src/cache/cache.dart index eb8bee7..61dfd0e 100644 --- a/lib/src/cache/cache.dart +++ b/lib/src/cache/cache.dart @@ -1,5 +1,5 @@ abstract class Cache { - Future remove(String key, bool cascade) async {} + Future? remove(String key, bool cascade); dynamic read(String key) {} @@ -13,5 +13,4 @@ abstract class Cache { void restore() {} void reset() {} - } diff --git a/lib/src/cache/in_memory.dart b/lib/src/cache/in_memory.dart index 7335aa2..2c9959a 100644 --- a/lib/src/cache/in_memory.dart +++ b/lib/src/cache/in_memory.dart @@ -14,7 +14,7 @@ class InMemoryCache implements Cache { /// A directory to be used for storage. /// This is used for testing, on regular usage /// 'path_provider' will provide the storage directory. - final Directory customStorageDirectory; + final Directory? customStorageDirectory; HashMap _inMemoryCache = HashMap(); bool _writingToStorage = false; @@ -64,7 +64,7 @@ class InMemoryCache implements Cache { Future get _localStoragePath async { if (customStorageDirectory != null) { // Used for testing - return customStorageDirectory.path; + return customStorageDirectory!.path; } final Directory directory = await getApplicationDocumentsDirectory(); @@ -139,7 +139,7 @@ class InMemoryCache implements Cache { } @override - Future remove(String key, bool cascade) { + Future? remove(String key, bool cascade) { // TODO: implement remove return null; } diff --git a/lib/src/cache/normalized/record_field_json_adapter.dart b/lib/src/cache/normalized/record_field_json_adapter.dart index bce333f..7630da6 100644 --- a/lib/src/cache/normalized/record_field_json_adapter.dart +++ b/lib/src/cache/normalized/record_field_json_adapter.dart @@ -2,20 +2,15 @@ import 'dart:convert'; import 'dart:core'; class RecordFieldJsonAdapter { - static RecordFieldJsonAdapter create() { - return new RecordFieldJsonAdapter(); - } - - RecordFieldJsonAdapter() { + return RecordFieldJsonAdapter(); } dynamic toJson(Map fields) { - assert(fields != null); return json.encode(fields); } - Map from(dynamic jsonObj) { + Map? from(dynamic jsonObj) { assert(jsonObj != null); return json.decode(jsonObj); } @@ -27,4 +22,4 @@ class RecordFieldJsonAdapter { return cacheJsonStreamReader.toMap(); } */ -} \ No newline at end of file +} diff --git a/lib/src/cache/normalized/sql/sql-normalized-cache.dart b/lib/src/cache/normalized/sql/sql-normalized-cache.dart index e131b72..e0b35b6 100644 --- a/lib/src/cache/normalized/sql/sql-normalized-cache.dart +++ b/lib/src/cache/normalized/sql/sql-normalized-cache.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:collection'; import 'package:flutter_graphql/src/cache/normalized/record_field_json_adapter.dart'; @@ -7,33 +8,33 @@ import 'package:sqflite/sqflite.dart'; import '../../cache.dart'; class SqlNormalizedCache implements Cache { - SqlNormalizedCache(this.dbHelper, this.recordFieldAdapter) { dbHelper.open(); database = dbHelper.db; } - + static const String UPDATE_STATEMENT = '''UPDATE ${SqlHelper.TABLE_RECORDS} SET ${SqlHelper.COLUMN_KEY}=?, ${SqlHelper.COLUMN_RECORD}=? WHERE ${SqlHelper.COLUMN_KEY}=?'''; static const String DELETE_STATEMENT = '''DELETE FROM ${SqlHelper.TABLE_RECORDS} WHERE ${SqlHelper.COLUMN_KEY}=?'''; static const String DELETE_ALL_RECORD_STATEMENT = '''DELETE FROM ${SqlHelper.TABLE_RECORDS}'''; - Database database; + Database? database; final SqlHelper dbHelper; final allColumns = [ - SqlHelper.COLUMN_ID, - SqlHelper.COLUMN_KEY, - SqlHelper.COLUMN_RECORD]; + SqlHelper.COLUMN_ID, + SqlHelper.COLUMN_KEY, + SqlHelper.COLUMN_RECORD + ]; final RecordFieldJsonAdapter recordFieldAdapter; HashMap _inMemoryCache = HashMap(); @override - Object read(String key) { + Object? read(String key) { // TODO: implement read return null; } Future>> _readFromStorage() async { - List> records = await database.query(SqlHelper.TABLE_RECORDS); + List> records = await (database!.query(SqlHelper.TABLE_RECORDS) as FutureOr>>); return records; } @@ -54,14 +55,17 @@ class SqlNormalizedCache implements Cache { @override void write(String key, dynamic values) { - database.insert(SqlHelper.TABLE_RECORDS, values); + database!.insert(SqlHelper.TABLE_RECORDS, values); } @override Future remove(String key, bool cascade) async { assert(key != null); - final deletedObj = await database.delete(SqlHelper.TABLE_RECORDS, where: '${SqlHelper.COLUMN_KEY}=?', whereArgs: [key].toList()); + final deletedObj = await database!.delete(SqlHelper.TABLE_RECORDS, + where: '${SqlHelper.COLUMN_KEY}=?', + whereArgs: [ + key + ].toList()); return true; } - -} \ No newline at end of file +} diff --git a/lib/src/cache/normalized/sql/sql_helper.dart b/lib/src/cache/normalized/sql/sql_helper.dart index 86820f8..1dd04b8 100644 --- a/lib/src/cache/normalized/sql/sql_helper.dart +++ b/lib/src/cache/normalized/sql/sql_helper.dart @@ -15,7 +15,7 @@ class SqlHelper { static const String IDX_RECORDS_KEY = 'idx_records_key'; static const String CREATE_KEY_INDEX = '''CREATE INDEX $IDX_RECORDS_KEY ON $TABLE_RECORDS ($COLUMN_KEY)'''; - Database db; + Database? db; Future open() async { final databasesPath = await getDatabasesPath(); @@ -27,7 +27,7 @@ class SqlHelper { } Future close() async { - await db.close(); + await db!.close(); } } \ No newline at end of file diff --git a/lib/src/cache/normalized_in_memory.dart b/lib/src/cache/normalized_in_memory.dart index 6d6be0e..83785da 100644 --- a/lib/src/cache/normalized_in_memory.dart +++ b/lib/src/cache/normalized_in_memory.dart @@ -1,28 +1,28 @@ -import 'package:meta/meta.dart'; -import 'package:flutter_graphql/src/utilities/traverse.dart'; import 'package:flutter_graphql/src/cache/in_memory.dart'; -typedef String DataIdFromObject(Object node); +import 'package:flutter_graphql/src/utilities/traverse.dart'; + +typedef String? DataIdFromObject(Object? node); class NormalizationException implements Exception { NormalizationException(this.cause, this.overflowError, this.value); StackOverflowError overflowError; String cause; - Object value; + Object? value; String get message => cause; } class NormalizedInMemoryCache extends InMemoryCache { NormalizedInMemoryCache({ - @required this.dataIdFromObject, + required this.dataIdFromObject, String prefix = '@cache/reference', }) : _prefix = prefix; DataIdFromObject dataIdFromObject; String _prefix; - Object _dereference(Object node) { + Object? _dereference(Object? node) { if (node is List && node.length == 2 && node[0] == _prefix) { return read(node[1]); } @@ -36,7 +36,7 @@ class NormalizedInMemoryCache extends InMemoryCache { */ @override dynamic read(String key) { - final Object value = super.read(key); + final Object? value = super.read(key); try { return traverse(value, _dereference); @@ -54,12 +54,15 @@ class NormalizedInMemoryCache extends InMemoryCache { } } - List _normalize(Object node) { - final String dataId = dataIdFromObject(node); + List? _normalize(Object? node) { + final String? dataId = dataIdFromObject(node); if (dataId != null) { write(dataId, node); - return [_prefix, dataId]; + return [ + _prefix, + dataId + ]; } return null; @@ -70,17 +73,15 @@ class NormalizedInMemoryCache extends InMemoryCache { replacing them with references */ @override - void write(String key, Object value) { - final Object normalized = traverseValues(value, _normalize); + void write(String key, Object? value) { + final Object normalized = traverseValues(Map.castFrom(value as Map), _normalize); super.write(key, normalized); } } -String typenameDataIdFromObject(Object object) { - if (object is Map && - object.containsKey('__typename') && - object.containsKey('id')) { - return "${object['__typename']}/${object['id']}"; +String? typenameDataIdFromObject(Object? object) { + if (object is Map && object.containsKey('__typename') && object.containsKey('id')) { + return '${object['__typename']}/${object['id']}'; } return null; diff --git a/lib/src/core/graphql_error.dart b/lib/src/core/graphql_error.dart index 228afb5..593e4db 100644 --- a/lib/src/core/graphql_error.dart +++ b/lib/src/core/graphql_error.dart @@ -6,10 +6,10 @@ class Location { column = data['column']; /// The line of the error in the query. - final int line; + final int? line; /// The column of the error in the query. - final int column; + final int? column; @override String toString() => '{ line: $line, column: $column }'; @@ -42,18 +42,18 @@ class GraphQLError { final dynamic data; /// The message of the error. - final String message; + final String? message; /// Locations where the error appear. - final List locations; + final List? locations; /// The path of the field in error. - final List path; + final List? path; /// Custom error data returned by your GraphQL API server - final Map extensions; + final Map? extensions; @override String toString() => - '$message: ${locations is List ? locations.map((Location l) => '[${l.toString()}]').join('') : "Undefined location"}'; + '$message: ${locations is List ? locations!.map((Location l) => '[${l.toString()}]').join('') : "Undefined location"}'; } \ No newline at end of file diff --git a/lib/src/core/observable_query.dart b/lib/src/core/observable_query.dart index c0f5cdd..5f6a161 100644 --- a/lib/src/core/observable_query.dart +++ b/lib/src/core/observable_query.dart @@ -4,10 +4,9 @@ import 'package:flutter_graphql/flutter_graphql.dart'; import 'package:flutter_graphql/src/core/query_manager.dart'; import 'package:flutter_graphql/src/core/query_result.dart'; import 'package:flutter_graphql/src/scheduler/scheduler.dart'; -import 'package:meta/meta.dart'; -typedef void OnData(QueryResult result); +typedef void OnData(QueryResult? result); enum QueryLifecycle { UNEXECUTED, @@ -23,33 +22,33 @@ enum QueryLifecycle { class ObservableQuery { ObservableQuery({ - @required this.queryManager, - @required this.options, + required this.queryManager, + required this.options, }) : queryId = queryManager.generateQueryId().toString(), scheduler = queryManager.scheduler { - controller = StreamController.broadcast( + controller = StreamController.broadcast( onListen: onListen, ); } final String queryId; - final QueryScheduler scheduler; + final QueryScheduler? scheduler; final QueryManager queryManager; - final Set> _onDataSubscriptions = + final Set> _onDataSubscriptions = Set>(); QueryLifecycle lifecycle = QueryLifecycle.UNEXECUTED; WatchQueryOptions options; - StreamController controller; + late StreamController controller; - Stream get stream => controller.stream; + Stream get stream => controller.stream; bool get isCurrentlyPolling => lifecycle == QueryLifecycle.POLLING; void onListen() { - if (options.fetchResults) { + if (options.fetchResults!) { controller.add( QueryResult( loading: true, @@ -82,18 +81,18 @@ class ObservableQuery { } // most mutation behavior happens here - void onData(Iterable callbacks) { + void onData(Iterable callbacks) { if (callbacks != null && callbacks.isNotEmpty) { - StreamSubscription subscription; + StreamSubscription? subscription; - subscription = stream.listen((QueryResult result) { - void handle(OnData callback) { - callback(result); + subscription = stream.listen((QueryResult? result) { + void handle(OnData? callback) { + callback!(result); } - if (!result.loading) { + if (!result!.loading!) { callbacks.forEach(handle); - subscription.cancel(); + subscription!.cancel(); _onDataSubscriptions.remove(subscription); if (_onDataSubscriptions.isEmpty) { @@ -111,7 +110,7 @@ class ObservableQuery { } } - void startPolling(int pollInterval) { + void startPolling(int? pollInterval) { if (options.fetchPolicy == FetchPolicy.cacheFirst || options.fetchPolicy == FetchPolicy.cacheOnly) { throw Exception( @@ -120,17 +119,17 @@ class ObservableQuery { } if (isCurrentlyPolling) { - scheduler.stopPollingQuery(queryId); + scheduler!.stopPollingQuery(queryId); } options.pollInterval = pollInterval; lifecycle = QueryLifecycle.POLLING; - scheduler.startPollingQuery(options, queryId); + scheduler!.startPollingQuery(options, queryId); } void stopPolling() { if (isCurrentlyPolling) { - scheduler.stopPollingQuery(queryId); + scheduler!.stopPollingQuery(queryId); options.pollInterval = null; lifecycle = QueryLifecycle.POLLING_STOPPED; } @@ -150,7 +149,7 @@ class ObservableQuery { queryManager.closeQuery(this, fromQuery: true); } - for (StreamSubscription subscription in _onDataSubscriptions) { + for (StreamSubscription subscription in _onDataSubscriptions) { subscription.cancel(); } diff --git a/lib/src/core/query_manager.dart b/lib/src/core/query_manager.dart index 1d55492..716b4d8 100644 --- a/lib/src/core/query_manager.dart +++ b/lib/src/core/query_manager.dart @@ -1,26 +1,20 @@ import 'dart:async'; -import 'package:meta/meta.dart'; - -import 'package:flutter_graphql/src/core/query_options.dart'; -import 'package:flutter_graphql/src/core/query_result.dart'; +import 'package:flutter_graphql/src/cache/cache.dart'; import 'package:flutter_graphql/src/core/graphql_error.dart'; import 'package:flutter_graphql/src/core/observable_query.dart'; - -import 'package:flutter_graphql/src/scheduler/scheduler.dart'; - +import 'package:flutter_graphql/src/core/query_options.dart'; +import 'package:flutter_graphql/src/core/query_result.dart'; +import 'package:flutter_graphql/src/link/fetch_result.dart'; import 'package:flutter_graphql/src/link/link.dart'; import 'package:flutter_graphql/src/link/operation.dart'; -import 'package:flutter_graphql/src/link/fetch_result.dart'; - -import 'package:flutter_graphql/src/cache/cache.dart'; - +import 'package:flutter_graphql/src/scheduler/scheduler.dart'; import 'package:flutter_graphql/src/utilities/get_from_ast.dart'; class QueryManager { QueryManager({ - @required this.link, - @required this.cache, + required this.link, + required this.cache, }) { scheduler = QueryScheduler( queryManager: this, @@ -30,7 +24,7 @@ class QueryManager { final Link link; final Cache cache; - QueryScheduler scheduler; + QueryScheduler? scheduler; int idCounter = 1; Map queries = {}; @@ -51,22 +45,22 @@ class QueryManager { return observableQuery; } - Future query(QueryOptions options) { + Future query(QueryOptions options) { return fetchQuery('0', options); } - Future mutate(MutationOptions options) { + Future mutate(MutationOptions options) { return fetchQuery('0', options); } - Future fetchQuery( + Future fetchQuery( String queryId, BaseOptions options, ) async { - final ObservableQuery observableQuery = getQuery(queryId); + final ObservableQuery? observableQuery = getQuery(queryId); // XXX there is a bug in the `graphql_parser` package, where this result might be // null event though the operation name is present in the document - final String operationName = getOperationName(options.document); + final String? operationName = getOperationName(options.document); // create a new operation to fetch final Operation operation = Operation( document: options.document, @@ -75,11 +69,11 @@ class QueryManager { ); FetchResult fetchResult; - QueryResult queryResult; + QueryResult? queryResult; try { if (options.context != null) { - operation.setContext(options.context); + operation.setContext(options.context!); } if (options.fetchPolicy == FetchPolicy.cacheFirst || @@ -119,8 +113,7 @@ class QueryManager { ).first; // save the data from fetchResult to the cache - if (fetchResult.data != null && - options.fetchPolicy != FetchPolicy.noCache) { + if (fetchResult.data != null && options.fetchPolicy != FetchPolicy.noCache) { cache.write( operation.toKey(), fetchResult.data, @@ -138,10 +131,14 @@ class QueryManager { queryResult = _mapFetchResultToQueryResult(fetchResult); } catch (error) { - // TODO some dart errors break this - final GraphQLError graphQLError = GraphQLError( - message: error.message, - ); + GraphQLError graphQLError; + + try { + // TODO some dart errors break this + graphQLError = GraphQLError(message: error.toString()); + } catch (e) { + graphQLError = GraphQLError(message: 'An error has ocurred'); + } if (queryResult != null) { queryResult.addError(graphQLError); @@ -161,7 +158,7 @@ class QueryManager { return queryResult; } - ObservableQuery getQuery(String queryId) { + ObservableQuery? getQuery(String queryId) { if (queries.containsKey(queryId)) { return queries[queryId]; } @@ -189,10 +186,10 @@ class QueryManager { } QueryResult _mapFetchResultToQueryResult(FetchResult fetchResult) { - List errors; + List? errors; if (fetchResult.errors != null) { - errors = List.from(fetchResult.errors.map( + errors = List.from(fetchResult.errors!.map( (dynamic rawError) => GraphQLError.fromJSON(rawError), )); } @@ -203,4 +200,4 @@ class QueryManager { loading: false, ); } -} \ No newline at end of file +} diff --git a/lib/src/core/query_options.dart b/lib/src/core/query_options.dart index d9bebd3..3ddb5ba 100644 --- a/lib/src/core/query_options.dart +++ b/lib/src/core/query_options.dart @@ -1,5 +1,4 @@ import 'package:flutter_graphql/src/graphql_client.dart'; -import 'package:meta/meta.dart'; /// [FetchPolicy] determines where the client may return a result from. The options are: /// - cacheFirst (default): return result from cache. Only fetch from network if cached result is not available. @@ -28,7 +27,7 @@ enum ErrorPolicy { /// Base options. class BaseOptions { BaseOptions({ - @required this.document, + required this.document, this.variables, this.fetchPolicy, this.errorPolicy, @@ -41,30 +40,30 @@ class BaseOptions { /// A map going from variable name to variable value, where the variables are used /// within the GraphQL query. - Map variables; + Map? variables; /// Specifies the [FetchPolicy] to be used. - FetchPolicy fetchPolicy; + FetchPolicy? fetchPolicy; /// Specifies the [ErrorPolicy] to be used. - ErrorPolicy errorPolicy; + ErrorPolicy? errorPolicy; /// Context to be passed to link execution chain. - Map context; + Map? context; - GraphQLClient client; + GraphQLClient? client; } /// Query options. class QueryOptions extends BaseOptions { QueryOptions({ - @required String document, - Map variables, - FetchPolicy fetchPolicy = FetchPolicy.cacheFirst, - ErrorPolicy errorPolicy = ErrorPolicy.none, + required String document, + Map? variables, + FetchPolicy? fetchPolicy = FetchPolicy.cacheFirst, + ErrorPolicy? errorPolicy = ErrorPolicy.none, this.pollInterval, - Map context, - GraphQLClient client, + Map? context, + GraphQLClient? client, }) : super( document: document, variables: variables, @@ -76,18 +75,18 @@ class QueryOptions extends BaseOptions { /// The time interval (in milliseconds) on which this query should be /// refetched from the server. - int pollInterval; + int? pollInterval; } /// Mutation options class MutationOptions extends BaseOptions { MutationOptions({ - @required String document, - Map variables, + required String document, + Map? variables, FetchPolicy fetchPolicy = FetchPolicy.networkOnly, ErrorPolicy errorPolicy = ErrorPolicy.none, - Map context, - GraphQLClient client + Map? context, + GraphQLClient? client }) : super( document: document, variables: variables, @@ -101,14 +100,14 @@ class MutationOptions extends BaseOptions { // ObservableQuery options class WatchQueryOptions extends QueryOptions { WatchQueryOptions({ - @required String document, - Map variables, - FetchPolicy fetchPolicy = FetchPolicy.cacheAndNetwork, - ErrorPolicy errorPolicy = ErrorPolicy.none, - int pollInterval, + required String document, + Map? variables, + FetchPolicy? fetchPolicy = FetchPolicy.cacheAndNetwork, + ErrorPolicy? errorPolicy = ErrorPolicy.none, + int? pollInterval, this.fetchResults, - Map context, - GraphQLClient client, + Map? context, + GraphQLClient? client, }) : super( document: document, variables: variables, @@ -120,7 +119,7 @@ class WatchQueryOptions extends QueryOptions { ); /// Whether or not to fetch result. - bool fetchResults; + bool? fetchResults; /// Checks if the [WatchQueryOptions] in this class are equal to some given options. bool areEqualTo(WatchQueryOptions otherOptions) { @@ -157,8 +156,8 @@ class WatchQueryOptions extends QueryOptions { } bool _areDifferentVariables( - Map a, - Map b, + Map? a, + Map? b, ) { if (a == null && b == null) { return false; diff --git a/lib/src/core/query_result.dart b/lib/src/core/query_result.dart index f72276f..583516c 100644 --- a/lib/src/core/query_result.dart +++ b/lib/src/core/query_result.dart @@ -10,21 +10,21 @@ class QueryResult { /// List or Map dynamic data; - List errors; - bool loading; - bool stale; + List? errors; + bool? loading; + bool? stale; bool get hasErrors { if (errors == null) { return false; } - return errors.isNotEmpty; + return errors!.isNotEmpty; } void addError(GraphQLError graphQLError) { if (errors != null) { - errors.add(graphQLError); + errors!.add(graphQLError); } else { errors = [graphQLError]; } diff --git a/lib/src/graphql_client.dart b/lib/src/graphql_client.dart index 2351015..aaa5801 100644 --- a/lib/src/graphql_client.dart +++ b/lib/src/graphql_client.dart @@ -1,14 +1,11 @@ import 'dart:async'; -import 'package:meta/meta.dart'; - -import 'package:flutter_graphql/src/core/query_manager.dart'; -import 'package:flutter_graphql/src/core/query_result.dart'; +import 'package:flutter_graphql/src/cache/cache.dart'; import 'package:flutter_graphql/src/core/observable_query.dart'; +import 'package:flutter_graphql/src/core/query_manager.dart'; import 'package:flutter_graphql/src/core/query_options.dart'; - +import 'package:flutter_graphql/src/core/query_result.dart'; import 'package:flutter_graphql/src/link/link.dart'; -import 'package:flutter_graphql/src/cache/cache.dart'; /// The link is a [Link] over which GraphQL documents will be resolved into a [FetchResult]. /// The cache is the initial [Cache] to use in the data store. @@ -19,12 +16,12 @@ class GraphQLClient { /// The initial [Cache] to use in the data store. final Cache cache; - QueryManager queryManager; + late QueryManager queryManager; /// Constructs a [GraphQLClient] given a [Link] and a [Cache]. GraphQLClient({ - @required this.link, - @required this.cache, + required this.link, + required this.cache, }) { queryManager = QueryManager( link: link, @@ -40,13 +37,13 @@ class GraphQLClient { /// This resolves a single query according to the [QueryOptions] specified and /// returns a [Future] which resolves with the [QueryResult] or throws an [Exception]. - Future query(QueryOptions options) { + Future query(QueryOptions options) { return queryManager.query(options); } /// This resolves a single mutation according to the [MutationOptions] specified and /// returns a [Future] which resolves with the [QueryResult] or throws an [Exception]. - Future mutate(MutationOptions options) { + Future mutate(MutationOptions options) { return queryManager.mutate(options); } diff --git a/lib/src/link/auth/auth_link.dart b/lib/src/link/auth/auth_link.dart index ca97b02..cca1249 100644 --- a/lib/src/link/auth/auth_link.dart +++ b/lib/src/link/auth/auth_link.dart @@ -1,8 +1,8 @@ import 'dart:async'; +import 'package:flutter_graphql/src/link/fetch_result.dart'; import 'package:flutter_graphql/src/link/link.dart'; import 'package:flutter_graphql/src/link/operation.dart'; -import 'package:flutter_graphql/src/link/fetch_result.dart'; typedef GetToken = Future Function(); @@ -10,21 +10,24 @@ class AuthLink extends Link { AuthLink({ this.getToken, }) : super( - request: (Operation operation, [NextLink forward]) { - StreamController controller; + request: (Operation? operation, [NextLink? forward]) { + late StreamController controller; Future onListen() async { try { - final String token = await getToken(); + final String token = await getToken!(); - operation.setContext(>{ - 'headers': {'Authorization': token} + operation?.setContext(>{ + 'headers': { + 'Authorization': token + } }); } catch (error) { controller.addError(error); } - - await controller.addStream(forward(operation)); + if (forward != null && operation != null) { + await controller.addStream(forward(operation)); + } await controller.close(); } @@ -34,5 +37,5 @@ class AuthLink extends Link { }, ); - GetToken getToken; -} \ No newline at end of file + GetToken? getToken; +} diff --git a/lib/src/link/fetch_result.dart b/lib/src/link/fetch_result.dart index 44a8554..acf27af 100644 --- a/lib/src/link/fetch_result.dart +++ b/lib/src/link/fetch_result.dart @@ -6,10 +6,10 @@ class FetchResult { this.context, }); - List errors; + List? errors; /// List or Map dynamic data; - Map extensions; - Map context; + Map? extensions; + Map? context; } \ No newline at end of file diff --git a/lib/src/link/http/http_config.dart b/lib/src/link/http/http_config.dart index 395343e..8bcda84 100644 --- a/lib/src/link/http/http_config.dart +++ b/lib/src/link/http/http_config.dart @@ -4,8 +4,8 @@ class HttpQueryOptions { this.includeExtensions, }); - bool includeQuery; - bool includeExtensions; + bool? includeQuery; + bool? includeExtensions; void addAll(HttpQueryOptions options) { if (options.includeQuery != null) { @@ -26,10 +26,10 @@ class HttpConfig { this.headers, }); - HttpQueryOptions http; - Map options; - Map credentials; - Map headers; + HttpQueryOptions? http; + Map? options; + Map? credentials; + Map? headers; } class HttpOptionsAndBody { @@ -38,6 +38,6 @@ class HttpOptionsAndBody { this.body, }); - final Map options; - final String body; + final Map? options; + final String? body; } \ No newline at end of file diff --git a/lib/src/link/http/link_http.dart b/lib/src/link/http/link_http.dart index 7749d3e..e35eb2a 100644 --- a/lib/src/link/http/link_http.dart +++ b/lib/src/link/http/link_http.dart @@ -1,29 +1,30 @@ import 'dart:async'; import 'dart:convert'; -import 'package:meta/meta.dart'; -import 'package:http/http.dart'; -import 'package:http_parser/http_parser.dart'; - -import 'package:flutter_graphql/src/link/link.dart'; -import 'package:flutter_graphql/src/link/operation.dart'; import 'package:flutter_graphql/src/link/fetch_result.dart'; -import 'package:flutter_graphql/src/link/http/http_config.dart'; import 'package:flutter_graphql/src/link/http/fallback_http_config.dart'; +import 'package:flutter_graphql/src/link/http/http_config.dart'; +import 'package:flutter_graphql/src/link/link.dart'; +import 'package:flutter_graphql/src/link/operation.dart'; +import 'package:http/http.dart'; +import 'package:http_parser/http_parser.dart'; class HttpLink extends Link { HttpLink({ - @required String uri, - bool includeExtensions, - Client fetch, - Map headers, - Map credentials, - Map fetchOptions, + required String uri, + bool? includeExtensions, + Client? fetch, + Map? headers, + Map? credentials, + Map? fetchOptions, }) : super( request: ( - Operation operation, [ - NextLink forward, + Operation? operation, [ + NextLink? forward, ]) { + if (operation == null) { + throw ArgumentError('flutter_graphQl: var operation is null'); + } final Client fetcher = fetch ?? Client(); final HttpConfig linkConfig = HttpConfig( @@ -35,8 +36,8 @@ class HttpLink extends Link { headers: headers, ); - final Map context = operation.getContext(); - HttpConfig contextConfig; + final Map? context = operation.getContext(); + late HttpConfig contextConfig; if (context != null) { // TODO: refactor context to use a [HttpConfig] object to avoid dynamic types @@ -46,22 +47,21 @@ class HttpLink extends Link { ), options: context['fetchOptions'], credentials: context['credentials'], - headers: context['headers'], + headers: (context['headers'] as Map).map((String key, dynamic value) => MapEntry(key, value ?? '')), ); } - final HttpOptionsAndBody httpOptionsAndBody = - _selectHttpOptionsAndBody( + final HttpOptionsAndBody httpOptionsAndBody = _selectHttpOptionsAndBody( operation, fallbackHttpConfig, linkConfig, contextConfig, ); - final Map options = httpOptionsAndBody.options; - final Map httpHeaders = options['headers']; + final Map options = httpOptionsAndBody.options!; + final Map? httpHeaders = options['headers']; - StreamController controller; + late StreamController controller; Future onListen() async { Response response; @@ -69,7 +69,7 @@ class HttpLink extends Link { try { // TODO: support multiple http methods response = await fetcher.post( - uri, + Uri.parse(uri), headers: httpHeaders, body: httpOptionsAndBody.body, ); @@ -97,10 +97,10 @@ class HttpLink extends Link { HttpOptionsAndBody _selectHttpOptionsAndBody( Operation operation, - HttpConfig fallbackConfig, [ + HttpConfig fallbackConfig, HttpConfig linkConfig, HttpConfig contextConfig, -]) { +) { final Map options = { 'headers': {}, 'credentials': {}, @@ -110,46 +110,48 @@ HttpOptionsAndBody _selectHttpOptionsAndBody( // http options // initialze with fallback http options - http.addAll(fallbackConfig.http); + http.addAll(fallbackConfig.http!); // inject the configured http options if (linkConfig.http != null) { - http.addAll(linkConfig.http); + http.addAll(linkConfig.http!); } // override with context http options if (contextConfig.http != null) { - http.addAll(contextConfig.http); + http.addAll(contextConfig.http!); } // options // initialze with fallback options - options.addAll(fallbackConfig.options); + options.addAll(fallbackConfig.options!); // inject the configured options if (linkConfig.options != null) { - options.addAll(linkConfig.options); + options.addAll(linkConfig.options!); } // override with context options if (contextConfig.options != null) { - options.addAll(contextConfig.options); + options.addAll(contextConfig.options!); } // headers // initialze with fallback headers - options['headers'].addAll(fallbackConfig.headers); + if (fallbackConfig.headers != null) { + (options['headers'] as Map).addAll(fallbackConfig.headers!); + } // inject the configured headers if (linkConfig.headers != null) { - options['headers'].addAll(linkConfig.headers); + (options['headers'] as Map).addAll(linkConfig.headers!); } // inject the context headers if (contextConfig.headers != null) { - options['headers'].addAll(contextConfig.headers); + (options['headers'] as Map).addAll(contextConfig.headers!); } // credentials @@ -174,11 +176,11 @@ HttpOptionsAndBody _selectHttpOptionsAndBody( }; // not sending the query (i.e persisted queries) - if (http.includeExtensions) { + if (http.includeExtensions!) { body['extensions'] = operation.extensions; } - if (http.includeQuery) { + if (http.includeQuery!) { body['query'] = operation.document; } @@ -190,7 +192,7 @@ HttpOptionsAndBody _selectHttpOptionsAndBody( FetchResult _parseResponse(Response response) { final int statusCode = response.statusCode; - final String reasonPhrase = response.reasonPhrase; + final String? reasonPhrase = response.reasonPhrase; if (statusCode < 200 || statusCode >= 400) { throw ClientException( @@ -220,22 +222,21 @@ FetchResult _parseResponse(Response response) { /// The default fallback encoding is set to UTF-8 according to the IETF RFC4627 standard /// which specifies the application/json media type: /// "JSON text SHALL be encoded in Unicode. The default encoding is UTF-8." -Encoding _determineEncodingFromResponse(Response response, - [Encoding fallback = utf8]) { - final String contentType = response.headers['content-type']; +Encoding _determineEncodingFromResponse(Response response, [Encoding fallback = utf8]) { + final String? contentType = response.headers['content-type']; if (contentType == null) { return fallback; } final MediaType mediaType = new MediaType.parse(contentType); - final String charset = mediaType.parameters['charset']; + final String? charset = mediaType.parameters['charset']; if (charset == null) { return fallback; } - final Encoding encoding = Encoding.getByName(charset); + final Encoding? encoding = Encoding.getByName(charset); return encoding == null ? fallback : encoding; } diff --git a/lib/src/link/link.dart b/lib/src/link/link.dart index 013828d..7b72ea8 100644 --- a/lib/src/link/link.dart +++ b/lib/src/link/link.dart @@ -8,22 +8,25 @@ typedef NextLink = Stream Function( ); typedef RequestHandler = Stream Function( - Operation operation, [ - NextLink forward, + Operation? operation, [ + NextLink? forward, ]); Link _concat( Link first, Link second, ) { - return Link(request: ( - Operation operation, [ - NextLink forward, - ]) { - return first.request(operation, (Operation op) { - return second.request(op, forward); - }); - }); + return Link( + request: first.request == null || second.request == null + ? null + : ( + Operation? operation, [ + NextLink? forward, + ]) { + return first.request!(operation, (Operation op) { + return second.request!(op, forward); + }); + }); } class Link { @@ -31,7 +34,7 @@ class Link { this.request, }); - final RequestHandler request; + final RequestHandler? request; Link concat(Link next) { return _concat(this, next); @@ -39,8 +42,8 @@ class Link { } Stream execute({ - Link link, - Operation operation, + required Link link, + Operation? operation, }) { - return link.request(operation); -} \ No newline at end of file + return link.request!(operation); +} diff --git a/lib/src/link/operation.dart b/lib/src/link/operation.dart index a3d5bb6..f3f74e8 100644 --- a/lib/src/link/operation.dart +++ b/lib/src/link/operation.dart @@ -8,10 +8,10 @@ class Operation { this.extensions, }); - final String document; - final Map variables; - final String operationName; - final Map extensions; + final String? document; + final Map? variables; + final String? operationName; + final Map? extensions; final Map _context = {}; diff --git a/lib/src/link/web_socket/link_web_socket.dart b/lib/src/link/web_socket/link_web_socket.dart index 29078f8..23db192 100644 --- a/lib/src/link/web_socket/link_web_socket.dart +++ b/lib/src/link/web_socket/link_web_socket.dart @@ -1,7 +1,7 @@ import 'package:flutter_graphql/src/link/link.dart'; class WebSocketLink extends Link { - RequestHandler subscriptionClient; + RequestHandler? subscriptionClient; // TODO: implement https://github.com/apollographql/apollo-link/blob/master/packages/apollo-link-ws/src/webSocketLink.ts } diff --git a/lib/src/scheduler/scheduler.dart b/lib/src/scheduler/scheduler.dart index 52028c2..7af0b83 100644 --- a/lib/src/scheduler/scheduler.dart +++ b/lib/src/scheduler/scheduler.dart @@ -9,7 +9,7 @@ class QueryScheduler { this.queryManager, }); - QueryManager queryManager; + QueryManager? queryManager; /// Map going from query ids to the [WatchQueryOptions] associated with those queries. Map registeredQueries = @@ -26,7 +26,7 @@ class QueryScheduler { Timer timer, Duration interval, ) { - intervalQueries[interval].retainWhere( + intervalQueries[interval]!.retainWhere( (String queryId) { // If ObservableQuery can't be found from registeredQueries or if it has a // different interval, it means that this queryId is no longer registered @@ -41,7 +41,7 @@ class QueryScheduler { } final Duration pollInterval = - Duration(seconds: registeredQueries[queryId].pollInterval); + Duration(seconds: registeredQueries[queryId]!.pollInterval!); return registeredQueries.containsKey(queryId) && pollInterval == interval; @@ -49,7 +49,7 @@ class QueryScheduler { ); // if no queries on the interval clean up - if (intervalQueries[interval].isEmpty) { + if (intervalQueries[interval]!.isEmpty) { intervalQueries.remove(interval); _pollingTimers.remove(interval); timer.cancel(); @@ -57,9 +57,9 @@ class QueryScheduler { } // fetch each query on the interval - for (String queryId in intervalQueries[interval]) { - final WatchQueryOptions options = registeredQueries[queryId]; - queryManager.fetchQuery(queryId, options); + for (String queryId in intervalQueries[interval]!) { + final WatchQueryOptions options = registeredQueries[queryId]!; + queryManager!.fetchQuery(queryId, options); } } @@ -70,11 +70,11 @@ class QueryScheduler { registeredQueries[queryId] = options; final Duration interval = Duration( - seconds: options.pollInterval, + seconds: options.pollInterval!, ); if (intervalQueries.containsKey(interval)) { - intervalQueries[interval].add(queryId); + intervalQueries[interval]!.add(queryId); } else { intervalQueries[interval] = [queryId]; diff --git a/lib/src/socket_client.dart b/lib/src/socket_client.dart index 4052b7c..f16efa2 100644 --- a/lib/src/socket_client.dart +++ b/lib/src/socket_client.dart @@ -1,16 +1,15 @@ import 'dart:async'; import 'dart:io'; -import 'package:uuid/uuid.dart'; - import 'package:flutter_graphql/flutter_graphql.dart'; +import 'package:uuid/uuid.dart'; -SocketClient socketClient; +late SocketClient socketClient; class SocketClient { final Uuid _uuid = Uuid(); final GraphQLSocket _socket; - static Map _initPayload; + static Map? _initPayload; SocketClient(this._socket) { _socket.connectionAck.listen(print); @@ -27,7 +26,7 @@ class SocketClient { final Map headers = const { 'content-type': 'application/json', }, - final Map initPayload, + final Map? initPayload, }) async { _initPayload = initPayload; diff --git a/lib/src/utilities/get_from_ast.dart b/lib/src/utilities/get_from_ast.dart index 7374316..784c3e6 100644 --- a/lib/src/utilities/get_from_ast.dart +++ b/lib/src/utilities/get_from_ast.dart @@ -1,6 +1,7 @@ -import 'package:graphql_parser/graphql_parser.dart'; +import 'package:collection/collection.dart' show IterableExtension; +import 'package:graphql_parser2/graphql_parser2.dart'; -String getOperationName(String rawDoc) { +String? getOperationName(String rawDoc) { final List tokens = scan(rawDoc); final Parser parser = Parser(tokens); @@ -13,10 +14,9 @@ String getOperationName(String rawDoc) { final DocumentContext doc = parser.parseDocument(); if (doc.definitions != null && doc.definitions.isNotEmpty) { - final OperationDefinitionContext definition = doc.definitions.lastWhere( + final OperationDefinitionContext? definition = doc.definitions.lastWhereOrNull( (DefinitionContext context) => context is OperationDefinitionContext, - orElse: () => null, - ); + ) as OperationDefinitionContext?; if (definition != null) { if (definition.name != null) { diff --git a/lib/src/utilities/helpers.dart b/lib/src/utilities/helpers.dart index eb86f99..68ee204 100644 --- a/lib/src/utilities/helpers.dart +++ b/lib/src/utilities/helpers.dart @@ -1,3 +1,3 @@ -bool notNull(Object any) { +bool notNull(Object? any) { return any != null; } \ No newline at end of file diff --git a/lib/src/utilities/traverse.dart b/lib/src/utilities/traverse.dart index d106b80..b4f797a 100644 --- a/lib/src/utilities/traverse.dart +++ b/lib/src/utilities/traverse.dart @@ -1,11 +1,11 @@ -typedef Object Transform(Object node); +typedef Object? Transform(Object? node); -Map traverseValues( - Map node, +Map traverseValues( + Map node, Transform transform, ) { - return node.map( - (String key, Object value) => MapEntry( + return node.map( + (String key, Object? value) => MapEntry( key, traverse(value, transform), ), @@ -14,19 +14,16 @@ Map traverseValues( // Attempts to apply the transform to every leaf of the data structure recursively. // Stops recursing when a node is transformed (returns non-null) -Object traverse(Object node, Transform transform) { - final Object transformed = transform(node); +Object? traverse(Object? node, Transform transform) { + final Object? transformed = transform(node); if (transformed != null) { return transformed; } - - if (node is List) { - return node - .map((Object node) => traverse(node, transform)) - .toList(); + if (node is List) { + return node.map((dynamic node) => traverse(node, transform)).toList(); } - if (node is Map) { - return traverseValues(node, transform); + if (node is Map) { + return traverseValues(node as Map, transform); } return node; } diff --git a/lib/src/websocket/messages.dart b/lib/src/websocket/messages.dart index 5afbbd8..a16ee6f 100644 --- a/lib/src/websocket/messages.dart +++ b/lib/src/websocket/messages.dart @@ -50,7 +50,7 @@ abstract class GraphQLSocketMessage extends JsonSerializable { class InitOperation extends GraphQLSocketMessage { InitOperation(this.payload) : super(MessageTypes.GQL_CONNECTION_INIT); - final Map payload; + final Map? payload; @override dynamic toJson() { diff --git a/lib/src/websocket/socket.dart b/lib/src/websocket/socket.dart index 72013bf..dad6cee 100644 --- a/lib/src/websocket/socket.dart +++ b/lib/src/websocket/socket.dart @@ -9,10 +9,10 @@ import '../../flutter_graphql.dart'; class GraphQLSocket { GraphQLSocket(this._socket) { _socket - .map>((dynamic message) => json.decode(message)) + .map?>((dynamic message) => json.decode(message)) .listen( - (Map message) { - final String type = message['type'] ?? 'unknown'; + (Map? message) { + final String type = message!['type'] ?? 'unknown'; final dynamic payload = message['payload'] ?? {}; final String id = message['id'] ?? 'none'; diff --git a/lib/src/widgets/cache_provider.dart b/lib/src/widgets/cache_provider.dart index 0af0e52..abee266 100644 --- a/lib/src/widgets/cache_provider.dart +++ b/lib/src/widgets/cache_provider.dart @@ -5,8 +5,8 @@ import 'package:flutter_graphql/src/widgets/graphql_provider.dart'; class CacheProvider extends StatefulWidget { const CacheProvider({ - final Key key, - @required this.child, + final Key? key, + required this.child, }) : super(key: key); final Widget child; @@ -17,13 +17,13 @@ class CacheProvider extends StatefulWidget { class _CacheProviderState extends State with WidgetsBindingObserver { - GraphQLClient client; + GraphQLClient? client; @override void initState() { super.initState(); - WidgetsBinding.instance.addObserver(this); + WidgetsBinding.instance!.addObserver(this); } @override @@ -32,7 +32,7 @@ class _CacheProviderState extends State client = GraphQLProvider.of(context).value; assert(client != null); - client.cache?.restore(); + client!.cache.restore(); super.didChangeDependencies(); } @@ -41,7 +41,7 @@ class _CacheProviderState extends State void dispose() { super.dispose(); - WidgetsBinding.instance.removeObserver(this); + WidgetsBinding.instance!.removeObserver(this); } @override @@ -50,18 +50,18 @@ class _CacheProviderState extends State switch (state) { case AppLifecycleState.inactive: - client.cache?.save(); + client!.cache.save(); break; case AppLifecycleState.paused: - client.cache?.save(); + client!.cache.save(); break; - case AppLifecycleState.suspending: + case AppLifecycleState.detached: break; case AppLifecycleState.resumed: - client.cache?.restore(); + client!.cache.restore(); break; } } diff --git a/lib/src/widgets/graphql_consumer.dart b/lib/src/widgets/graphql_consumer.dart index 49c8f17..0747eea 100644 --- a/lib/src/widgets/graphql_consumer.dart +++ b/lib/src/widgets/graphql_consumer.dart @@ -3,21 +3,21 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_graphql/src/graphql_client.dart'; import 'package:flutter_graphql/src/widgets/graphql_provider.dart'; -typedef Widget GraphQLConsumerBuilder(GraphQLClient client); +typedef Widget GraphQLConsumerBuilder(GraphQLClient? client); class GraphQLConsumer extends StatelessWidget { const GraphQLConsumer({ - final Key key, - @required this.builder, + final Key? key, + required this.builder, this.client, }) : super(key: key); final GraphQLConsumerBuilder builder; - final GraphQLClient client; + final GraphQLClient? client; @override Widget build(BuildContext context) { - GraphQLClient tmpClient; + GraphQLClient? tmpClient; if (client != null) tmpClient = client; else diff --git a/lib/src/widgets/graphql_provider.dart b/lib/src/widgets/graphql_provider.dart index 3233c05..80bb143 100644 --- a/lib/src/widgets/graphql_provider.dart +++ b/lib/src/widgets/graphql_provider.dart @@ -4,17 +4,18 @@ import 'package:flutter_graphql/src/graphql_client.dart'; class GraphQLProvider extends StatefulWidget { const GraphQLProvider({ - Key key, + Key? key, this.client, this.child, }) : super(key: key); - final ValueNotifier client; - final Widget child; + final ValueNotifier? client; + final Widget? child; static ValueNotifier of(BuildContext context) { - final _InheritedGraphQLProvider inheritedGraphqlProvider = - context.inheritFromWidgetOfExactType(_InheritedGraphQLProvider); + final _InheritedGraphQLProvider inheritedGraphqlProvider = context + .getElementForInheritedWidgetOfExactType<_InheritedGraphQLProvider>()! + .widget as _InheritedGraphQLProvider; return inheritedGraphqlProvider.client; } @@ -30,7 +31,7 @@ class _GraphQLProviderState extends State { void initState() { super.initState(); - widget.client.addListener(didValueChange); + widget.client!.addListener(didValueChange); } @override @@ -43,16 +44,16 @@ class _GraphQLProviderState extends State { @override Widget build(BuildContext context) { return _InheritedGraphQLProvider( - client: widget.client, - child: widget.child, + client: widget.client!, + child: widget.child!, ); } } class _InheritedGraphQLProvider extends InheritedWidget { _InheritedGraphQLProvider({ - this.client, - Widget child, + required this.client, + required Widget child, }) : clientValue = client.value, super(child: child); diff --git a/lib/src/widgets/mutation.dart b/lib/src/widgets/mutation.dart index 9130dcb..05c6ff3 100644 --- a/lib/src/widgets/mutation.dart +++ b/lib/src/widgets/mutation.dart @@ -1,67 +1,57 @@ import 'package:flutter/widgets.dart'; - -import 'package:flutter_graphql/src/graphql_client.dart'; +import 'package:flutter_graphql/src/cache/cache.dart'; import 'package:flutter_graphql/src/core/observable_query.dart'; import 'package:flutter_graphql/src/core/query_options.dart'; import 'package:flutter_graphql/src/core/query_result.dart'; -import 'package:flutter_graphql/src/cache/cache.dart'; +import 'package:flutter_graphql/src/graphql_client.dart'; import 'package:flutter_graphql/src/utilities/helpers.dart'; - import 'package:flutter_graphql/src/widgets/graphql_provider.dart'; typedef RunMutation = void Function(Map variables); typedef MutationBuilder = Widget Function( RunMutation runMutation, - QueryResult result, + QueryResult? result, ); -typedef void OnMutationCompleted(QueryResult result); -typedef void OnMutationUpdate(Cache cache, QueryResult result); +typedef void OnMutationCompleted(QueryResult? result); +typedef void OnMutationUpdate(Cache cache, QueryResult? result); /// Builds a [Mutation] widget based on the a given set of [MutationOptions] /// that streams [QueryResult]s into the [QueryBuilder]. class Mutation extends StatefulWidget { const Mutation({ - final Key key, - @required this.options, - @required this.builder, + final Key? key, + required this.options, + required this.builder, this.onCompleted, this.update, }) : super(key: key); final MutationOptions options; final MutationBuilder builder; - final OnMutationCompleted onCompleted; - final OnMutationUpdate update; + final OnMutationCompleted? onCompleted; + final OnMutationUpdate? update; @override MutationState createState() => MutationState(); } class MutationState extends State { - GraphQLClient client; - ObservableQuery observableQuery; - - WatchQueryOptions get _options => WatchQueryOptions( - document: widget.options.document, - variables: widget.options.variables, - fetchPolicy: widget.options.fetchPolicy, - errorPolicy: widget.options.errorPolicy, - fetchResults: false, - context: widget.options.context, - client: widget.options.client - ); + GraphQLClient? client; + ObservableQuery? observableQuery; + + WatchQueryOptions get _options => WatchQueryOptions(document: widget.options.document, variables: widget.options.variables, fetchPolicy: widget.options.fetchPolicy, errorPolicy: widget.options.errorPolicy, fetchResults: false, context: widget.options.context, client: widget.options.client); // TODO is it possible to extract shared logic into mixin void _initQuery() { - if (_options.client != null) - client =_options.client; + if (_options.client != null) + client = _options.client; else client = GraphQLProvider.of(context).value; assert(client != null); observableQuery?.close(); - observableQuery = client.watchQuery(_options); + observableQuery = client!.watchQuery(_options); } @override @@ -75,17 +65,17 @@ class MutationState extends State { super.didUpdateWidget(oldWidget); // TODO @micimize - investigate why/if this was causing issues - if (!observableQuery.options.areEqualTo(_options)) { + if (!observableQuery!.options.areEqualTo(_options)) { _initQuery(); } } - OnData get update { + OnData? get update { // fallback client in case widget has been disposed of - final Cache cache = client.cache; + final Cache cache = client!.cache; if (widget.update != null) { - void updateOnData(QueryResult result) { - widget.update(client?.cache ?? cache, result); + void updateOnData(QueryResult? result) { + widget.update!(client?.cache ?? cache, result); } return updateOnData; @@ -93,15 +83,23 @@ class MutationState extends State { return null; } - Iterable get callbacks { - return [widget.onCompleted, update].where(notNull); + Iterable get callbacks { + return [ + widget.onCompleted, + update + ].where(notNull); } - void runMutation(Map variables) => observableQuery - ..setVariables(variables) - ..onData(callbacks) // add callbacks to observable - ..sendLoading() - ..fetchResults(); + void runMutation(Map variables) { + if (observableQuery == null) { + return; + } + observableQuery! + ..setVariables(variables) + ..onData(callbacks) // add callbacks to observable + ..sendLoading() + ..fetchResults(); + } @override void dispose() { @@ -111,14 +109,14 @@ class MutationState extends State { @override Widget build(BuildContext context) { - return StreamBuilder( + return StreamBuilder( initialData: QueryResult( loading: false, ), stream: observableQuery?.stream, builder: ( BuildContext buildContext, - AsyncSnapshot snapshot, + AsyncSnapshot snapshot, ) { return widget.builder( runMutation, @@ -127,4 +125,4 @@ class MutationState extends State { }, ); } -} \ No newline at end of file +} diff --git a/lib/src/widgets/query.dart b/lib/src/widgets/query.dart index 42f6d21..48b4583 100644 --- a/lib/src/widgets/query.dart +++ b/lib/src/widgets/query.dart @@ -7,15 +7,15 @@ import 'package:flutter_graphql/src/core/query_result.dart'; import 'package:flutter_graphql/src/widgets/graphql_provider.dart'; -typedef QueryBuilder = Widget Function(QueryResult result); +typedef QueryBuilder = Widget Function(QueryResult? result); /// Builds a [Query] widget based on the a given set of [QueryOptions] /// that streams [QueryResult]s into the [QueryBuilder]. class Query extends StatefulWidget { const Query({ - final Key key, - @required this.options, - @required this.builder, + final Key? key, + required this.options, + required this.builder, }) : super(key: key); final QueryOptions options; @@ -26,38 +26,29 @@ class Query extends StatefulWidget { } class QueryState extends State { - ObservableQuery observableQuery; + ObservableQuery? observableQuery; WatchQueryOptions get _options { - FetchPolicy fetchPolicy = widget.options.fetchPolicy; + FetchPolicy? fetchPolicy = widget.options.fetchPolicy; if (fetchPolicy == FetchPolicy.cacheFirst) { fetchPolicy = FetchPolicy.cacheAndNetwork; } - return WatchQueryOptions( - document: widget.options.document, - variables: widget.options.variables, - fetchPolicy: fetchPolicy, - errorPolicy: widget.options.errorPolicy, - pollInterval: widget.options.pollInterval, - fetchResults: true, - context: widget.options.context, - client: widget.options.client - ); + return WatchQueryOptions(document: widget.options.document, variables: widget.options.variables, fetchPolicy: fetchPolicy, errorPolicy: widget.options.errorPolicy, pollInterval: widget.options.pollInterval, fetchResults: true, context: widget.options.context, client: widget.options.client); } void _initQuery() { - GraphQLClient client; + GraphQLClient? client; - if (_options.client != null) - client =_options.client; + if (_options.client != null) + client = _options.client; else client = GraphQLProvider.of(context).value; assert(client != null); observableQuery?.close(); - observableQuery = client.watchQuery(_options); + observableQuery = client!.watchQuery(_options); } @override @@ -71,7 +62,7 @@ class QueryState extends State { super.didUpdateWidget(oldWidget); // TODO @micimize - investigate why/if this was causing issues - if (!observableQuery.options.areEqualTo(_options)) { + if (!observableQuery!.options.areEqualTo(_options)) { _initQuery(); } } @@ -84,17 +75,17 @@ class QueryState extends State { @override Widget build(BuildContext context) { - return StreamBuilder( + return StreamBuilder( initialData: QueryResult( loading: true, ), - stream: observableQuery.stream, + stream: observableQuery!.stream, builder: ( BuildContext buildContext, - AsyncSnapshot snapshot, + AsyncSnapshot snapshot, ) { - return widget?.builder(snapshot.data); + return widget.builder(snapshot.data); }, ); } -} \ No newline at end of file +} diff --git a/lib/src/widgets/subscription.dart b/lib/src/widgets/subscription.dart index 3a03122..1379403 100644 --- a/lib/src/widgets/subscription.dart +++ b/lib/src/widgets/subscription.dart @@ -8,9 +8,9 @@ import '../websocket/messages.dart'; typedef OnSubscriptionCompleted = void Function(); typedef SubscriptionBuilder = Widget Function({ - final bool loading, - final dynamic payload, - final dynamic error, + bool? loading, + dynamic payload, + dynamic error, }); class Subscription extends StatefulWidget { @@ -18,8 +18,8 @@ class Subscription extends StatefulWidget { this.operationName, this.query, { this.variables = const {}, - final Key key, - @required this.builder, + final Key? key, + required this.builder, this.initial, this.onCompleted, }) : super(key: key); @@ -28,7 +28,7 @@ class Subscription extends StatefulWidget { final String query; final dynamic variables; final SubscriptionBuilder builder; - final OnSubscriptionCompleted onCompleted; + final OnSubscriptionCompleted? onCompleted; final dynamic initial; @override @@ -89,7 +89,7 @@ class _SubscriptionState extends State { void _onDone() { if (widget.onCompleted != null) { - widget.onCompleted(); + widget.onCompleted!(); } } @@ -101,4 +101,4 @@ class _SubscriptionState extends State { payload: _data, ); } -} \ No newline at end of file +} diff --git a/pubspec.yaml b/pubspec.yaml index f763570..4aef066 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,25 +1,21 @@ name: flutter_graphql description: A GraphQL client for Flutter, bringing all the features from a modern GraphQL client to one easy to use package. -version: 1.0.0-rc.3 -authors: - - Rex Raphael +version: 1.1.4-dev.5 homepage: https://github.com/snowballdigital/graphql-flutter/tree/master dependencies: flutter: sdk: flutter - meta: ^1.1.6 - http: ^0.12.0 - http_parser: ^3.1.3 - path_provider: ^0.5.0+1 - uuid: ^2.0.0 - sqflite: ^1.1.0 - graphql_parser: ^1.1.1 + http: ^0.13.4 + http_parser: ^4.0.0 + path_provider: ^2.0.5 + uuid: ^3.0.5 + sqflite: ^2.0.0+4 + graphql_parser2: ^2.1.0 dev_dependencies: flutter_test: sdk: flutter - test: ^1.3.0 environment: - sdk: ">=2.0.0 <3.0.0" + sdk: '>=2.12.0 <3.0.0' diff --git a/test/normalized_in_memory_test.dart b/test/normalized_in_memory_test.dart index 6a81f4c..77c4b56 100644 --- a/test/normalized_in_memory_test.dart +++ b/test/normalized_in_memory_test.dart @@ -1,5 +1,5 @@ -import 'package:test/test.dart'; import 'package:flutter_graphql/src/cache/normalized_in_memory.dart'; +import 'package:flutter_test/flutter_test.dart'; List reference(String key) { return ['cache/reference', key];