Flutter Web problem deliberation

Dave Parth
Globant
Published in
9 min readJul 3, 2020

I love Flutter as mobile app developer since android is good but flutter helps me to build beautiful app in no time. And it helps me to have multi-platform support like iOS, Desktop, and Web without much of a hassle. about 2–3 months ago I have started with Flutter Web after watching many animations and support from the community. I am in love with Flutter Web also although Flutter web is still in the early phase and this post is not regarding badmouthing the technology but it’s regarding the problem I faced and the solutions I have used. If you have also faced such problems do post it in comments.

To me, the flutter web is more of a like react-native in mobile. Yes at the release time dart UI code gets converted to the javascript more or less but it’s not pure HTML CSS and so every part of UI has been manipulated by javascript DOM manipulation.

Flutter Web Architecture

Starting with the main problem which is design problems. Unlike bootstrap, flutter does not come with pre-column and row nature but you need to divide columns by using MediaQuery which is not that convenient as you grow your app. It’s difficult as changing UI without view rendered side by side, you might say that flutter has hot reload but believe me flutter web does not support hot reload that much and you might find your self stop and launch flutter web app many times. also, at development time web browser back and forth button does not work correctly and so we can not debug the content correctly. Now coming to the point

Few problems:
1. Preference widget vs using cookies for Web. — Got solution
2. How to communicate between Dart to JS and JS to Dart? — Got solution
3. Stack Widget does not work properly due to issue related rendering layer. — Still need to find a solution.
4. Flutter SVG replacement for the web. — Got solution
5. Replace Table for GridView to multi-screen size support.
6. Web File Picker
7. Web URL # not getting removed for now.
8. Run Flutter web app on macOS or Mobile end.
9. await for non-future function return — Got Solution.

1. Preference widget vs using cookies for Web?

Preference library might work here also on the web but it’s not storing data in the cookies, as it suggests it is kind of like local storage to store small amounts of data. But for auth token and some other stuff, we will be needing cookies for the web.
And that’s where JS comes into the picture. We might be able to find cookies library but we better not and the reason is the process. We will not understand how to communicate with Dart to JS and JS to Dart.

here’s small code for storing cookies using JS

function setCookies(cookieKeyName, value, utcExpireTime, isSecureOnly){
var expires = "";
if (utcExpireTime!=null) {
expires = "; expires=" + utcExpireTime;
}
var extraDetails = "";
if(isSecureOnly){
extraDetails = "Secure; HttpOnly; "
}
document.cookie = cookieKeyName + "=" + (value || "") + expires + "; path=/; "+ extraDetails;
}
function getCookies(cookieKeyName){
var nameEQ = cookieKeyName + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
function removeCookies(cookieKeyName){
document.cookie = cookieKeyName+'=; Max-Age=-99999999;';
}

If we have this code then we can just call JS function from javascript to fetch or save cookies for flutter web. and the reason being is that you might have a preference or local storage class that wraps the library class if you don’t want to place a huge burden of changing libraries and class you just need to change the data adapter which is for the web its cookies or local storage. But again you are not going to store everything in cookies right so that’s why I mentioned JS function just to store what we want at the point of time.

2. How to communicate between Dart to JS and JS to Dart?

Steps:
1. Create a Javascript file in the project/web and add this Javascript file in index.html.
2. Create dart Util file import dart: js package.
3. callMethod from js package.
4. use interop to pass a callback to javascript.

Note: Dart can not pass a function to javascript directly as it’s a different realm but what we can pass javascript interop which is kind of like wrapper around dart function so that Javascript can call this interop and ultimately it will call dart function.
We can directly use js-interop library mentioned in document but it’s easy on our end also and this way We can understand new terms and behind the scene process.

Interop is just kind of like Isolates functionality for the web.

If you don’t know what Isolates are and why it’s there do read this post:

Adding understandable gist to understand.

3. Stack Widget does not work properly due to issue related rendering layer.

I still haven’t found a solution for this but it’s worth mentioning as the issue has been already reported in the flutter repository. here
But it might take our lot of time to debug the issue. It’s more related to the widget repaint and focus issue. I was working with the iframe where I have added pdf in the back and want to add separate download button at front (don't want default toolbar for pdf embed view to popup thereon iframe) so I tried to add button in the stack layout but unfortunately click does not work even if the button is on top of iframe And I’m still trying to find the solution. will post resolution after I find some. here is a reported issue for the lib I have tried but no luck so far.

4. Flutter SVG replacement for the web

We will be needing the platform widget here for this as flutter_svg library currently does not support the web. and we can easily do this by mentioning kIsWeb condition to check if this is the web platform app.

Here we are just checking if it’s web then load the image via a network, as it will get translated to HTML image tag with the SVG path and HTML by default, supports SVG via a browser. We can also check the flutter safe web library for this.

5. Replace Table for GridView to multi-screen size support.

GridView is no doubt good but it’s troublesome when we will be having web, you might ask why it’s because to we need give AspectRatio and which is the calculations made with width vs height values. In some cases, I find it useful but here for the web I need more control not just AspectRatio what I need is wrap content kind of functionality for a row.
If we use Table Widget it’s very easy to shift between the mobile screen and web screen just by calculating the columns in the table and we can just add children like that. And believe me, it’s better to use Table when we need more handling over our width and height rather than focusing on childAspectRatio as sometimes you will find your self in that overflow red line.

6. File Picker

File picking works differently on mobile and on the web.

Issue
1. For the web, we will be having relative paths or kind of like an in-memory file. Due to which we will not be having a path and can not use multipart requests right away.
2. dart:io package does not support Flutter Web for now, and the File we picked will give us dart:html package File class.
3. So the question is what’s the difference we will use that File class Correct? Well no, We can’t use it as Multipart File uses path, and since there’s no path method you can not be able to use it. another reason is that the multipart path does not work with the HTML package it’s just used for the IO package. and there goes our dynamic object type solution to handle at the upload time.

Well we are halfway through as we understood this, so what we need
1. import dart:html package
2. fetch file
3. there’s one handy method if we have file bytes we can upload files via multipart request using HTTP package which is http.MultipartFile.fromBytes

In gist example, we have is PlatformDependentFilePicker class which will check for web or system, if it’s web then it’s going to create an input element that is important to pick a file from the system and we will click it. which will bring us the wizard to pick file and on selection done we will receive an event with file object which will be the dart:html package object. I have created custom FlutterWebFile class to store html file objects as well as bytes that we read. once we have both we will simply add these things into the HTTP package and create upload requests. I will also try to put this code in the retrofit generator and create a pull request for this. But there’s a discussion going on to add support or mock support for dart:io package for the web and I’m waiting for it.

7. Web URL # not getting removed for now.

There’s a known issue here and it’s due to main.dart.js even if you remove # from that it will still not work as flutter reads after that #/ content. but still, we need to give some leverage to URL so please read the next content carefully.

A common thing for web apps to have is the ability to change screens based on the URL. For example, going to https://app/login should give you something different than https://app/anotherpage. Flutter, in fact, supports named routes, which have two purposes:

  1. To traverse to a specific page regardless of flow like movies page in Netflix rather than going in to list and then search a movie.
  2. In any app, they allow you to predefine routes for your app and give them names and then be able to navigate to them just by specifying their name.

There are many examples for flutter web so I will not create gist for it but we need to use path matching and not a traditional style that we do in mobile to open next screen with data as in web, user can directly come from a specific link and we might not able to give support if we do that. So for every screen, you need to fetch data using web API.

here’s the link to understand this approach. If we do this then we don’t need to go back and give support for weblinks as well for mobile apps. few things and we are good to go.

8. Run Flutter web app on macOS or Mobile end.

Will write a second article for this as it’s long to understand the procedure. and the article already too long to add more code. so keep following for the next article.

9. await for non-future function return — Got Solution.

A very quick solution that might bug you in the future. What if you have a non-future function and still want to wait for the result?

void clickButton(Function callback){
//... do something
int value = 0;
//post some value after the data fetching or computing
callback(value);
}

now if this is the situation. and we want to wait for the result then what we can do is use the completer function. like this

import 'dart:async';void main() async{
Completer completer = new Completer();
clickButton((value){
print("return value from callback $value");
completer.complete(value);
});
await completer.future;
print("after completing future");
}
void clickButton(Function callback){
//... do something
int value = 0;
//post some value after the data fetching or computing
callback(value);
}

Here we are calling click button function and want to wait for the result inside the callback. Completer function gives us the future and this future will get completed once we call completer.complete function. This way we can await the result of the callback function.
Console output:

return value from callback 0 
after completing future

There’s no technology that comes with perfection, but the flutter web is promising. I do love to solve those problems even though it might take days of mine. And there will be many problems like this so feel free to drop problems and your solutions in comments. Also do not forget to comment on your takeaways from this post, I would love to hear from you.

Hope you liked it.

Do you know you can press the clap👏 button 50 times? The higher you go, the more it motivates me to write more stuff!

Hello, I am Parth Dave. A noob developer and noob photographer. You can find me on Linkedin or stalk me on GitHub or maybe follow me on Twitter?

Have a nice fluttery day!

--

--

Dave Parth
Globant
Writer for

Developer, Photographer. Flutter and Automation enthusiast.