graphql_flutter_henry_fork 0.8.1

  • README.md
  • CHANGELOG.md
  • Example
  • Installing
  • Versions
  • 10

GraphQL Flutter

version MIT License All Contributors PRs Welcome

Watch on GitHub Star on GitHub

Table of Contents

About this project

GraphQL brings many benefits, both to the client: devices will need less requests, and therefore reduce data useage. And to the programer: requests are arguable, they have the same structure as the request.

The team at Apollo did a great job implenting GraphQL in Swift, Java and Javascript. But unfortunately they're not planning to release a Dart implementation.

This project is filling the gap, bringing the GraphQL spec to yet another programming language. We plan to implement most functionality from the Apollo GraphQL client and from most features the React Apollo components into Dart and Flutter respectively.

With that being said, the project lives currently still inside one package. We plan to spilt up the project into multiple smaler packages in the near future, to follow Apollo's modules design.

Installation

First depend on the library by adding this to your packages pubspec.yaml:

dependencies:
  graphql_flutter_henry_fork: ^0.8.1

Now inside your Dart code you can import it.

import 'package:graphql_flutter_henry_fork/graphql_flutter_henry_fork.dart';

Usage

To use the client it first needs to be initialized with an endpoint and cache. If your endpoint requires authentication you can provide it to the client contructor. If you need to change the api token at a later stage, you can call the setter apiToken on the Client class.

For this example we will use the public GitHub API.

...

import 'package:graphql_flutter_henry_fork/graphql_flutter_henry_fork.dart';

void main() {
  ValueNotifier<Client> client = ValueNotifier(
    Client(
      endPoint: 'https://api.github.com/graphql',
      cache: InMemoryCache(),
      apiToken: '<YOUR_GITHUB_PERSONAL_ACCESS_TOKEN>',
    ),
  );

  ...
}

...

Graphql Provider

In order to use the client, you app needs to be wrapped with the GraphqlProvider widget.

  ...

  return GraphqlProvider(
    client: client,
    child: MaterialApp(
      title: 'Flutter Demo',
      ...
    ),
  );

  ...

Queries

Creating a query is as simple as creating a multiline string:

String readRepositories = """
  query ReadRepositories(\$nRepositories) {
    viewer {
      repositories(last: \$nRepositories) {
        nodes {
          id
          name
          viewerHasStarred
        }
      }
    }
  }
"""
    .replaceAll('\n', ' ');

In your widget:

...

Query(
  readRepositories, // this is the query you just created
  variables: {
    'nRepositories': 50,
  },
  pollInterval: 10, // optional
  builder: ({
    bool loading,
    var data,
    String error,
  }) {
    if (error != '') {
      return Text(error);
    }

    if (loading) {
      return Text('Loading');
    }

    // it can be either Map or List
    List repositories = data['viewer']['repositories']['nodes'];

    return ListView.builder(
      itemCount: repositories.length,
      itemBuilder: (context, index) {
        final repository = repositories[index];

        return Text(repository['name']);
    });
  },
);

...

Mutations

Again first create a mutation string:

String addStar = """
  mutation AddStar(\$starrableId: ID!) {
    addStar(input: {starrableId: \$starrableId}) {
      starrable {
        viewerHasStarred
      }
    }
  }
"""
    .replaceAll('\n', ' ');

The syntax for mutations is fairly similar to that of a query. The only diffence is that the first argument of the builder function is a mutation function. Just call it to trigger the mutations (Yeah we deliberately stole this from react-apollo.)

...

Mutation(
  addStar,
  builder: (
    runMutation, { // you can name it whatever you like
    bool loading,
    var data,
    String error,
}) {
  return FloatingActionButton(
    onPressed: () => runMutation({
      'starrableId': <A_STARTABLE_REPOSITORY_ID>,
    }),
    tooltip: 'Star',
    child: Icon(Icons.star),
  );
},
  onCompleted: (Map<String, dynamic> data) {
    showDialog(
    context: context,
    builder: (BuildContext context) {
      return AlertDialog(
        title: Text('Thanks for your star!'),
        actions: <Widget>[
          SimpleDialogOption(
            child: Text('Dismiss'),
            onPressed: () {
              Navigator.of(context).pop();
            },
          )
        ],
      );
    }
  );
}),

...

Subscriptions (Experimental)

The syntax for subscriptions is again similar to a query, however, this utilizes WebSockets and dart Streams to provide real-time updates from a server. Before subscriptions can be performed a global intance of socketClient needs to be initialized.

We are working on moving this into the same GraphqlProvider stucture as the http client. Therefore this api might change in the near future.

socketClient = await SocketClient.connect('ws://coolserver.com/graphql');

Once the socketClient has been initialized it can be used by the Subscription Widget

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Subscription(
          operationName,
          query,
          variables: variables,
          builder: ({
            bool loading,
            dynamic payload,
            dynamic error,
          }) {
            if (payload != null) {
              return Text(payload['requestSubscription']['requestData']);
            } else {
              return Text('Data not found');
            }
          }
        ),
      )
    );
  }
}

Once the socketClient is initialized you could also use it without Flutter.

final String operationName = "SubscriptionQuery";
final String query = """subscription $operationName(\$requestId: String!) {
  requestSubscription(requestId: \$requestId) {
    requestData
  }
}""";
final dynamic variables = {
  'requestId': 'My Request',
};
socketClient
    .subscribe(SubscriptionRequest(operationName, query, variables))
    .listen(print);

Graphql Consumer

You can always access the client direcly from the GraphqlProvider but to make it even easier you can also use the GraphqlConsumer widget.

  ...

  return GraphqlConsumer(
    builder: (Client client) {
      // do something with the client

      return Container(
        child: Text('Hello world'),
      );
    },
  );

  ...

Offline Cache (Experimental)

The in-memory cache can automatically be saved to and restored from offline storage. Setting it up is as easy as wrapping your app with the CacheProvider widget.

Make sure the CacheProvider widget is inside the GraphqlProvider widget.

...

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GraphqlProvider(
      client: client,
      child: CacheProvider(
        child: MaterialApp(
          title: 'Flutter Demo',
          ...
        ),
      ),
    );
  }
}

...

Roadmap

This is currently our roadmap, please feel free to request additions/changes.

FeatureProgress
Queries
Mutations
Subscriptions
Query polling
In memory cache
Offline cache sync
Optimistic results🔜
Client state management🔜
Modularity🔜

Contributing

Feel free to open a PR with any suggestions! We'll be actively working on the library ourselves.

Contributors

This package was originally created and published by the engineers at Zino App B.V.. Since then the community has helped to make it even more useful for even more developers.

Thanks goes to these wonderful people (emoji key):

<img src="https://avatars2.githubusercontent.com/u/4757453?v=4" width="100px;"/><br /><sub><b>Eustatiu Dima</b></sub><br />🐛 💻 📖 💡 🤔 👀<img src="https://avatars3.githubusercontent.com/u/17142193?v=4" width="100px;"/><br /><sub><b>Zino Hofmann</b></sub><br />🐛 💻 📖 💡 🤔 🚇 👀<img src="https://avatars2.githubusercontent.com/u/15068096?v=4" width="100px;"/><br /><sub><b>Harkirat Saluja</b></sub><br />📖 🤔<img src="https://avatars3.githubusercontent.com/u/5178217?v=4" width="100px;"/><br /><sub><b>Chris Muthig</b></sub><br />💻 📖 💡 🤔<img src="https://avatars1.githubusercontent.com/u/7611406?v=4" width="100px;"/><br /><sub><b>Cal Pratt</b></sub><br />🐛 💻 📖 💡 🤔<img src="https://avatars0.githubusercontent.com/u/9830761?v=4" width="100px;"/><br /><sub><b>Miroslav Valkovic-Madjer</b></sub><br />💻<img src="https://avatars2.githubusercontent.com/u/4523129?v=4" width="100px;"/><br /><sub><b>Aleksandar Faraj</b></sub><br />🐛

This project follows the all-contributors specification. Contributions of any kind are welcome!

[0.8.0] - August 10 2018

Breaking change

n/a

Fixes / Enhancements

  • Added basic error handeling for queries and mutations @mmadjer
  • Added missing export for the GraphqlConsumer widget @AleksandarFaraj

Docs

n/a

[0.7.1] - August 3 2018

Breaking change

n/a

Fixes / Enhancements

  • Code formatting

Docs

  • Updated the package description

[0.7.0] - July 22 2018

Breaking change

n/a

Fixes / Enhancements

  • Added support for subsciptionsin the client.
  • Added the Subscription widget. You can no direcly acces streams from Flutter.

Docs

  • Added instructions for adding subscripton to your poject.
  • Updated the About this project section.

[0.6.0] - July 19 2018

Breaking change

  • The library now requires your app to be wrapped with the GraphqlProvider widget.
  • The global client variable is no longer available. Instead use the GraphqlConsumer widget.

Fixes / Enhancements

  • Added the GraphqlProvider widget. The client is now stored in an InheritedWidget, and can be accessed anywhere within the app.
Client client = GraphqlProvider.of(context).value;
  • Added the GraphqlConsumer widget. For ease of use we added a widget that uses the same builder structure as the Query and Mutation widgets.

Under the hood it access the client from the BuildContext.

  • Added the option to optionally provide the apiToken to the Client constructor. It is still possible to set the apiToken with setter method.
  return new GraphqlConsumer(
    builder: (Client client) {
      // do something with the client

      return new Container();
    },
  );

Docs

  • Added documentation for the new GraphqlProvider
  • Added documentation for the new GraphqlConsumer
  • Changed the setup instructions to include the new widgets
  • Changed the example to include the new widgets

[0.5.4] - July 17 2018

Breaking change

n/a

Fixes / Enhancements

  • Query: changed Timer to Timer.periodic @eusdima
  • Minor logic tweak @eusdima
  • Use absolute paths in the library

Docs

  • Fix mutations example bug not updating star bool @cal-pratt

[0.5.3] - July 13 2018

Breaking change

n/a

Fixes / Enhancements

  • Added polling timer as a variable for easy deletion on dispose
  • Fixed bug when Query timer is still active when the Query is disposed
  • Added instant query fetch when the query variables are updated

Docs

n/a

[0.5.2] - July 11 2018

Breaking change

n/a

Fixes / Enhancements

  • Fixed error when cache file is non-existent

Docs

n/a

[0.5.1] - June 29 2018

Breaking change

n/a

Fixes / Enhancements

  • Fixed json error parsing.

Docs

n/a

[0.5.0] - June 25 2018

Breaking change

n/a

Fixes / Enhancements

  • Introduced onCompleted callback for mutiations.
  • Excluded some config files from version control.

Docs

  • Fixed typos in the readme.md.
  • The examples inculde an example of the onCompleted callback.

[0.4.1] - June 22 2018

Breaking change

n/a

Fixes / Enhancements

n/a

Docs

  • The examples now porperly reflect the changes to the library.

[0.4.0] - June 21 2018

Breaking change

  • The Client now requires a from of cache.
  • The name of the execute method on the Client class changed to query.

Fixes / Enhancements

  • Implemented in-memory cache.
  • Write memory to file when in background.
  • Added provider widget to save and restore the in-memory cache.
  • Restructure the project.

Docs

  • Update the README.md to refelct changes in the code.
  • update the example to refelct changes in the code.

[0.3.0] - June 16 2018

Breaking change

  • Changed data type to Map instaid of Object to be more explicit.

Fixes / Enhancements

  • Cosmatic changes.

Docs

  • Added a Flutter app example.
  • Fixed the example in README.md.
  • Added more badges.

[0.2.0] - June 15 2018

Breaking change

  • Changed query widget polling argument to pollInterval, following the react-apollo api.

Fixes / Enhancements

  • Query polling is now optional.

Docs

  • Updated the docs with the changes in api.

[0.1.0] - June 15 2018

My colleague and I created a simple implementation of a GraphQL Client for Flutter. (Many thanks to Eus Dima, for his work on the initial client.)

Breaking change

n/a

Fixes / Enhancements

  • A client to connect to your GraphQL server.
  • A query widget to handle GraphQL queries.
  • A mutation widget to handle GraphQL mutations.
  • Simple support for query polling.

Docs

  • Initial documentation.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:graphql_flutter_henry_fork/graphql_flutter_henry_fork.dart';

import './mutations/addStar.dart' as mutations;
import './queries/readRepositories.dart' as queries;

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    ValueNotifier<Client> client = ValueNotifier(
      Client(
        endPoint: 'https://api.github.com/graphql',
        cache: InMemoryCache(),
        apiToken: '<YOUR_GITHUB_PERSONAL_ACCESS_TOKEN>',
      ),
    );

    return GraphqlProvider(
      client: client,
      child: CacheProvider(
        child: MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(title: 'Flutter Demo Home Page'),
        ),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({
    Key key,
    this.title,
  }) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Query(
        queries.readRepositories,
        pollInterval: 1,
        builder: ({
          bool loading,
          Map data,
          Exception error,
        }) {
          if (error != null) {
            return Text(error.toString());
          }

          if (loading) {
            return Text('Loading');
          }

          // it can be either Map or List
          List repositories = data['viewer']['repositories']['nodes'];

          return ListView.builder(
            itemCount: repositories.length,
            itemBuilder: (context, index) {
              final repository = repositories[index];

              return Mutation(
                mutations.addStar,
                builder: (
                  addStar, {
                  bool loading,
                  Map data,
                  Exception error,
                }) {
                  if (data.isNotEmpty) {
                    repository['viewerHasStarred'] =
                        data['addStar']['starrable']['viewerHasStarred'];
                  }

                  return ListTile(
                    leading: repository['viewerHasStarred']
                        ? const Icon(Icons.star, color: Colors.amber)
                        : const Icon(Icons.star_border),
                    title: Text(repository['name']),
                    // NOTE: optimistic ui updates are not implemented yet, therefore changes may take upto 1 second to show.
                    onTap: () {
                      addStar({
                        'starrableId': repository['id'],
                      });
                    },
                  );
                },
                onCompleted: (Map<String, dynamic> data) {
                  showDialog(
                    context: context,
                    builder: (BuildContext context) {
                      return AlertDialog(
                        title: Text('Thanks for your star!'),
                        actions: <Widget>[
                          SimpleDialogOption(
                            child: Text('Dismiss'),
                            onPressed: () {
                              Navigator.of(context).pop();
                            },
                          )
                        ],
                      );
                    },
                  );
                },
              );
            },
          );
        },
      ),
    );
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  graphql_flutter_henry_fork: ^0.8.1

2. Install it

You can install packages from the command line:

with pub:


$ pub get

with Flutter:


$ flutter packages get

Alternatively, your editor might support pub get or flutter packages get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:graphql_flutter_henry_fork/graphql_flutter_henry_fork.dart';
  
Version Uploaded Documentation Archive
0.8.1 Aug 14, 2018 Go to the documentation of graphql_flutter_henry_fork 0.8.1 Download graphql_flutter_henry_fork 0.8.1 archive
0.8.0 Aug 14, 2018 Go to the documentation of graphql_flutter_henry_fork 0.8.0 Download graphql_flutter_henry_fork 0.8.0 archive
0.7.0 Aug 1, 2018 Go to the documentation of graphql_flutter_henry_fork 0.7.0 Download graphql_flutter_henry_fork 0.7.0 archive
Popularity:
Describes how popular the package is relative to other packages. [more]
0
Health:
Code health derived from static analysis. [more]
0
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
50
Overall:
Weighted score of the above. [more]
10
Learn more about scoring.

We analyzed this package on Dec 10, 2018, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.1.0
  • pana: 0.12.7
  • Flutter: 1.0.0

Platforms

Detected platforms:

Error(s) prevent platform classification:

Fix dependencies in pubspec.yaml.

Analysis issues and suggestions

Fix dependencies in pubspec.yaml.

Running flutter packages pub upgrade failed with the following output:

ERR: The current Dart SDK version is 2.1.0-dev.9.4.flutter-f9ebf21297.
 
 Because graphql_flutter_henry_fork depends on test >=0.12.0-beta.3 <1.3.0 which requires SDK version >=1.8.0 <2.0.0-∞, version solving failed.

Maintenance issues and suggestions

Fix platform conflicts. (-20 points)

Error(s) prevent platform classification:

Fix dependencies in pubspec.yaml.

Running dartdoc failed. (-10 points)

Make sure dartdoc runs without any issues.

The description is too short. (-20 points)

Add more detail about the package, what it does and what is its target use case. Try to write at least 60 characters.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.0.0-dev.52.0 <3.0.0