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 623 forks source link

GraphQLProvider should not be hardcoded in GraphQl widgets: Query, CacheProvider... #640

Closed sokolej79 closed 2 years ago

sokolej79 commented 4 years ago

GraphQlProvider should be optional for queries and should not be hard coded in CacheProvider and Query widget like this code: client = GraphQLProvider.of(context).value;. Now you have dependencies with GraphQlProvider and cant be replaced with ordinary Provider or other provider libraries and state management.

Describe alternatives you've considered Every developer should choose how to provide Client to query and other widgets. GraphqlWidget should be compatible with provider packages like hooks or Provider.

One solution would be to use bloc, redux add only graphql client with services, but we don't want to use other state management and boilerplate code for queries and subscriptions. Only Provider or hooks or built in GraphQlProvider with queries and subscriptions and for mutations it is acceptable to use client with services without mutation widget. GrapqhQl doesn't need to use state management like bloc, redux. Library should provide local state management: https://www.apollographql.com/docs/react/data/local-state/

sokolej79 commented 4 years ago

I searched closed tickets. I will try this solutions: https://github.com/zino-app/graphql-flutter/issues/339 https://github.com/FilledStacks/flutter-tutorials/issues/11

fogelfish commented 4 years ago

@sokolej79 did you succeed in crafting a GraphQL service free of widget dependencies? I'm relatively new at flutter and dart and so far have been unable to derive a solution from the references you mentioned.

sokolej79 commented 4 years ago

@sokolej79 did you succeed in crafting a GraphQL service free of widget dependencies? I'm relatively new at flutter and dart and so far have been unable to derive a solution from the references you mentioned.

I just use my own widgets for query and mutation with graphql service, which is ordinary service class for read query, mutation and subscription.

fogelfish commented 4 years ago

I succeeded after a lot of guidance from the flutter_bloc documentation and one particular example in the github repo.

I currently have a GraphQLClient service (I named it hasuraApiClient) with methods performQuery and performMutation; classes FetchGraphQLData and MutateGraphQLData that extend GraphQLEvents (that extends Equatable); a class GraphQLStates that extends Equatable to handle loading, success, and failure states; and GraphQLBloc to handle the FetchGraphQLData and MutateGraphQLData events. I hope to refactor this to be more compact, but at least I got over the hump.

One way I used this is shown here:

  Widget build(BuildContext context) {
    return Navigator(
      initialRoute: 'questions',
      onGenerateRoute: (RouteSettings settings) {
        WidgetBuilder builder;
        switch (settings.name) {
          case 'questions':
            builder = (BuildContext _) => BlocProvider<GraphQLBloc>(
                  create: (BuildContext context) => GraphQLBloc(
                    hasuraApiClient: hasuraApiClient,
                  )..add(
                      FetchGraphQLData(
                          query: queries.getUserUnansweredQuestions,
                          variables: <String, dynamic>{
                            'user_id': user.uid,
                          }),
                    ),
                  child: QuestionsWidget(
                    token: token,
                    user: user,
                  ),
                  lazy: false,
                );
            break;
. . .

I would be happy to share the whole thing. I'm a beginning Flutter developer but maybe my solution will be a help to others.

prakash-indorkar commented 3 years ago

I succeeded after a lot of guidance from the flutter_bloc documentation and one particular example in the github repo.

I currently have a GraphQLClient service (I named it hasuraApiClient) with methods performQuery and performMutation; classes FetchGraphQLData and MutateGraphQLData that extend GraphQLEvents (that extends Equatable); a class GraphQLStates that extends Equatable to handle loading, success, and failure states; and GraphQLBloc to handle the FetchGraphQLData and MutateGraphQLData events. I hope to refactor this to be more compact, but at least I got over the hump.

One way I used this is shown here:

  Widget build(BuildContext context) {
    return Navigator(
      initialRoute: 'questions',
      onGenerateRoute: (RouteSettings settings) {
        WidgetBuilder builder;
        switch (settings.name) {
          case 'questions':
            builder = (BuildContext _) => BlocProvider<GraphQLBloc>(
                  create: (BuildContext context) => GraphQLBloc(
                    hasuraApiClient: hasuraApiClient,
                  )..add(
                      FetchGraphQLData(
                          query: queries.getUserUnansweredQuestions,
                          variables: <String, dynamic>{
                            'user_id': user.uid,
                          }),
                    ),
                  child: QuestionsWidget(
                    token: token,
                    user: user,
                  ),
                  lazy: false,
                );
            break;
. . .

I would be happy to share the whole thing. I'm a beginning Flutter developer but maybe my solution will be a help to others.

Hi @fogelfish would it be possible to share the code please .. I'm really struggling to make it work. Thanks

fogelfish commented 3 years ago

@prakash-indorkar, I created a gist called working example of flutter_bloc, equatable, graphql_flutter (now revised to work with graphql: ^4.0.1 and graphql_flutter: ^4.0.1 and dev dependency gql_code_gen: ^0.1.5).

There is a lot of context I do not include in the gist that is either above or below the layers of the widget tree where graphQL is configured and controlled.

I bolted this graphQL functionality into other fully-developed authentication code written by Andrea Bizzotto in his Flutter & Firebase Course: Build a Complete App for iOS & Android, with further assistance from his repo firebase_auth_demo_flutter.

The only piece from higher up the widget tree that I'll show in this post is the part of the main AppState build method where Auth is provided and invoked. (I've substituted an ellipsis where a number of other providers are added.)

    return FutureBuilder(
      future: _initializeFlutterFireFuture,
      builder: (context, snapshot) {
        switch (snapshot.connectionState) {
          case ConnectionState.done:
            if (snapshot.hasError) {
              return Center(
                child: Text('Error: ${snapshot.error}',
                    textDirection: TextDirection.ltr),
              );
            }
            return MultiProvider(
              providers: [
                Provider<AppleSignInAvailable>.value(
                    value: widget.appleSignInAvailable),
                Provider<AuthService>(
                  create: (_) => AuthServiceAdapter(
                    initialAuthServiceType: widget.initialAuthServiceType,
                  ),
                  dispose: (_, AuthService authService) =>
                      authService.dispose(),
                ),
                ... 
              ],
              child: AuthWidgetBuilder(builder: (BuildContext context,
                  AsyncSnapshot<MyAppUser> userSnapshot) {
                return MultiBlocProvider(
                  providers: [
                    BlocProvider<ThemeBloc>(
                      create: (BuildContext context) => ThemeBloc(themeState),
                    ),
                  ],
                  child: MaterialApp(
                    theme: ThemeData(primarySwatch: Colors.indigo),
                    home: EmailLinkErrorPresenter.create(
                      context,
                      child: AuthWidget(userSnapshot: userSnapshot),
                    ),
                  ),
                );
              }),
            );
            break;
          default:
            return Center(
                child: Text('Loading', textDirection: TextDirection.ltr));
        }
      },
    );

My gist contains the complete AuthWidgetBuilder and AuthWidget classes and all GraphQL-specific classes, plus one example of a query. It does not include auth UI or app code, JWT functionality, or presentation UI of questions and answers. (This little app presents rainbow-colored Likert-scale questions retrieved from a Heroku Hasura GraphQL backend and updates the DB with users' answers.)

Experienced devs may see better ways to do what I've done but I was unable to find any tutorials that laid out sample code showing how to integrate flutter_bloc, equatable, and graphql_flutter. Hope this helps somebody.