Flutter Web: Some Notes

Vinay Shankri
Flutter Community
Published in
5 min readDec 14, 2020

tl;dr

I recently started working on a new Flutter project primarily for the Web (of course works on devices as well). I want to share few things that I had to sort out before getting steady and speeding through web app development such as:

CORS (cross-origin resource sharing) and handling app-specific custom headers, Change in REST endpoint URL in development and production, Routes to effectively work with browser back and deep linking, Hot reloading, and debugging

The gist of my App

It is an enterprise app with multiple modules and screens. Data is purely served from Java-based REST endpoints. I use VS Code for Flutter development and “dio” as my Dart HTTP client.

I have custom SSE implementation (Server Sent Events) as “dio” did not cut it. Please refer Server Sent Events(SSE) with Flutter for more information.

Hot Reloading

Currently, hot reloading is not supported in Chrome. You have to press “r” or “R” in VS code terminal every time you want your changes to reflect in your browser. Hopefully, sometime soon hot reloading in Chrome will be the same as the Android emulator or iOS simulator.

To circumvent this, I run my app both on Chrome and iOS simulator for faster development. This also helps me instantly test responsive and reactive screens.

Step-wise debugging

Unfortunately at this point, we cannot step-wise debug our code (In VS Code) when running on Chrome. For now, I debug in the iOS simulator or Android emulator. Even though it is not terribly bad, I hope we get this feature at the earliest.

CORS and why should I care about it for Web?

One way to run a flutter web app on Chrome is by running the below command from your project directory

flutter run -d chrome

Note: The main advantage of running it this way is, we get to see all exceptions and log messages in VS code terminal instead of Chrome browser’s console (fewer windows to look at) + Ability to refresh UI by pressing ‘r’ or ‘R’ on VS code terminal instead of refreshing the browser.

The above command opens a new copy of the Chrome browser and assigns a new port at runtime, for example, http://localhost:62731/#. Now all Flutter web pages are served from port “62731” and my REST endpoint is running on a different port (Java-based microservice) example: http://localhost:8080/myproject/myendpoint.

Now CORS kicks in on the browser because of 2 different ports. The browser needs permission to access REST endpoints running on port 8080.

Note: CORS is independent of the authentication and authorization mechanism of your app

Any other way to overcome CORS?

There are other ways that I found are more cumbersome and less convenient at this point in time (it may change in the near future).

A few that I am aware of:

flutter run -d chrome --web-port=9090: With this command, we can specify the port. Now we can set up our own proxy server to overcome CORS. This needs extra setup + All logs and exceptions appear in the Chrome browser’s console and not on VS code terminal.

flutter run -d web-server --web-port=9090 --web-enable-expression-evaluation: This technically is supposed to show logs and exceptions in VS code terminal but I did not find much success.

open -n -a “Google Chrome” --args --user-data-dir=/tmp/temp_chrome_user_data_dir http://localhost:8100/ --disable-web-security: Running an instance of Chrome to ignore CORS

I am going with enabling CORS on my Java-based microservices in development mode. In production (for web), all requests (static content & REST calls) anyways go through the proxy (same domain & port) and there is no need to enable CORS

How to enable CORS on the server?

Add the below headers to your CORS pre-flight request and the response status should be 202.

Note: CORS preflight request is an HTTP OPTIONS call made by the browser asking for permission. We need to respond with the below headers and a response status of 202 when the HTTP method == OPTIONS. I am handling this in Servlet Filter of my Java-based microservice.

line #1: VM arguments to enable CORS in DEV mode only.

line #2: “*” is for all domains and ports. However, you can whitelist IPs as well.

line #3: We need to whitelist all headers that are part of the request. This is a must for CORS if you plan to send app-specific custom headers. In the above example, “custId” & “appId” are custom headers.

I have tested (dio or default) this and it works fine on devices and the web.

How are things handled using dio (Dart HTTP client)?

Dio: “A powerful HTTP client for Dart, which supports Interceptors, Global configuration, FormData, Request Cancellation, File downloading, Timeout, etc.”

I will try to keep it simple and not include my app-specific nuances. I have 3 classes: Application Constants, Simple Logger, and an abstract class (AbstractHttp.dart) that all service classes (all classes responsible for making REST calls) extend.

Application Constants

As the name goes it is a simple class that holds all constants.

line#4 & #7: Check development mode or not

line#10: Production URL for iOS & Android devices (Web app uses relative path)

line #14 through #17: Constants specific to DEV mode as used in AbstractHttp.data

Logger

A simple logging wrapper for the Logger package. In this example, I am just printing log statements to the console. In my app, it is integrated with SPLUNK

AbstractHttp

All service classes (classes making REST calls) across modules of my app extend AbstractHttp. The main idea is to keep things centralized.

line #24: _init() method sets timeouts, adds 2 interceptors (logging and app-specific interceptor), and prepares base URL (line #4). Logging interceptor is great to see details of REST calls in VS Code terminal.

line #61: _prepBaseUri() adjusts base URL based on DEV, PROD, Web, and for devices. This really helps to test seamlessly on local dev and in different environments.

line #43: _interceptor() registers an app-specific interceptor to add custom headers in DEV mode. In my app, these headers are automatically injected by the authentication layer (when the user has logged in). I just need to simulate the same in DEV mode. The real point here is you can centrally orchestrate based on your requirements that works well in DEV and environments across devices and the web.

Routing & Dependency Injection

I exclusively use named routes. It works fine on devices and is perfect for the web to support browser back and deep linking. Some great packages are Flutter Modular & Fluro. I use Flutter Modular as I take advantage of modules and dependency injection along with routing. It is very similar to Guice dependency injection modules for Java.

CONCLUSION

It is a breeze to start a Flutter app for iOS and Android. Flutter Web is still in beta and has some catching up to do. Based on the speed of Flutter’s evolution, I am hoping Flutter Web to be out of beta very soon. I just wanted to share a few things that helped me on my Flutter Web project. I plan to take it to production early next year.

Thank you.

Vinay

--

--

Vinay Shankri
Flutter Community

With 21+ years of experience. I see myself as a craftsman who takes a keen interest in every aspect of building a great quality product.