Creating Animation Curves in Flutter

In this blog post we will take a look at how we can create our own custom Curve in Flutter. And as an example we will create two curves: a Sine and Spring curve.

A curve in Flutter can be any mapping of a function over a time period (t) from 0.0 to 1.0.

Easing curves are used to adjust the rate of change of an animation over time, allowing them to speed up and slow down, rather than moving at a constant rate.

A curve must map t=0.0 to 0.0 and t=1.0 to 1.0.

So to illustrate this we need to define some function that takes a time t and outputs some value, however, we have to respect the conditions that when t=0.0 then the function should output 0.0, and when t=1.0 the function should output 1.0.

As an example, let’s say we have a normal linear function:

f(t) = mt+c

This is a straight line, with constants m and c, and the function is defined by input t.

Let’s make this function even more boring, and define a value of 0 for c and 1 for m. Now we have:

f(t) = 1t +0
     = t

Straight Line

We just defined the default animation curve in Flutter: Curves.linear.

Borrrinnngg.

Sine Curve

Let’s create something more interesting and create a looping animation using a Sine curve. We’ll define the following function:

f(t) = sin(t)

Plain Sine Curve

But now we have a problem, remember that our Flutter curve must map t=0.0 to 0.0 and t=1.0 to 1.0.

As you can see in the image above, or if you take a look at the graph here, you will note that this is not the case.

You will also note that the curve does not do any oscillations (or loops) within the time frame from 0 to 1.

So we want to modify the sine curve slightly:

f(t) = sine(3*2pi*t)*0.5 +0.5

We multiple t by 3*2pi to get 3 oscillations, we decrease the height of the curve by multiplying everything by 0.5, and then offset the curve to make sure it returns a positive value by increasing the output of the function by 0.5.

Now we have this:

Custom Sine Curve

Take a look at the graph here: https://www.desmos.com/calculator/omh7xyuzxk

This is much better, however, keen observers will note that it still does not map a value of 0.0 at t=0.0 and 1.0 at t=1.0.

Technically this would be a problem if we do a once of animation, but we want to create an animation that loops. For example, by calling the repeat method on our AnimationController.

Take a look at this demo application that I made, to see an example of the animation in action: https://dartpad.dev/a3c7d5b24443efdf28ecd3ba19222555

The application is a PageView with multiple examples, keep scrolling until you hit the Sine demo page. You will note that the animation does not satisfy the 0 and 1 rule, however, it works because it is looping. It restarts the animation at the same point where it ended.

So let’s take this function and create a curve in Flutter.

We need to create a new class which extends the Curve class, and then override the transformInternal method, which receives a value t of type double, that we can use to pass in to our own function and return that value.

See below:

class SineCurve extends Curve {
  final double count;

  SineCurve({this.count = 3});

  // t = x
  @override
  double transformInternal(double t) {
    var val = sin(count * 2 * pi * t) * 0.5 + 0.5;
    return val; //f(x)
  }
}

There we go. Now you can use this as you normally would when using a curve.

Spring Curve

In a similar way we can create a spring curve, such as the one below:

Spring Curve

And the code for this:

class SpringCurve extends Curve {
  const SpringCurve({
    this.a = 0.15,
    this.w = 19.4,
  });
  final double a;
  final double w;

  @override
  double transformInternal(double t) {
    return -(pow(e, -t / a) * cos(t * w)) + 1;
  }
}

Note that this curves satisfy the rule of returning 0.0 at t=0.0 and 1.0 at t=1.0. At least, it almost satisfies that rule, at t=1.0 it’s not exactly 1.0, but close enough :)

If you need unique animations in your application, or animations that require specific behavior, then knowing how to create your own curve will be of great value. There is no limit to what curve you can create.

Here is the link to the spring curve, play around with it and see what you can make:

https://www.desmos.com/calculator/6gbvrm5i0s

Learning More

Want to learn more? Check out my Mastering Animation in Flutter course. The course goes into great detail discussing everything you need to be a pro at animation in Flutter. It’s the handbook to animate everything in Flutter, and it will teach you what you need to know in a structured and fun way. You might also be lucky and get it at a discount, use the promo code FUN for a 75% discount.

https://fun-with-flutter.teachable.com/

Each lecture has a dedicated video, code examples, and an interactive DartPad to get you interacting and working with the code examples.

Proxy Flutter Apps

From hacking, debugging, developing, or to tickle some curiosity - there are a variety of reasons that you might want to inspect the underlying web traffic of an application. This article discusses a couple of possible solutions for Flutter applications, with a focus on proxying.

The tools and techniques are from a developers perspective, not a security researcher, as most of the techniques listed below will require modification to the source code. If you are looking at an app from a black box perspective I would suggest the following article: https://blog.nviso.be/2019/08/13/intercepting-traffic-from-android-flutter-applications/

Before we can proxy our application we need to understand some basics regarding HTTPS and SSL certificates. If you are modifying source code to intercept traffic it is important you understand what you are doing from a security perspective.

Note

This blog post was written as a supplement to the video provided above. It serves to provide additional information along with summaries and links to relevant resources.

Other Networking Tools

Below are a couple of tools that you can use “out of the box” to inspect your network traffic. I’m listing them here to give you options, but in my personal opinion, they’re only useful up until a certain point. If they provide you with what you want, then great.

Flutter Stetho

A plugin that connects Flutter to the Chrome Dev Tools on Android devices via the [Stetho Android Library]

Alice

Alice is an HTTP Inspector tool for Flutter which helps debugging http requests. It catches and stores HTTP requests and responses, which can be viewed via simple UI. It is inspired from Chuck.

Flipper

Flipper is a desktop app from Facebook that allows you to debug iOS and Android apps.

These tools instrument your application and use a variety of different techniques to log all your application traffic. It then forwards that information to some GUI client where you can inspect the traffic. Alice’s GUI is baked directly into your Flutter application. Stetho forwards it to the Chrome Dev Tools (most convenient in my opinion). While Flipper requires a desktop client to connect to.

Then we have the Dart DevTools, which can do a lot, however at the time of writing it does not have a network profiler. See this feature request for more info. If the Dart DevTools has a network profiler by the time you are reading this then I would ignore the above options and use the DevTools.

Proxying and HTTPS

The rest of this article will be all about proxying and what you need to know before you proxy an application.

What is a proxy

A proxy, or a Proxy Server, is:

a server (a computer system or an application) that acts as an intermediary for requests from clients seeking resources from other servers.

So imagine we have our Flutter application (A) and our web server (B). To communicate with our server the traffic flows like this:

A -> B.

Now let’s put a proxy tool in the middle of those two (C):

A -> C -> B

It’s easy as one, three, two.

This is a very basic description. For more info see Wikipedia.

But it serves to illustrate what we want. We want a way to inspect the traffic as it is in transition from the application (client) to the server. To achieve this we will need to alter the flow of traffic by first sending the application traffic to an intercepting proxy, and that proxy will then forward it to the server.

Proxy Tools

Lucky for us there are a number of proxy tools available. Some popular options are:

The tool is dependent on what you want to do. Depending on the tool you use it could allow you to monitor the traffic, or intercept and modify the traffic as it is being transmitted. Most of these tools can also be used to perform various levels of security testing.

The companion video for this article demonstrates the basic usage of BurpSuite(https://portswigger.net/burp) as that is what the author is familiar with.

It doesn’t really matter what tool you choose. What we are interested in is the Flutter side of things.

How to Proxy HTTP Traffic

Standard HTTP traffic is easy to proxy. The data is sent unencrypted over the network, and all we need to do is configure our client to first send the data to the proxy, and the proxy will intercept and forward the HTTP traffic. Easy to do.

Please see the video for details how to configure the proxy for BurpSuite, as well as configure an Android Emulator to use a proxy. Whether you are proxying an Android or iOS device, an Emulator, or a browser, the basics are the same - you’ll need to configure the proxy to point to the proxy server’s ip address and listening port.

Encrypted HTTP(S)

An easily digestable video regarding HTTPS - How does HTTPS work

This is where things get interesting. Aside from the fact that HTTPS encrypts our data over the wire, it also acts as a policewoman to ensure that we can trust the server that we are communicating with.

When our Flutter app sends an HTTPS request to our server an SSL Handshake is initiated.

In a TLS/SSL handshake, clients and servers exchange SSL certificates, cipher suite requirements, and randomly generated data for creating session keys.

An SSL certificate is a data file hosted on a website’s origin server. SSL certificates make SSL/TLS encryption possible, and they contain the website’s public key and the website’s identity, along with related information. Devices attempting to communicate with the origin server will reference this file to obtain the public key and verify the server’s identity. The private key is kept secret and secure.

The SSL certificate sent by the server is signed by a certificate authority (CA)

In cryptography, a certificate authority or certification authority is an entity that issues digital certificates. A digital certificate certifies the ownership of a public key by the named subject of the certificate.

Our browsers and mobile devices have a list of known and trusted CAs. That means our browsers can verify that the server we are communicating with (who sent the SSL certificate) is who they say they are. If you want more information on Digital Certificates and Certificate Authorities I suggest the following videos:

Once we have established trust with the server we are communicating with, then we can initiate the encryption process. For that we will need an encryption key that is shared between the client and the server. This key will be used to encrypt and decrypt traffic between them. This is known as Symmetric Key Encryption.

First we need to generate a Symmetric Key, and here our client (Android/iOS device or browser) steps up and does just that. Now we need to send that key to our server. But hold on a minute. The point of this whole process is to be secure. That means we can’t just send the Symmetric key over non-secure HTTP. We need a way to encrypt the encryption key, and a way for the server to decrypt that to, finally, get the key to do further encryption.

To do this we will use Asymmetric Encryption.

Asymmetric cryptography, also known as public key cryptography, uses public and private keys to encrypt and decrypt data

That SSL Certificate we mentioned above, the one that the client receives from the server, contains a public key that we can use to encrypt the secret. To decrypt Asymmetric Encryption requires the matching private key of the public key used to encrypt the data. Under the correct circumstances, only our server will have access to that private key. And there we go, we have a secure way to send the Symmetric Key (or secret) to the server. And now all future traffic can be encrypted with that secret using Symmetric Cryptography.

You might ask, why do we need Symmetric Cryptography. Why not just use Asymmetric Encryption for everything. The short answer is because Symmetric Cryptography is faster, which lends itself well for internet traffic.

Self-signed Certificates

In cryptography and computer security, a self-signed certificate is a certificate that is not signed by a certificate authority (CA). These certificates are easy to make and do not cost money. However, they do not provide all of the security properties that certificates signed by a CA aim to provide.

Anyone can create their own Self-signed Certificate, meaning you do not have to make use of a Certificate Authority to sign your SSL certificate. But if anyone can create a self-signed certificate then why do we need a Certificate Authority? Well now it goes back to that chain of trust. Because a certificate is signed by a known, and trusted, Certificate Authority it means that our browsers and devices can trust those certificates. If a certificate is self-signed or signed by an unknown Certificate Authority you will see a screen similar to the one below:

Proxy Error

If you are testing an application in a development environment the picture above might be very familiar. You will either tell your browser to ignore the warning and continue (bad idea if you do not trust the end point), or you will load your self-signed certificate into the browser’s certificate manager as a trusted certificate. If you do the later your browser will no longer give a certificate warning and will treat the certificates signed with your self-signed certificate as trustworthy.

In the companion video I go into great detail explaining and demonstrating this.

On a mobile device this works in a similar fashion. Just like you can install a self-signed certificate in a browser, you can also install your own self-signed certificates on an Android and iOS device. Between Android and iOS the process is a little bit different and there are some limitations on Android (the reason I made this post). See the following links for more details:

The main thing that you need to know is that from Android N onwards it gets harder to intercept an application’s SSL traffic. Android applications will not respect self-signed certificates added to the device’s trust store. Certificates that you add on Android will be trusted by the Android browser, but not by other applications.

The reason that it won’t work is for security purposes. A browser does not know which endpoint it will communicate with - you as the user might try to access https://google.com or you might go to https://flutter.dev, or any other possible endpoint. For a mobile application, or thick client, that is different. Under most circumstances your application should know the endpoint that it is communicating with. For example, if your company name is MathIsFun and you make an app that is called MathIsFun and your API server is hosted at https://api.mathisfun.com, then your app only needs to care about that particular API’s SSL certificate.

If that SSL certificate is signed by a known and trusted Certificate Authority then great, all you need to do is make an HTTPS request to your endpoint and the rest will be handled for you. If it is not signed by a Certificate Authority then you will need to modify your source code to tell your application that it should trust your self-signed certificate.

This is quite easy to do, but should be done with caution. Self-signed certificates are perfect for development environments. However, under most circumstances you would prefer to go the Certificate Authority route. Most cloud services automatically configure an SSL certificate for you anyway, so there is no reason not to. Firebase hosting is an example. If you host an application on Firebase Hosting they will automatically provision SSL for you.

Note that a lot of these providers use shared SSL certificates - meaning other domains/sites might be using the same certificate as your site. This might not be an issue for you, but there are reasons that you might want your own dedicated SSL certificate issued to your domain/company. For more info see below:

Firebase Shared SSL Certificates

How to proxy HTTPS

What we wanted from the start. I suggest you jump to the video as it will be easier to see exactly what you need to do, especially if this is the first time you are trying to intercept traffic.

The basics are as follows:

  • Start up the intercepting proxy tool (BurpSuite for example)
  • Configure the tool to listen on a dedicated port
  • Setup your device/browser to send traffic to the proxy tool
  • Get a certificate warning
  • Realise that your proxy tool’s self-signed certificate is not recognised by your browser or mobile device
  • Add the proxy tool’s self signed certificate to your device or browser’s trust store
  • See that your proxy tool is intercepting the traffic (Unless you’re using Android N or above)
  • Realise that your proxy tool is sending that data on to the server, and the server is responding to the proxy tool, and the proxy tool is forwarding the response on to you (win)

How to proxy HTTPS traffic on Android N and above

As mentioned above, from Android N onwards you need to configure your application manually to trust self-signed certificates.

In Dart/Flutter this is quite easy to do.

Import some libraries:

import 'dart:io';
import 'package:flutter/services.dart';

Add your proxy tool’s certificate as an asset in your pubsec.yaml file. For example:

assets:
   - assets/raw/certificate.pem

Then add the following code somewhere in your application before making network requests. For example, in the main function.

void main() {
  ByteData data = await rootBundle.load('assets/raw/certificate.pem');
  SecurityContext context = SecurityContext.defaultContext;
  context.setTrustedCertificatesBytes(data.buffer.asUint8List());
  runApp(MyApp());
}

What this does is it sets the set of trusted X509 certificates used by SecureSocket client connections, when connecting to a secure server.

And x509 is:

a standard defining the format of public key certificates. X.509 certificates are used in many Internet protocols, including TLS/SSL, which is the basis for HTTPS, the secure protocol for browsing the web.

So basically you are adding your certificate to your application’s list of trusted certificates. This is done at runtime and should only happen once. If you add it multiple times you will get an exception. You also need to add the certificate before attempting any SSL connections with your proxy, otherwise you will get an SSL exception.

Once you have done this you no longer need to add your self-signed certificate to your device/browser. As you are instructing your application to trust this certificate.

SSL Encodings

The method setTrustedCertificatesBytes requires a PEM or PKCS12 file containing x509 certificates.

Depending on the Proxy Tool you use you might be required to convert your SSL certificates to a different encoding or file format. For example in the video I convert a DER file to a PEM file. For more information regarding this please see the following links:

Considering Security

You will see a lot of questions on the internet asking how to get rid of the certificate errors in their application. A common solution is to disable all certificate verification and allow your app to accept any certificate.

As an example, see the Stack Overflow thread here:

Solve Flutter Certificate Error

This is obviously a terrible idea, and should never be considered - not even for a development environment. As you saw above it is quite easy to load a custom certificate into our application’s list of trusted certificates, so don’t be lazy when it comes to the security of your app.

You can limit the exposure more by only allowing these certificates to be loaded for debug builds of your application. Ensuring that the release build of your application will not trust any SSL certificates that were used for development and debugging purposes.

You can easily modify the example above into something similar to the following:

Future<bool> addSelfSignedCertificate() async {
  ByteData data = await rootBundle.load('assets/raw/certificate.pem');
  SecurityContext context = SecurityContext.defaultContext;
  context.setTrustedCertificatesBytes(data.buffer.asUint8List());
  return true;
}

void main() async {
  assert(await addSelfSignedCertificate());
  runApp(MyApp());
}

Now our main function will only add our proxy certificate for debug builds of our application, as we wrap the function call in an assert. When performing a release build of our application, Dart strips all assert logic from our application.

Conclusion

It is important to understand the basics of SSL Certificates and how HTTPS works before altering logic related to the security of your application’s network traffic. Adding a self-signed certificate to your application’s trust store is quite simple to do, but should still be done with caution. Do not take the easy route when it comes to the security of your application.

Done correctly, you will have an easy way to intercept and inspect the underlying network traffic that your application generates.

Using Firebase in Flutter Web

As with anything in the tech space — take note of this article’s date (September 2019). Landscapes are constantly changing, what is said below may no longer be relevant or there may be easier alternatives.

Using Firebase in Flutter web is fairly straightforward, however, not as simple as it could be. At the time of this writing, there is no universal Flutter library for Dart or universal Firebase package that extends to Android, iOS, and Web (maybe even desktop) when building Flutter applications.

See here for a discussion on the topic.

This article will only discuss the web implementation and how to setup Firebase for Flutter web.

If you’ve never used Firebase before, then you will be better served reading the Firebase docs, or watching one of the endless numbers of videos regarding Firebase. I suggest the actual Firebase channel or Fireship.

Initializing

The package that you will need to use is the standard web implementation of Firebase for Dart. Find it here:

https://pub.dev/packages/firebase

The package’s Pub page gives all the information you need to get set up and started. But as a summary, let’s go through the implementation to get Firebase Authentication going.

NOTE: If you are using Firebase Hosting then watch the video linked above for an alternative implementation.

First, you’ll need to add Firebase as a dependency in you pubsec.yaml

dependencies:
  firebase: ^5.0.0

Next, you’ll need to include the Firebase JavaScript libraries in your .html file. For a Flutter project, the default HTML file is located at web/index.html.

<script src="https://www.gstatic.com/firebasejs/6.6.0/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/6.6.0/firebase-auth.js"></script>

Your versions may be different. You can find the latest versions on the Firebase website. Additionally, you need to import the Firebase libraries that you will be using — for example, Auth, Database, Firestore. For a full list see the Firebase website.

It makes sense to initialize Firebase as soon as your application starts. So head over to your Flutter project’s main.dart file and import:

import 'package:firebase/firebase.dart';

And initialize your Firebase project in main.dart:

void main() {
  initializeApp(
    apiKey: "YourApiKey",
    authDomain: "YourAuthDomain",
    databaseURL: "YourDatabaseUrl",
    projectId: "YourProjectId",
    storageBucket: "YourStorageBucket");
}

You can find the properties for your Firebase project in the Firebase console, within the Project Overview/Settings section of your project (in the companion video, linked above, I walk you through the process).

Alternatively, you can also initialize your app within JavaScript in the index.html file.

<body>
  <!-- Previously loaded Firebase SDKs -->

  <script>
    // TODO: Replace the following with your app's Firebase project configuration
    var firebaseConfig = {
      // ...
    };
    // Initialize Firebase
    firebase.initializeApp(firebaseConfig);
  </script>
</body>

A Note on Hot Reload/Restart

After adding firebase.initializeApp you might notice that hot reload and hot restart no longer works. With an error message similar to the following in your browser’s console:

Firebase App named ‘[DEFAULT]’ already exists (app/duplicate-app).

This means you will need to add a check before calling initializeApp, and that check is to make sure that it hasn’t already been initialized. You can do that by changing your main function as follows:

void main() {
  if (fb.apps.isEmpty) {
    fb.initializeApp(
      apiKey: 'AIzaSyD8JoU_58xKlQFvva7nS7VHTKc1vUkaosk',
      authDomain: 'fun-with.firebaseapp.com',
      databaseURL: 'https://fun-with.firebaseio.com',
      projectId: 'fun-with',
      storageBucket: 'fun-with.appspot.com',
      messagingSenderId: '1006728819313',
    );
  }
}

Notice the conditional to see if fb.apps.length is not equal to 0. If yes, then we initialize Firebase.

Firebase Authentication

Now that Firebase is initialized we can easily use the Firebase relevant functionality.

You can get an instance of Firebase Authentication by first importing firebase.dart and access the Auth object through the auth() method, and the GoogleAuthProvider object through the GoogleAuthProvider() method. See below:

import 'package:firebase/firebase.dart';
import 'package:meta/meta.dart';

@immutable
class UserRepository {
  UserRepository({Auth firebaseAuth, GoogleAuthProvider googleSignin})
      : _firebaseAuth = firebaseAuth ?? auth(),
        _googleSignIn = googleSignin ?? GoogleAuthProvider();

  final Auth _firebaseAuth;
  final GoogleAuthProvider _googleSignIn;

  Future<UserCredential> signInWithGoogle() async {
    try {
      return await _firebaseAuth.signInWithPopup(_googleSignIn);
    } catch (e) {
      print('Error in sign in with google: $e');
      throw '$e';
    }
  }

  Future<UserCredential> signInWithCredentials(
      String email, String password) async {
    try {
      return await _firebaseAuth.signInWithEmailAndPassword(email, password);
    } catch (e) {
      print('Error in sign in with credentials: $e');
      // return e;
      throw '$e';
    }
  }

  Future<UserCredential> signUp({String email, String password}) async {
    try {
      return await _firebaseAuth.createUserWithEmailAndPassword(
        email,
        password,
      );
    } catch (e) {
      print('Error siging in with credentials: $e');
      throw '$e';
      // throw Error('Error signing up with credentials: $e');
      // return e;
    }
  }

  Future<dynamic> signOut() async {
    try {
      return Future.wait([
        _firebaseAuth.signOut(),
      ]);
    } catch (e) {
      print ('Error signin out: $e');
      // return e;
      throw '$e';
    }
  }

  Future<bool> isSignedIn() async {
    final currentUser = _firebaseAuth.currentUser;
    return currentUser != null;
  }

  Future<String> getUser() async {
    return (_firebaseAuth.currentUser).email;
  }
}

And here is an example to log in through email and password:

Future<UserCredential> signInWithCredentials(
      String email, String password) async {
    try {
      return await _firebaseAuth.signInWithEmailAndPassword(email, password);
    } catch (e) {
      print('Error in sign in with credentials: $e');
      throw '$e';
    }
}

That’s it! You are set up and have an example method. For more examples, you can take a look at the Fun with Flutter web application’s source code:

https://github.com/funwithflutter/fun-with-flutter-website

There is also a link on the Firebase Pub page that point to additional examples.

https://github.com/FirebaseExtended/firebase-dart/tree/master/example

Conclusion

I hope you found this useful. It is quite easy to do once you know how. If you have better solutions let me know in the comments!

If you want to explore a website made entirely in Flutter, as well as read more Flutter articles, go take a look at the Fun with Flutter website. It’s still under active development, and Flutter web is still new, so don’t judge too harshly. Ideally, visit it on Chrome on a computer for best performance.

Flutter Web: Using HTML and JavaScript

The date of publication is important. If you’re reading this and it’s not September 2019 then there might be a better solution to the problem I’ll be describing below.

If it is September 2019, and/or the problem below does affect you, then welcome, enjoy, and bask in the information that will be shared with you.

Don’t care about the background info and just want a solution? Scroll to the end.

Le Problème

Take a look at the Github issue below for a detailed overview of the problem. Scroll to the bottom of the issue - there might even be a solution to the problem we’ll be discussing.

https://github.com/flutter/flutter/issues/35588

Still here? Guess people are still working on the issue.

Here’s my take on the problem

Let’s begin with a quick overview of Packages and Plugins:

Dart packages: General packages written in Dart, for example the path package. Some of these might contain Flutter specific functionality and thus have a dependency on the Flutter framework, restricting their use to Flutter only, for example the fluro package.

Plugin packages: A specialized Dart package which contain an API written in Dart code combined with a platform-specific implementation for Android (using Java or Kotlin), and/or for iOS (using ObjC or Swift). A concrete example is the battery plugin package.

The above quotes are taken from the Developing Packages page on the Flutter.dev website.

A plugin is essentially a way to perform platform specific functionality from dart code (using widgets) - think fingerprint reader, storage, shared preferences and camera.

We can make plugins for Android, using Java or Kotlin.

We can make plugins for iOS, using Objective-C or Swift.

What about Web, using HTML and JavaScript? At the time of writing, no luck there yet. 

Well that’s a lie, technically you can make web plugins, however I’m not sure if there is consensus on the correct way to do this. You can make use of relative file imports to import the relevant platform code - see this Github issue for more information, or alternatively give this Medium post a read.

But point remains - how do I use HTML and JavaScript in my Flutter web app, today!

“All I want to do is create an external link” - As said by me, myself and I.

Dart as a language targeted the web way before Flutter became a thing. For more information on using Dart for the web, see https://webdev.dartlang.org. It can also be used in conjunction with Angular.

Point is, there are Dart libraries available to target and build for the web.

An example of such a library is dart:html:

HTML elements and other resources for web-based applications that need to interact with the browser and the DOM (Document Object Model).

This library includes DOM element types, CSS styling, local storage, media, speech, events, and more. To get started, check out the Element class, the base class for many of the HTML DOM types.

With the recent merge of Flutter-Web into the main Flutter repository it is no longer possible (at the time of writing) to directly do one of the following imports:

import dart:html;

or

import dart:js;

These libraries are platform specific, i.e. the Web/Browsers.

They cannot be used within an Android or iOS application. As such, if you’re using Flutter Web on the main Flutter channel, in a project targeting Web, Android and iOS - then your IDE will likely throw this error:

Target of URI doesn't exist

Target URI doesn’t exist

The error you see above is caused by the Flutter Analyzer SDK, see this comment below from the Github issue:

Flutter has two copies of the Dart SDK. One - used by flutter tools - has the dart:html library and the standard Dart web compilers. That allows the flutter tool to build flutter for web apps. That copy lives in //bin/cache/dart-sdk/

The other copy of the Dart SDK is used to analyze user code against. That copy lives in //bin/cache/pkg/sky_engine/lib/. The libraries there are defined in the flutter/engine repo in https://github.com/flutter/engine/blob/master/sky/packages/sky_engine/lib/_embedder.yaml.

The Solution

We had a meeting with the Dart Analyzer team and are moving forward with adding dart:html and dart:js to the Flutter Analyzer SDK

While writing this article this was the last comment for the Github issue. Meaning that, depending on when you’re reading this, it might no longer be an issue.

So try and import dart:html or dart:js. If it works, great! You wasted your time reading this article. Don’t feel bad, I wasted my time writing it.

Doesn’t work?

Also great!

You can use the universal_html package in the meantime:

Cross-platform dart:html that works in the browser, Dart VM, and Flutter. Typical use cases are:

  • Cross-platform application development (e.g. Flutter mobile and web versions).
  • Web crawling and scraping

HTML Examples

For HTML, import like this:

import 'package:universal_html/prefer_universal/html.dart' as html;

Simple example, let’s open a link.

html.window.open('https://youtube.com', 'youtube');

Let’s pop a window:

html.window.alert('Hello, world!');

JavaScript Example

For JavaScript, import like this:

import 'package:universal_html/prefer_universal/js.dart' as js;

Let’s pop a window:

js.context.callMethod('alert', ['Hello, world!']);

The top-level getter context provides a JsObject that represents the global object in JavaScript, usually window.

For more complex usage, see the Dart JS library and Dart HTML library.

Conclusion

Again, depending on when you’re reading this, you might not need universal_html. You might not even need HTML and JavaScript access, depending on the ecosystem of web related Flutter plugins.

HTML and JavaScript access will most likely only concern plugin creators. Just like you normally wouldn’t directly use Kotlin or Swift when creating a Flutter application, you would ideally not directly use JavaScript and HTML when developing for the web in Flutter. Instead these functionalities will be exposed through the plugins we know and love, or through new plugins that you can easily incorporate.

But that’s for the future. For now, if you want to open a link, for example, then you need access to HTML and JavaScript.

Flutter Web Updates Sept 2019

As you may have heard by now, the separate Flutter-Web branch has been merged into the main Flutter repository. This means you no longer need to do special imports for your Flutter-Web projects. It also means you can import packages from the pub store into your Web application as you normally would.

Flutter web old repo

Don’t get too excited though, at the time of writing, this is only available on the Flutter dev and master channel - so you’d need to switch to those channels and live a bit dangerously to give Flutter Web a test drive. You will also need to enable Flutter Web.

flutter config --enable-web

For more info on your current channel and how to upgrade and change channels see Flutter Channels.

For a full overview and up to date information on enabling Flutter Web I suggest you visit the Flutter Web getting started page.

The process of converting my Flutter Web application to the main Flutter repository was fairly smooth. It involved changing some of the imports in the pubsec.yaml file, and changing or removing all of the Flutter-Web specific dependencies. It now resembles the normal pubsec.yaml dependencies we have grown to love. If you want to see an example you can take a look at the FunWith site’s pubsec.yaml file.

Running

flutter create your_project_name

Should by default support builds for iOS, Android and now Web. You can verify by noting the additional Web directory that is created in your main project directory.

You can launch your debug app on Chrome running:

flutter run -d chrome

All of the above options are also available in Visual Studio Code and I would assume Android Studio (I don’t use it). Meaning when you run your app from your IDE it should bring up the browser as an option to launch. Running the app in debug mode will require Chrome. However, when you build you are basically generating static HTML and JavaScript files, meaning it can be hosted as you would normally host a static website and it will run on any device/platform/browser. Performance may vary though - at the time of writing I observed better performance on Chrome.

Over the past couple of days that I converted the FunWith app to the main repository, added Firebase Authentication, changed code, tried adding packages, made use of native HTML elements, and published a release build, I encountered a lot of hiccups. Some that were platform specific, some that required creative work arounds, some that were fixed by the Flutter Web team, and obviously one or two of my own.

Point is, proceed with caution. That all said, Flutter Web is SUPER AWESOME.

Over the last couple of months I’ve seen a lot of improvements in the performance of my app (see the YouTube video above where I run through the different iterations of my app up until this point). Especially now that you can create release builds of your Flutter Web application running:

flutter build web --release

In addition to that, the browser now recognises your mouse when it hovers over elements, and you can use the scroll wheel on your mouse to scroll in Flutter Web.

I’ll be making more posts on Flutter Web as I add features, and I’ll also be creating a couple of blog posts and videos explaining my current implementation, how to incorporate Firebase, and some of the work arounds I used to add certain functionality (links, tab-throughs). Note that depending on when you read this the environment might be completely different - as Flutter Web is maturing and adding more features things may be different in the future.

If you want to see a website made in Flutter Web, feel free to check out the Fun with Flutter website. The code is also open source, if you are interested you can find it here.

Last but not least, you now create an account on the site (it uses Firebase as a backend). If you sign up the site will reveal to you the Flutter YouTube channel that I currently enjoy the most! Early bird signees will also be rewarded with discounts on future material and other goodies. Depending on when you read this there might already be some other cool stuff on the site. So go check it out anyway :)

Making a Blog With Flutter and Hugo

Lets begin with a quick description for Flutter Web and Hugo, and then provide an overview and motivation for using these two technologies together.

Overview

Flutter Web: is a code-compatible implementation of Flutter that is rendered using standards-based web technologies: HTML,CSS and JavaScript. With Flutter for web, you can compile existing Flutter code written in Dart into a client experience that can be embedded in the browser and deployed to any web server. You can use all the features of Flutter, and you don’t need a browser plug-in.

Hugo: is one of the most popular open-source static site generators. With its amazing speed and flexibility, Hugo makes building websites fun again. Hugo is written in Go (aka Golang).

Motivation

The FunWith website and blog are companions for the Fun with Flutter YouTube channel. So using Flutter Web seemed like an obvious choice as it serves the dual purpose of function and learning (for myself as well as others).

The use of Hugo came more from need than desire. Flutter Web is still in early stages and as a result I wanted to ensure that the content I produce has a safe place to live; come in Hugo. While at the same time I wanted to expose that content to the Flutter web application and make use of Flutter as much as I could; come in Hugo as an API.

Why not both meme

Lastly, I did not want to make unnecessary work for myself. Frameworks like Hugo, Ghost, Jekyll, etc, are made in such a way that they are easily configurable, fast, and most important it’s easy to produce content on.

My vision was that I could make a normal blog post, save it on the FunWith blog, and auto-magically the FunWith app should be updated to reflect the change and expose the new blog content.

Motivation and expectations set - let’s jump into the code.

The Code

The main reason you’re here. Both the blog and site are open source so take a look if you want!

The Hugo docs provide a better explanation than I could to get setup and started using Hugo.

I did not do anything special for the blog, but let me give you a quick overview of how Hugo works. In Hugo you can define layout files that style different sections of your site. For example, you could have layout files that create the home page, site sections, and individual posts. Once you run Hugo it proceeds to take your markdown files (and their respective folders and sub-folders) and generates static HTML pages based on these defined layouts. Hugo will also generate an index.html file for the landing (home) page.

If you use a theme for Hugo there is technically nothing you need to do on your end except to make the markdown files (blog posts) and then run Hugo to generate the static HTML pages.

I was not too concerned about the look of the FunWith blog as it mostly serves to statically serve the content, while the Flutter site will take that content and make it pretty. So I didn’t spend any time customizing the blog - except for some time learning the ropes of Hugo and choosing a theme. The theme that I’m using at the time of writing is Slick.

Next, we need a way to get the blog content into our Flutter web application. This is relatively simple to do, provided you understand some Hugo templating and functionality.

Generating JSON

This is all possible owing to Hugo’s ability to output different file types. For example, instead of outputting HTML pages we can set it up to output json files - or both json and HTML as we will be doing. Similar to how we would create layout files to customize our theme, we can also define layouts that will build static json files when running Hugo.

The first thing to do is define the layout types in the Hugo configuration file. Below is an example of such a configuration.

Config.toml

[outputs]
    home = ["json","html"]
    page = ["html"]
    section = ["html"]

As you can see we are stating which section of the site require what output types. Only the home page is marked to output json and HTML. That means upon running Hugo it will produce an index.html and index.json file in accordance to the HTML and json index layout files we provide.

The index.html page will be built using the theme’s HTML layout file. However, we need to tell Hugo what layout file to use for the home page’s json output, as this is not specified by the theme we are using.

In our site’s directory we must create a new layout file within the layouts folder. We’ll need to call this file index.json.json - as this is the file name that Hugo will be looking for to build index.json.

As an example, let’s give this file the following content:

index.json.json - example file

{
    "api": {
        "version": 1.0,
        "description": "doing some tests"
    }
}

Building Hugo will generate an index.json file with this exact content, as is. What we want is for Hugo to generate json content based on our blog data (markdown files). For that we’ll need to make use of Hugo’s templating.

Below is an example of how I generate some of the json data for the FunWith site.

index.json.json - layout

{{- $.Scratch.Add "allPages" slice -}}
{{- range .Data.Pages.ByDate.Reverse -}}
    {{- $.Scratch.Add "allPages" (dict "uri" .Permalink "title" .Title "content" .Plain ) -}}
{{- end -}}

{{- dict "pages" ( $.Scratch.Get "allPages") | jsonify -}}

This is an example index.json.json layout file that will iterate over all available posts on the blog (in reverse date) and then create a dictionary of the URI, the title, and the content of the posts. It will then take that dictionary and pipe (pass in as a parameter) it to the jsonify function to generate json data.

The above layout will produce output similar to the below (depending on the content of the blog/posts).

index.json - output

{
    "pages": [
        {
            "content": "Choclate is good!\n",
            "title": "Chock Stick",
            "uri": "https://fun-with-blog.firebaseapp.com/posts/chock-stick/"
        },
        {
            "content": "Building, working, playing. Blah.\n",
            "title": "Test Post",
            "uri": "https://fun-with-blog.firebaseapp.com/posts/test-post/"
        },
        {
            "content": "First page. Doing some testing.\n",
            "title": "First Post",
            "uri": "https://fun-with-blog.firebaseapp.com/posts/first-post/"
        }
    ]
}

If you’re not familiar with Hugo, and Go, the code above might seem very strange. This article will not discuss Hugo templating as there are a number of better resources for that. I would suggest starting with the Hugo documentation and if you’re interested in a more complete example of using Hugo to generate an API then you can take a look at the following links:

https://forestry.io/blog/build-a-json-api-with-hugo/

https://forestry.io/blog/hugo-json-api-part-2/

That is basically that. We now have a static json file that we can host and query to retrieve the blog content that we need.

For example, from the Flutter web application I make an HTTP request to the index.json file hosted on the FunWith blog. Once the json is retrieve it can be parsed into objects and used within the application as desired. Future blog posts will go into more detail regarding this.

What’s Next

We have the tools and the know how to expand this as much as we want. We can define as many json output files as desired for as many sections and posts as needed. These json files can be fine grained to output the exact content that we want.

At the time of writing this blog I’m redirecting users from the FunWith app to the FunWith blog to read the actual blog posts. For example, a user can see what blog posts there are on the FunWith site, and upon clicking a post they are taken to the FunWith blog to do the actual reading.

This is not ideal and in the future this will change. Either by parsing the markdown directly with Flutter, or alternatively making use of an iFrame or WebView to render the blog content in site.

Well that’s all! I don’t expect a lot of you read up until here. If you did, thanks and I hope you enjoyed this post. I will be making more blogs related to the creation of the FunWith site and how the site expands and changes.

Until next time.

-Gordon

The Fun with Flutter website

Made entirely in Flutter! Well for the most part but it’s still pretty cool!

Care to watch a video instead of reading, here you go:

This is a short introductory post as I will be making follow up videos and blog posts detailing the creation, struggles and fun I had in making the FunWith website. These will discuss architecture, state management, dependency injection, cute animations, and custom widgets of course.

They’ll also discuss shortcomings, for example there is no easy way to render a video yet and it took way too much Googling to find a way to make an external link.

For me, however, the most exciting part will be to see the performance improvements in the site as Flutter for Web matures and what we can do once more packages become available.

As a side note, the website is nowhere near complete. It’s even short of my initial vision for version 1.0 - there was still a lot left to do in my Trello board.

But, I set myself a deadline and this is the current state of the website. And here it is.

There are a couple of nice things that I did do however, that I’m proud off.

  • I’m using the Bloc pattern for state management which required a bit of a mind shift, but now it’s in a pretty solid state and easy to interact with from the UI.
  • There is a nice integration between Hugo for the blog content and the Flutter website. Where all I need to do is write a Hugo post and the website updates automatically. For those of you that don’t know, Hugo is a framework to easily create a static website or blog.
  • And there is also a little bit of fancy animation that was easy to do using a neat trick with paddings.

The Future

So what content can you expect? To be honest - whatever I feel like!

I’ll have a section showcasing flutter packages that I’ve made - go take a look on the site, these packages are interactive (as they’re written in Flutter). What better way to showcase a Flutter package than have it be interactive on a website made in Flutter - pretty cool.

There will be blog posts.

There will be a section for tips and tricks or neat resources that I’ve come across. At some point in the future I’d also like to make a section for quality learning material.

Again, whatever I feel like. I’m using this as a learning opportunity for myself that, over time, will hopefully be extended, expanded and refined to be of value to you and other like minded people.

The next blog post and video will be how I use Hugo to generate the blog content and expose the content through a static API to the Flutter website. I took this route as an easy way to overcome some of the limitations for Flutter web, in the future this might be completely different.

Anyway, there’s a lot of code, writing and video editing to do. Everything is open source, so please if you feel like you want to contribute with a blog post, a video, an idea, or even code - you are more than welcome. If you are still learning and feel like you don’t have anything to contribute, then let me tell you you’re wrong. As I said, I’m still learning and figuring out most things as I go along. Why not join me?

Cheers people, catch you in the next video - or blog post I guess :)

Oh, and remember to check out the website for yourself - link in the description!