Migrating to the new CoinMarketCap API with Flutter

 

 Many of us just beginning our Flutter journey will have come across several tutorials for creating a simple cryptocurrency application. These are great! Except many are outdated and use a public API that has since been deprecated.

This is a great tutorial, but it needs a sequel.

I recommend you download a free software called Postman. It is excellent for debugging APIs and understanding the errors that are returned.


Migrating The API

If you followed any of these tutorials, you likely called the API in an async function like this.

String _apiURL =
    "https://api.coinmarketcap.com/v1/ticker/";
http.Response response = await http.get(_apiURL);

However, you’ll quickly find that this API call no longer works. Why is this? If we enter the URL into the browser we will see that it returns a 410 error with a message.

WARNING: This API is now offline. Please switch to the new CoinMarketCap API. (https://pro.coinmarketcap.com/migrate/)

That sounds easy enough. But keep in mind, this API is no longer public, and will require an API key to be able to access them. Many of the endpoints are available from a free account, so you won’t have to pay.

Create an account on CoinMarketCap and have a look through the documentation. It looks like the API endpoint we would like to call is https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest.

So, let’s call the new API.

String _apiURL =
    "https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest";
http.Response response = await http.get(_apiURL);

It returns a 1002 error.

API key is missing

So let’s call it while adding our API key into the header of the get request.

String _apiURL =
    "https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest";
http.Response response = await http.get(_apiURL,
    headers: {"X-CMC_PRO_API_KEY": "192f6ffc-306a-495f-8110-ebd51b64b52d"}); // Replace this key with your own

It returns a 400 error.

\’value\’ must contain at least one of [id, symbol, slug]

So let’s specify what we want to be returned. In this case, I’ll request the first 15 id’s.

String _apiURL =
  "https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?id=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15";
http.Response response = await http.get(_apiURL,
    headers: {"X-CMC_PRO_API_KEY": "192f6ffc-306a-495f-8110-ebd51b64b52d"}); // Replace this key with your own

Working With The New API

Now the API should be getting called successfully! But your application may still not be working correctly.

InternalLinkedHashMap<String, dynamic> is not subtype of type List<dynamic>

I found that the new API returns JSON in a slightly different format to the old one, and so caused the above error when attempting to save the response to a List. Let’s have a look at the response.

 

 

The two top-level values are “status” and “data”. If we add the response to a list like before, we will save these two Maps, and not the 15 Maps of cryptocurrency data as we would expect.

This was resolved by creating a for loop which iterated through each “id” in “data” and saved its contents to a List in the state.

List cryptoDatas = []; // Create an empty list of Cryptocurrencies
Map<String, dynamic> responseJSON = json.decode(response.body);
if (responseJSON["status"]["error_code"] == 0) {
    for (int i = 1; i <= responseJSON["data"].length; i++) {
        cryptoDatas.add(responseJSON["data"][i.toString()]); // Add Map to List
    }
}
setState(() {
    this._cryptoList = cryptoDatas; // Set the state with the new list
});

We’re nearly there! But you may still have problems. The JSON formatting is different compared to the old API so you may be looking for certain values in the wrong place.

When debugging the application I was receiving an error that stated I was “calling .length on a null object”. This was a lie and it sent me on a wild goose chase.

The actual issue I encountered was when I was attempting to save the price value from the JSON as a double in the application. Previously it looked like this.

String cryptoPrice(Map crypto) {
    int decimals = 2;
    int fac = pow(10, decimals);
    double d = double.parse(crypto['price_usd']);
    return "\$" + (d = (d * fac).round() / fac).toString();
}

To understand the problem we should look at how the JSON formats the price response.

 

 price is no longer directly under the id like “id/price_usd”. It is further down the tree. “id/quote/USD/price”. You will also notice that “price” is not returning a String representation of a number, but a Double. Therefore we no longer need to parse the result.

String cryptoPrice(Map crypto) {
    int decimals = 2;
    int fac = pow(10, decimals);
    double d = (crypto['quote']['USD']['price']);
    return "\$" + (d = (d * fac).round() / fac).toString();
}

Congratulations! You should now have migrated to the new API and once again be correctly receiving and displaying the results.

 

 

Final Thoughts

Creating this application and overcoming these API problems was fulfilling. But this application was developed in a single file.

Main.dart

My next steps are to learn more about Flutter application architecture so that I don’t fall into the trap of creating messy and un-maintainable applications. I recommend you do the same.

You can find the project on my GitHub. It will continually be updated as I learn more about Flutter.

https://github.com/CharlievWilliams/BlockchainMarkets

posted @ 2020-07-15 18:52  PeterLin88  阅读(572)  评论(0)    收藏  举报