Optimizing Flutter Web App Initial Load Times

218 Views Asked by At

Flutter Web apps often face slow initial load times. While this topic has been discussed, the technology has changed and so are the best practices.

Goal: To create a comprehensive resource for maximizing Flutter Web initial load performance. I'm seeking actionable tips and tricks from developers who have achieved close to "green" scores on Google PageSpeed Insights.

Let's Build the Ultimate Flutter Web Loading Optimization Guide! Every trick and tips are welcome no matter how small or insignificant you think they are.

1

There are 1 best solutions below

0
Aks On

There are few steps to make flutter web initial load performance better.

  1. Add a splash in your "index.html". This will remove the blank screen which occurs at initial startup of flutter web app. It will also detect as "First Contentful Paint" by page speed insights. For adding images in splash we need to add images in multiple sizes (1x, 2x, 3x, 4x).

  2. User "defer" with "script" tag. Adding "defer" means the scripts will be downloaded in parallel, but they will be executed in the order they appear in the HTML document. This means that if you have several scripts with defer, they won't block the parsing of the HTML or the rendering of the webpage, but they will still execute in the sequence they're written in the HTML file.

  3. Use "preload" for "main.dart.js" and "images" (if images are used in index.html). By using "preload", you're instructing the browser to start fetching "main.dart.js" as soon as possible, even before it reaches the point in the HTML where it's actually needed. This can help improve performance by allowing the browser to preload the script while it's still processing other parts of the page, reducing the overall load time.

    <link rel="preload" href="main.dart.js" as="script">
    <link rel="preload" href="splash/img/light-1x.png" as="image">
    

Here's an example of an index.html file incorporating "splash", "defer", and "preload" attributes:

<!DOCTYPE html>
<html>

<head>
  <base href="$FLUTTER_BASE_HREF">

  <meta charset="UTF-8">
  <meta content="IE=Edge" http-equiv="X-UA-Compatible">
  <meta name="description" content="A new Flutter project.">

  <!-- iOS meta tags & icons -->
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="apple-mobile-web-app-title" content="flutter_web">
  <link rel="apple-touch-icon" href="icons/Icon-192.png">

  <!-- Favicon -->
  <link rel="icon" type="image/png" href="favicon.png" />

  <title>flutter_web</title>
  <link rel="manifest" href="manifest.json">

  <link rel="preload" href="main.dart.js" as="script">
  <link rel="preload" href="splash/img/light-1x.png" as="image">

  <script>
    // The value below is injected by flutter build, do not touch.
    const serviceWorkerVersion = null;
  </script>
  <!-- This script adds the flutter initialization JS code -->
  <script src="flutter.js" defer></script>


  <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
  <style id="splash-screen-style">
    html {
      height: 100%
    }
    body {
      margin: 0;
      min-height: 100%;
      background-color: #ffffff;
      background-size: 100% 100%;
    }

    .center {
      margin: 0;
      position: absolute;
      top: 50%;
      left: 50%;
      -ms-transform: translate(-50%, -50%);
      transform: translate(-50%, -50%);
    }

    .contain {
      display: block;
      width: 100%;
      height: 100%;
      object-fit: contain;
    }

    .stretch {
      display: block;
      width: 100%;
      height: 100%;
    }

    .cover {
      display: block;
      width: 100%;
      height: 100%;
      object-fit: cover;
    }

    .bottom {
      position: absolute;
      bottom: 0;
      left: 50%;
      -ms-transform: translate(-50%, 0);
      transform: translate(-50%, 0);
    }

    .bottomLeft {
      position: absolute;
      bottom: 0;
      left: 0;
    }

    .bottomRight {
      position: absolute;
      bottom: 0;
      right: 0;
    }
  </style>

</head>

<body>
  <!-- showing image at center during splash -->
  <picture id="splash">
    <source
      srcset="splash/img/light-1x.png 1x, splash/img/light-2x.png 2x, splash/img/light-3x.png 3x, splash/img/light-4x.png 4x"
      media="(prefers-color-scheme: light)">
    <source
      srcset="splash/img/dark-1x.png 1x, splash/img/dark-2x.png 2x, splash/img/dark-3x.png 3x, splash/img/dark-4x.png 4x"
      media="(prefers-color-scheme: dark)">
    <img class="center" aria-hidden="true" src="splash/img/light-1x.png" alt="">
  </picture>

  <script defer>
    // Import the functions you need from the SDKs you need
    import { initializeApp } from "firebase/app";
    import { firebaseConfig } from ".firebase_json";
    // TODO: Add SDKs for Firebase products that you want to use
    // https://firebase.google.com/docs/web/setup#available-libraries

    // Initialize Firebase
    const app = initializeApp(firebaseConfig);
  </script>
  <script defer>
    window.addEventListener('load', function (ev) {
      // Download main.dart.js
      _flutter.loader.loadEntrypoint({
        serviceWorker: {
          serviceWorkerVersion: serviceWorkerVersion,
        },
        onEntrypointLoaded: function (engineInitializer) {
          engineInitializer.initializeEngine().then(function (appRunner) {
            appRunner.runApp();
          });
        }
      });
    });
  </script>
</body>

</html>

At this moment, we observe that the splash screen remains visible for a while before transitioning to the home page. However, an issue arises where the splash screen is obscured by the Scaffold(). This can be verified by setting the Scaffold's background color to Colors.transparent or by right-clicking at the center of the screen where the splash icon was displayed.

To resolve this issue, we can utilize the flutter_native_splash package. Simply add the package to your project, and within the void main() function, implement the following changes.

void main(){

  WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();

  FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);

  FlutterNativeSplash.remove();

}