In a previous article, I mentioned how to get better at coding, I like to do side projects to practice using frameworks different from those I use at work.This is one of those scenarios. While working on a mobile app that uses some data from Contentful, I noticed a lack of documentation on how to get started. In this post, I will go over how to setup Contentful on a simple cross-platform app using the Flutter framework.
Contentful
Data Model
The first step is to sign-up for a contentful account and create a space. Once the space is setup, we create a content model and populate it with some dummy data.
Refer to the documentation for help with the platform.
Data Model
The next thing we need to do on the contentful platform is to take note of the API keys. Under the Settings tab we click on Api keys and then select the available Token Item from the list, or we click the +Add API key button to generate one.
The fields we need are Space ID and Content Delivery API — access token.
Flutter
Dependency Setup
To set up Flutter, refer to the official documentation. Google has comprehensive guides on everything related to Flutter. Once that’s done, the first thing we need to do is add a few dependencies to our Flutter project. We will do so by adding the following entries to the pubspec.yaml file:
dependencies: flutter: sdk: flutter json_annotation: 4.3.0 contentful: 3.0.5 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 dev_dependencies: flutter_test: sdk: flutter build_runner: 2.1.4 json_serializable: 6.0.0
JSON serialization
Next we will create a new dart class to hold our contentful model. The structure of the model will reflect the response object from the Contentful api.
import 'package:contentful/contentful.dart'; import 'package:json_annotation/json_annotation.dart'; part 'product.g.dart'; () class ProductContent extends Entry<Product> { ProductContent({ required SystemFields sys, required Product fields, }) : super(sys: sys, fields: fields); static ProductContent fromJson(Map<String, dynamic> json) { return _$ProductContentFromJson(json); } Map<String, dynamic> toJson() => _$ProductContentToJson(this); } () class Product { final String name; final String description; final int price; Product({this.name = '', this.description = '', this.price = 0}); static Product fromJson(Map<String, dynamic> json) { return _$ProductFromJson(json); } Map<String, dynamic> toJson() => _$ProductToJson(this); }
Notice how this file contains annotations from the json_annotation dependency we imported earlier. This will allow us to serialise the response model we receive from the contentful api into a strongly typed dart object that can be used in our code. To read more about json serialisation in Flutter, refer to the following article.
To generate the product.g.dart file we need to execute the following command:
flutter packages pub run build_runner build
Contentful client
For the sake of this article, we will use a simple repository dart class to configure the HTTP client that will be used to connect to the Contentful API:
import 'package:contentful/client.dart'; import 'package:flutter_application_1/product.dart'; class Repository { Client contentful = Client( BearerTokenHTTPClient( '<access token>', ), spaceId: '<space id>', ); Future<List<ProductContent>> getProducts() async { final collection = await contentful.getEntries<ProductContent>( {'content_type': 'product'}, ProductContent.fromJson, ); return collection.items; } }
Our repository class uses the Client and BearerTokenHTTPClient classes from the Contentful library to instantiate the client object. For now, we use the client to retrieve the entries of content_type:product
, which we created earlier, from the api.
Widget
Now that we have the model and repository setup, we can move on to the widget to display that data. To achieve this, we will use the FutureBuilder widget from the flutter library.
In the main.dart file, we will wrap the generated Scaffold widget with a FutureBuilder and pass it our getProducts. Inside the Scaffold we will replace the existing Column widget to display the result as follows:
// ... Widget build(BuildContext context) { return FutureBuilder<List<ProductContent>>( future: Repository().getProducts(), builder: (context, snapshot) { var products = (snapshot.data != null) ? snapshot.data! : []; return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: products .map<Widget>( (product) => Text( 'Product: ' + product.fields!.name + ' Description: ' + product.fields!.description + ' Price: ' + product.fields!.price.toString(), ), ) .toList(), ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: const Icon(Icons.add), ), ); }); } // ...
Result
The last step is to run the app by executing the command:
flutter run
and the result should be as follows:
Conclusion
Flutter is a great framework for making cross-platform apps. There is a lot of documentation on how to get started and build apps relatively quickly.
Unfortunately, when I tried to load some data from Contentful for my project, I had a bit of trouble with it at first, and the official documentation wasn’t enough.
That is why in this article I briefly went over how to configure a contentful space with some dummy data, how to install the required dependencies on a flutter app, and how to code a simple repository to retrieve the dummy data and display it on the flutter app.