zino-hofmann / graphql-flutter

A GraphQL client for Flutter, bringing all the features from a modern GraphQL client to one easy to use package.
https://zino-hofmann.github.io/graphql-flutter
MIT License
3.25k stars 620 forks source link

_InternalLinkedHashMap<dynamic, dynamic> is not a subtype of Map<String, dynamic>? in type cast #1091

Closed moehmeni closed 2 years ago

moehmeni commented 2 years ago

This is the error when I try to run client.query with a GraphQLConsumer for onChanged event of a TextField

Unhandled Exception: type '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>?' in type cast

seems there is an issue during encoding/decoding the API result into the Hive box (it's weird it was working as expected yesterday while I did not change the code and today it has issues)

Expected behavior Get the result from client.query and update the view (using a Getx controller since Query was not working as expected)

device / execution context Android simulator

The event function:

  void onInputChange(String value, GraphQLClient client, SearchController _) {
    if (value.isEmpty) {
      _.changeQuery("");
      return;
    }
    client
        .query(
      QueryOptions(document: gql(searchArtistQuery), variables: {
        'q': value,
      }),
    )
        .then((QueryResult result) {
      if (result.data == null) return;
      final artists = (result.data?['searchArtists'] ?? [])
          .map((artist) => Artist.fromJson(artist))
          .toList();
      if (artists.isEmpty) return;
      _.changeQuery(value);
      _.changeResult(artists);
    });
  }
Stacktrace: ```dart E/flutter (23039): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: type '_InternalLinkedHashMap' is not a subtype of type 'Map?' in type cast E/flutter (23039): #0 BoxImpl.get package:hive/…/box/box_impl.dart:44 E/flutter (23039): #1 HiveStore.get package:graphql/…/cache/hive_store.dart:51 E/flutter (23039): #2 GraphQLCache.readNormalized package:graphql/…/cache/cache.dart:114 E/flutter (23039): #3 NormalizingDataProxy.readQuery. package:graphql/…/cache/_normalizing_data_proxy.dart:87 E/flutter (23039): #4 denormalizeOperation package:normalize/src/denormalize_operation.dart:63 E/flutter (23039): #5 NormalizingDataProxy.readQuery package:graphql/…/cache/_normalizing_data_proxy.dart:85 E/flutter (23039): #6 QueryManager.maybeRebroadcastQueries package:graphql/…/core/query_manager.dart:473 E/flutter (23039): #7 QueryManager.query package:graphql/…/core/query_manager.dart:164 E/flutter (23039): E/flutter (23039): #8 GraphQLClient.query package:graphql/src/graphql_client.dart:152 E/flutter (23039): E/flutter (23039): ```
budde377 commented 2 years ago

@mdmomeni Thanks for reporting this! Do you mind sharing how you're initializing the client? Specifically, how are you providing the Hive box to the client.

moehmeni commented 2 years ago

My main function:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize Firebase
  await FCM.init();

  // Initialize Hive (from GraphQL package)
  await initHiveForFlutter();

  // Initialize UserController Hive box
  await UserController.init(); // await Hive.openBox<String>("user");

  // GraphQL
  final HttpLink httpLink = HttpLink(
    "http://10.0.2.2:8000/graphql",
  );

  final AuthLink authLink = AuthLink(
    getToken: () => UserController.token,
  );

  final Link link = authLink.concat(httpLink);

  ValueNotifier<GraphQLClient> client = ValueNotifier(
    GraphQLClient(
      link: link,
      cache: GraphQLCache(store: HiveStore()),
    ),
  );
  runApp(MyApp(client));
}

Also MyApp:

class MyApp extends StatelessWidget {
  const MyApp(this.client, {Key? key}) : super(key: key);
  final ValueNotifier<GraphQLClient> client;

  @override
  Widget build(BuildContext context) {
    return GraphQLProvider(
      client: client,
      child: GetMaterialApp(
...

Client provider:

GetBuilder<SearchController>(
              init: SearchController(),
              builder: (_) {
                return GraphQLConsumer(builder: (client) {
                  return Expanded(
                      child: Column(children: [
                    searchField(client, _),
                    const SizedBox(height: 10),
                    Expanded(
                        child: Stack(
                      alignment: Alignment.center,
                      children: [mainBody(_), submitBtn()],
                    ))
                  ]));
                });
              }),
...

  Widget searchField(GraphQLClient client, SearchController _) {
    return Padding(
      padding: const EdgeInsets.all(10),
      child: TextField(
        textDirection: TextDirection.ltr,
        decoration: CInputDecoration(hintText: "جستجو"),
        onChanged: (q) => onInputChange(q, client, _),
      ),
    );
  }
budde377 commented 2 years ago

Cheers @mdmomeni! I've created a PR with a fix. If you're able to verify that it's working for you, that'd be a huge help!

moehmeni commented 2 years ago

Thank you, but how can I replace the updated code? and should I review the PR or something after testing it?

budde377 commented 2 years ago

@mdmomeni

You should be able to add the branch as a dependency directly. I.e., update your pubspec.yaml file with

  graphql_flutter:
    git:
      url: git://github.com/zino-hofmann/graphql-flutter.git
      ref: fix-hive
      path: packages/graphql_flutter

I'm not sure if you can add a review, but a comment on the PR (or here) will be fine.

Adherentman commented 2 years ago

@mdmomeni

You should be able to add the branch as a dependency directly. I.e., update your pubspec.yaml file with

  graphql_flutter:
    git:
      url: git://github.com/zino-hofmann/graphql-flutter.git
      ref: fix-hive
      path: packages/graphql_flutter

I'm not sure if you can add a review, but a comment on the PR (or here) will be fine.

@budde377 The exception still exists. When I set fetchPolicy to FetchPolicy.networkOnly, this problem does not exist. But I set it to FetchPolicy.cacheAndNetwork, and this exception occurred. I guess that the data type stored in hive does not match the Map.

This is my data structure:

[
  {
    _id: 6141e5e09fa3f5afd79e6ecb,
    type: urlLaunch,
    content:
      {
        image: image1.png,
        url: https://example1.com,
      },
    __typename: Recommend,
  },
  {
    _id: 6141e8029fa3f5afd79e6ecc,
    type: urlLaunch,
    content:
      {
        image: image.png,
        url: https://example.com,
      },
    __typename: Recommend,
  },
]

I use fast_immutable_collections and freezed

@freezed
class Recommend with _$Recommend {
  factory Recommend({
    required String type,
    required IMap<String, String> content,
  }) = _Recommend;
  factory Recommend.fromJson(Map<String, dynamic> json) =>
      _$RecommendFromJson(json);
}

void exampleQuery () {
  final result =
      await client.query$Recommend(Options$Query$Recommend(
    operationName: 'exampleQuery',
    fetchPolicy: FetchPolicy.cacheAndNetwork,
    variables: Variables$Query$Recommend(
      memberId: memberId,
    ),
  ));

  if (result.hasException) {
    handleException(result);
    throw Exception(result.exception.toString());
  }
    final recommendList = IList.fromJson(
     ///  🔨 If I want to not report this exception, I need to do the following
     /// FIX: should work code json.decode(json.encode(result.data?["exampleQuery"]))
      result.data?["exampleQuery"], 

      (dynamic item) => Recommend.fromJson(item),
    );
  return recommendList;
}
budde377 commented 2 years ago

Thanks for reporting. Please check if #1167 fixes this for you! @Adherentman

Adherentman commented 2 years ago

Thanks for reporting. Please check if #1167 fixes this for you! @Adherentman

@budde377 I checked the relevant code and it should solve the problem。 But I have a problem trying to use the latest code in pubspec.yaml.

graphql:
    git:
      url: https://github.com/zino-hofmann/graphql-flutter.git
      ref: main
      path: packages/graphql
  graphql_flutter:
    git:
      url: https://github.com/zino-hofmann/graphql-flutter.git
      ref: main
      path: packages/graphql_flutter

get error:

Because every version of graphql_flutter from git depends on graphql from hosted and example_app depends on graphql from git, graphql_flutter from git is forbidden.
So, because example_app depends on graphql_flutter from git, version solving failed.
pub get failed (1; So, because example_app depends on graphql_flutter from git, version solving failed.)

Or when will it be published on pub.dev

vincenzopalazzo commented 2 years ago

this is your error, you should put also inside the override package the graphql, this tell to pub "Hey you are using git, but there is some depenciences in common, so ignore it and use git".

It is strange, but the dependieces resolution world is more strange.

BTW I'm releasing the new beta release later in the day

Adherentman commented 2 years ago

I have used main branch to verify that it runs very well. Thank you.

vincenzopalazzo commented 2 years ago

@Adherentman the new beta is out