0% found this document useful (0 votes)
142 views9 pages

Flutter Web - Some Notes. CORS (Cross-Origin Resource Sharing) - by Vinay Shankri - Flutter Community - Dec, 2020 - Medium

This document summarizes some notes about developing Flutter web apps. It discusses issues with CORS when making requests to backends on different ports, and provides solutions like enabling CORS headers on the backend server. It also covers hot reloading not working in Chrome, debugging challenges, and using the Dio HTTP client to abstract REST calls with interceptors and global configuration.

Uploaded by

NixonMR
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
142 views9 pages

Flutter Web - Some Notes. CORS (Cross-Origin Resource Sharing) - by Vinay Shankri - Flutter Community - Dec, 2020 - Medium

This document summarizes some notes about developing Flutter web apps. It discusses issues with CORS when making requests to backends on different ports, and provides solutions like enabling CORS headers on the backend server. It also covers hot reloading not working in Chrome, debugging challenges, and using the Dio HTTP client to abstract REST calls with interceptors and global configuration.

Uploaded by

NixonMR
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 9

13/1/2021 Flutter Web: Some Notes.

e Notes. CORS (cross-origin resource sharing)… | by Vinay Shankri | Flutter Community | Dec, 2020 | Medium

Flutter Web: Some Notes


Vinay Shankri Follow
Dec 14, 2020 · 5 min read

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

https://medium.com/flutter-community/flutter-web-for-an-enterprise-app-a056fb4e26d1 1/9
13/1/2021 Flutter Web: Some Notes. CORS (cross-origin resource sharing)… | by Vinay Shankri | Flutter Community | Dec, 2020 | Medium

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.

https://medium.com/flutter-community/flutter-web-for-an-enterprise-app-a056fb4e26d1 2/9
13/1/2021 Flutter Web: Some Notes. CORS (cross-origin resource sharing)… | by Vinay Shankri | Flutter Community | Dec, 2020 | Medium

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.

https://medium.com/flutter-community/flutter-web-for-an-enterprise-app-a056fb4e26d1 3/9
13/1/2021 Flutter Web: Some Notes. CORS (cross-origin resource sharing)… | by Vinay Shankri | Flutter Community | Dec, 2020 | Medium

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.

1 if("y".equalsIgnoreCase(System.getProperty("dev"))){
2 response.addHeader("Access-Control-Allow-Origin", "*");
3 response.addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELE
4 response.addHeader("Access-Control-Allow-Headers", "custId, appId, Origin, Conten
5 response.addHeader("Access-Control-Expose-Headers", "Authorization, authenticated
6 response.addHeader("Access-Control-Max-Age", "1728000");
7 response.addHeader("Access-Control-Allow-Credentials", "true");
8 }

medium-cors.java hosted with ❤ by GitHub view raw

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)

https://medium.com/flutter-community/flutter-web-for-an-enterprise-app-a056fb4e26d1 4/9
13/1/2021 Flutter Web: Some Notes. CORS (cross-origin resource sharing)… | by Vinay Shankri | Flutter Community | Dec, 2020 | Medium

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

1 ///Application Constants
2 class AppConstants {
3 ///DEV OR PROD MODE
4 static const bool isProd = bool.fromEnvironment('dart.vm.product');
5
6 ///DEV OR PROD MODE
7 static const bool isDev = !bool.fromEnvironment('dart.vm.product');
8
9 ///this is base URI for non-web browser (for browser it is relative path)
10 static final String prodBaseUri = 'https://myprodurl/';
11
12 ///*** START: DEV related */
13 ///base URI when on isDev == true
14 static final bool useDevHeaders = true;
15 static final String devBaseUri = 'http://localhost:8080';
16 static final String custId = 'XYZ_COMPANY';
17 static final String appId = 'G3GYEP314WM1MZ37';
18
19 ///*** END: DEV related */
20 }

medium-app-constants.dart hosted with ❤ by GitHub view raw

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

1 import 'package:logging/logging.dart';
2
3 ///simple logger class (singleton) that writes to console
4 ///this can be customized to you app's requirement
5 ///refer this link for more info: https://pub.dev/packages/logger
6 class XLogger {
7 static final XLogger _xLogger = XLogger._internal();
8
9 ///I am just using 'main' for simplicity
10 ///In actual app you can have logger for each module
11 var logger = Logger('main');
12
13 factory XLogger() {
14 return _xLogger;
15 }
16
17 XLogger._internal() {
18 _init();
19 }
20
21 void init() {
https://medium.com/flutter-community/flutter-web-for-an-enterprise-app-a056fb4e26d1 5/9
13/1/2021 Flutter Web: Some Notes. CORS (cross-origin resource sharing)… | by Vinay Shankri | Flutter Community | Dec, 2020 | Medium
21 void _init() {
22 Logger.root.level = Level.ALL;
23 Logger.root.onRecord.listen((record) {
24 if (record.error != null && record.stackTrace != null) {
25 print('${record.level.name}: ${record.loggerName}: ${record.time}: ${record.mess
26 print(
27 'level: ${record.level.name} loggerName: ${record.loggerName} time: ${record
28 } else if (record.error != null) {
29 print('level: ${record.level.name} loggerName: ${record.loggerName} time: ${reco
30 } else {
31 print('level: ${record.level.name} loggerName: ${record.loggerName} time: ${reco
32 }
33 });
34 }
35 }

medium-flutter-logger.dart hosted with ❤ by GitHub view raw

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.

1 import 'package:dio/dio.dart';
2 import 'package:flutter/foundation.dart';
3 import 'package:logging/logging.dart';
4 import 'package:meta/meta.dart';
5 import 'package:universal_html/html.dart' as html;
6
7 ///abstract class that all service classes extend
8 abstract class AbstractHttp {
9 static const String custId = 'CustID';
10 static const String appId = 'appID';
11
12 static Dio _dio;
13
https://medium.com/flutter-community/flutter-web-for-an-enterprise-app-a056fb4e26d1 6/9
13/1/2021 Flutter Web: Some Notes. CORS (cross-origin resource sharing)… | by Vinay Shankri | Flutter Community | Dec, 2020 | Medium

14 String _baseUri;
15
16 @mustCallSuper
17 AbstractHttp() {
18 _init();
19 }
20
21 Dio get dio => _dio;
22
23 ///init common dio options
24 void _init() {
25 if (_dio == null) {
26 _dio = new Dio();
27 _dio.options.connectTimeout = 5000;
28 _dio.options.receiveTimeout = 10000;
29
30 if (_dio.interceptors.length == 0) {
31 _dio.interceptors.add(LogInterceptor(responseBody: false, logPrint: (msg) => XLo
32 _interceptor();
33 }
34 _baseUri = _prepBaseUri();
35 }
36 }
37
38 ///URL = base URI + + specific endpoint
39 String getUrl(String endpointSpecificUri) => _baseUri + endpointSpecificUri;
40
41 ///single custom rest interceptor
42 ///keeping it simple with basic implementation
43 void _interceptor() {
44 _dio.interceptors.add(InterceptorsWrapper(
45 onRequest: (RequestOptions options) async {
46 ///adding headers in DEV mode based on AppConstants
47 if (AppConstants.isDev && AppConstants.useDevHeaders) {
48 options.headers.addAll({custId: AppConstants.custId, appId: AppConstants.appId
49 }
50 return options;
51 },
52 //just got valid response from server
53 onResponse: (Response response) async => response,
54
55 ///just received error from server
56 onError: (DioError e) async => e,
57 ));
58 }
59
60 ///base URI
61 String _prepBaseUri() {
62 ///in DEV mode, devBaseUri is common for web and devices
63 if (AppConstants.isDev) {
64 ///dev URL for both web and devices
65 return AppConstants.devBaseUri;
66 } l if ( C i d k b) {
https://medium.com/flutter-community/flutter-web-for-an-enterprise-app-a056fb4e26d1 7/9
13/1/2021 Flutter Web: Some Notes. CORS (cross-origin resource sharing)… | by Vinay Shankri | Flutter Community | Dec, 2020 | Medium
66 } else if (AppConstants.isProd && kIsWeb) {
67 ///relative URL for web in production
68 return html.window.location.toString();
69 } else {
70 ///absolute URL for iOS and android devices
71 return AppConstants.prodBaseUri;
72 }
73 }
74 }

medium-flutter-xhttp.dart hosted with ❤ by GitHub view raw

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

https://medium.com/flutter-community/flutter-web-for-an-enterprise-app-a056fb4e26d1 8/9
13/1/2021 Flutter Web: Some Notes. CORS (cross-origin resource sharing)… | by Vinay Shankri | Flutter Community | Dec, 2020 | Medium

Flutter Web Flutter Flutter App Development Cors Flutter Http

About Help Legal

Get the Medium app

https://medium.com/flutter-community/flutter-web-for-an-enterprise-app-a056fb4e26d1 9/9

You might also like