Greycastle Logo

Generating Flutter package badges

Generating Flutter package badges

I tend to automate the most basic tasks because it’s more fun than copy-pasting. If I need to do something more than ~5x I’m like to write a script for it.

This time I was writing a blog post comparing some Flutter packages and had to generate badges for each package. I wanted to show stars, rating, popularity and such and found that copy-pasting this for each single package was tedious.

In the end, combined with writing this article about it I didn’t save any time for sure 😂 but it was a lot more fun. Also, I learned about CORS proxies (see bottom).

This is so lightweight I won’t even link to something outside but just drop it here. It uses jquery and a few lines of code to call the dart.pub API. Here it is (or check the jsFiddle):

function setOutput(html) {
  $('#code-output').text(html);
  $('#rendered-ouput').html(html);
}

function renderOutput(packageName, githubName) {
  const html = `
    <a href="https://github.com/${githubName}" target="_blank">
      <img decoding="async" src="https://img.shields.io/github/stars/${githubName}.svg">
    </a><br>
    <a href="https://pub.dev/packages/${packageName}/score" target="_blank">
      <img decoding="async" src="https://badges.bar/${packageName}/likes">
    </a><br>
    <a href="https://pub.dev/packages/${packageName}/score" target="_blank">
      <img decoding="async" src="https://badges.bar/${packageName}/popularity">
    </a><br>
    <a href="https://pub.dev/packages/${packageName}/score" target="_blank">
      <img decoding="async" src="https://badges.bar/${packageName}/pub%20points">
    </a>
  `.trim();
  setOutput(html);
}

function renderError(error) {
  setOutput(error.toString());
}

function run() {
  const packageName = $('#pubdev')[0].value;
  const url = `https://pub.dev/api/packages/${packageName}`;
  const proxyUrl = "https://api.allorigins.win/get?url=" + encodeURIComponent(url);

  $.ajax({
    url: proxyUrl,
    method: "GET",
    success: function(response) {
      const data = JSON.parse(response.contents);
      if (data.error) {
        renderError(data.error.message);
        return;
      }
      const github = data.latest.pubspec.homepage;
      const githubName = github.replace('https://github.com/', '').replace(//$/, '');
      renderOutput(packageName, githubName);
    },
    error: function(err) {
      renderError(err);
    }
  });
}

Input

pub.dev package name

Render it!

Html

Rendered

The code

Explaining it

As mentioned, building this I found that pub.dev has an open API! You can GET any package data by calling https://pub.dev/api/packages/, check out the get_it package for example.

Secondly, calling this API from inside a page of my own, such as this, I hit a CORS issue. That is, the pub.dev server isn’t sending back the headers required to tell my browser that this source is intended for my browser. What Chrome then does is that it simply shuts it down. It looks like this:

Showing a Chrome developer console error log for missing CORS headers

Getting a CORS error requesting 3rd party URL

It turns out many kind people out there has provided us with free solutions for simply proxying our request and adding the headers required!

One such service is https://allorigins.win/. How do you use it? Simply call their service and pass your URL as a query parameter:

const actualUrl = `https://pub.dev/api/packages/${packageName}`
const proxyUrl = "https://api.allorigins.win/get?url=" + encodeURIComponent(actualUrl)

You can then inspect the request and see that the allorigins proxy has added the headers we need.

Image showing CORS headers returned on a Chrome request to allorigins.win proxy

CORS headers added by allorigins.win proxy service

Not faster but more fun

Using these tiny jsFiddle’s, a proxy service, open API endpoints and a bit of jQuery it’s really fast to script away boring repetitive tasks.

Really, I need to learn how to focus at the task in front of me instead of throwing myself over the next shiny thing 🙂 see you next time!

© 2024 Greycastle