23 Jun 2021 by Jakub Neander

GraphQL in Flutter - Building a Mobile Shop using Saleor API

This tutorial will show you how to create a simple mobile app in Flutter on top of the Saleor API that exchanges the data using GraphQL. Along with some basic styling. Our goal is to show you how to create a mobile basis of an e-commerce app in Flutter.

Introduction

GraphQL is a query language and server-side runtime for APIs. It is able to provide a complete and convenient description of the data in your API. The query language allows clients to form the data requests for the exact shape of the data they need. GraphQL is designed to make APIs flexible and convenient to use so that they are easier to evolve and maintain. GraphQL helps you keep up with the growing complexity of apps.

GraphQL has many advantages compared to REST. Instead of using a fixed data structure approach, GraphQL requests specific data the client’s needs. REST responses are notorious for containing too much data or not enough. GraphQL solves this by fetching exact data in a single request. GraphQL also has an introspection feature that allows developers to check types & the schema to ensure they’re asking for data the right way.

Saleor is an all-in-one platform for creating a production-grade shopping experience on the Internet. Saleor exposes a comprehensive set of e-commerce notions as a GraphQL endpoint. Building digital shops used to be complex. Nowdays you can rely on specialist solutions such as Saleor providing consolidated e-commerce knowledge that exposes it in an easy-to-use manner. Saleor allows you to manage any number of storefronts such as web or mobile apps from a single back-end.

In this tutorial, we will build a mobile app on top of the Saleor GraphQL API using the Flutter framework. Flutter is an open-source user interface toolkit created by Google. It’s become an increasingly popular tool because it’s a cross-platform solution. Flutter allows you to use one programming language and one code base to target different platforms such iOS, Android, Desktop and the browser at once. This means you no longer need to learn different languages to target specific platforms with your apps.


Prerequisites

Before you begin this guide, you’ll need the following:


Getting Started

Let’s start by creating a Flutter application inside VS Code. Make sure you have Flutter and Dart installed along with their VS Code extensions. Then, press CTRL + SHIFT + P to open the VS Code command palette. Use the command Flutter: New Application Project. This will give you a basic boilerplate to work with.

Name it whatever you would like and you should be good to go.

Run the phone simulator and start the app to make sure it works as expected. On MacOS you can use the simulator that comes with Xcode. For other operating systems, refer to the Flutter documentation for details. The boilerplate comes with a simple counting app. It should look something like this.

text

Installing the Libraries & Dependencies

First, we’re going to add graphql_flutter to our project. This is a GraphQL client for Flutter that brings all the features from a modern GraphQL client to one easy to use package. It also combines the benefits of GraphQL with the benefits of Streams in Dart to deliver a high-performance client.

Go inside the pubspec.yaml file at the base of the project. Find the line cupertino_icons. Right below it, add the following.

graphql_flutter:

Save the file. Your project will automatically find the latest version. You can make sure it did so by going into pubspec.lock file and finding the graphql_flutter version. Currently, 5.0.0 is the latest stable version.

Removing the Boilerplate

Now that everything is set up, we can remove the increment counter and some comments in our project. Your main.dart file should look something like this.

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required 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: Center(),
    );
  }
}

We can now start getting our data from the Saleor GraphQL endpoint.


Your First Query

At the top of the main.dart file under the import statement, define the productsGraphQL variable that will hold the following GraphQL query as a string:

query products {
  products(first: 10) {
    edges {
      node {
        id
        name
        description
        thumbnail {
          url
        }
      }
    }
  }
}

Typically, you would want to have separate files for each query. But for the sake of this tutorial, we’re going to keep everything inside our main.dart file.

Next, we’re going to write an integration and create the GraphQL client. We’re also going to wrap our client inside a value notifier.

void main() {
  final HttpLink httpLink = HttpLink("https://demo.saleor.io/graphql/");

  ValueNotifier<GraphQLClient> client = ValueNotifier(
    GraphQLClient(
      link: httpLink,
      cache: GraphQLCache(store: InMemoryStore()),
    ),
  );

  runApp(MyApp());
}

Our client must be then provided to the application widget tree using the GraphQLProvider class. Again, this isn’t the way you’d want to write code for production. But it will be enough for this tutorial.

void main() {
  final HttpLink httpLink = HttpLink("https://demo.saleor.io/graphql/");

  ValueNotifier<GraphQLClient> client = ValueNotifier(
    GraphQLClient(
      link: httpLink,
      cache: GraphQLCache(store: InMemoryStore()),
    ),
  );

  var app = GraphQLProvider(client: client, child: MyApp());
  runApp(app);
}

If you run this app, it will be completely empty. You have to explicitly specify the data in the query response that we want to display. Go into the _MyHomePageState class at the bottom of the file and use the Query widget as the child in the position of the body: parameter.

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Query(
        options: QueryOptions(
          document: gpl(productsGraphQL),
        ),
        builder: (QueryResult result, {fetchMore, refetch}) {
          if (result.hasException) {
            return Text(result.exception.toString());
          }

          if (result.isLoading) {
            return Center(
              child: CircularProgressIndicator(),
            );
          }

          final productList = result.data?['products']['edges'];
          print(productList);

          return Text("Something");
        },
      ),
    );
  }
}

In the Query widget, we’re going to use two parameters: options and builder. For options, we’re going to use QueryOptions which will use the variable holding the GraphQL query as string that we created earlier, i.e. productsGraphQL, wrapped inside a helper function.

For the builder, we will have QueryResult. We will create a check to see if there was an exception or not. We’ll also include an if-statement to display a circular progress indicator while the data fetching is in progress.

If our data load properly, we’re going to extract the proper fields. And for now, we’ll simply print the data to our debug console.

You should see the word “something” displayed in your app and the products from the query inside your debug console.

Now that we know we can fetch data properly, we can start displaying those products in our app. We’re also going to be adding some simple styling to keep our app looking fresh. Our products will be arranged in a 2-column grid. We will use the GridView widget for that. Its itemBuilder gets each product that we fetched using our GraphQL query within the Query widget and then we extract the product image and name to display it.

Edit the return statement to display the products.

return Column(
  children: [
    Padding(
      padding: EdgeInsets.all(16.0),
      child: Text(
        "Products",
        style: Theme.of(context).textTheme.headline5,
      ),
    ),
    Expanded(
      child: GridView.builder(
        itemCount: productList.length,
        gridDelegate: SilverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2,
          mainAxisSpacing: 2.0,
          crossAxisSpacing: 2.0,
          childAspectRatio: 0.75,
        ), // SilverGridDelegateWithFixedCrossAxisCount
        itemBuilder: (_, index) {
          var product = productList[index]['node'];
          return Column(
            children: [
              Container(
                padding: EdgeInsets.all(2.0),
                width: 180,
                height: 180,
                child: Image.network(product['thumbnail']['url']),
              ),
              Padding(
                padding: EdgeInsets.symmetric(vertical: 8.0),
                child: Text(
                  product['name'],
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
              ),
              Text("\$4.50")
            ],
          );
        }
      )
    )
  ],
);

Also, keep in mind we are hard-coding the price because we didn’t include that in our query. All our items in our shop will have the same price as a result.

And with that, your app should be displaying the products. It should look something like this.

text

You should see a centered title, all the products, and the hard-coded price for each of them.

And that’s going to be it. You did it!


Next Steps

As an exercise, try to modify the GraphQL query to include the real price. You can start from the GraphQL playground. Use the docs tab to explore possible parameters of a query. The query we use is called products. Find it and identify which fields are needed to get the price of each product as the response.


Conclusion

In this tutorial, we created a simple mobile application using the Flutter framework. We integrated with a GraphQL endpoint using the graphql_flutter package. Finally, we used the Saleor API as the GraphQL endpoint with a query for fetching a list of products. We finished off by displaying the results to the user.

If you had any problems with finishing this tutorial, let us know on GitHub Discussions or Twitter. We’d be happy to help!

The code is available on our GitHub.


Read more:

— Share