Alternatively, you can inline the entire contents using a template token:

        

Custom Initialization

To customize the initialization process, you can create your own flutter_bootstrap.js file in the web subdirectory of your project. This file can use several special tokens that the build step will substitute:

A basic custom flutter_bootstrap.js might look like this:

{{flutter_js}}{{flutter_build_config}}_flutter.loader.load();

2. Optimizing Renderer Selection

Flutter web can use either the HTML or CanvasKit renderer. The CanvasKit renderer offers better performance for complex UIs but has a larger initial download size. You can customize the renderer selection based on various factors:

const searchParams = new URLSearchParams(window.location.search);const renderer = searchParams.get(\\'renderer\\');const userConfig = {  renderer: renderer || \\'\\',  canvasKitVariant: \\'auto\\',  canvasKitMaximumSurfaces: getCanvasKitMaximumSurfaces(),};_flutter.loader.load({  config: userConfig,});

CanvasKit Surfaces

The canvasKitMaximumSurfaces option is particularly important for performance. It determines the maximum number of overlay surfaces that the CanvasKit renderer can use.

From the Flutter documentation:

\\\"The maximum number of overlay surfaces that the CanvasKit renderer can use.\\\"

The optimal number of surfaces depends on the device\\'s capabilities. Here\\'s an example function to determine this:

function getCanvasKitMaximumSurfaces() {  const memory = navigator.deviceMemory || 4;  const cpuCores = navigator.hardwareConcurrency || 2;  if (memory <= 2 || cpuCores <= 2) {    return 2; // Low-end device  } else if (memory >= 8 && cpuCores >= 6) {    return 8; // High-end device  } else {    return 4; // Medium-range device  }}

3. Implementing a Landing Page and Lazy Loading

To improve perceived load time and provide a better user experience, implement a landing page that shows while your Flutter app initializes. This approach involves creating a simple HTML/CSS/JavaScript landing page that is displayed immediately, while the Flutter app loads in the background.

HTML Structure

First, update your index.html file to include the landing page structure:

    Your Flutter Web App    

Welcome to Your App

Loading awesome content...

CSS Styling

Create a styles.css file in your web directory:

body, html {  height: 100%;  margin: 0;  display: flex;  justify-content: center;  align-items: center;  font-family: Arial, sans-serif;  background-color: #f0f0f0;}#landing-page {  text-align: center;}.loader {  border: 5px solid #f3f3f3;  border-top: 5px solid #3498db;  border-radius: 50%;  width: 50px;  height: 50px;  animation: spin 1s linear infinite;  margin: 20px auto;}@keyframes spin {  0% { transform: rotate(0deg); }  100% { transform: rotate(360deg); }}#start-app-btn {  padding: 10px 20px;  font-size: 16px;  background-color: #4CAF50;  color: white;  border: none;  border-radius: 5px;  cursor: pointer;}#start-app-btn:hover {  background-color: #45a049;}

JavaScript for Landing Page

Create a landing-page.js file in your web directory:

let engineInitialized = false;let appStarted = false;document.addEventListener(\\'DOMContentLoaded\\', function() {  const startAppBtn = document.getElementById(\\'start-app-btn\\');  startAppBtn.addEventListener(\\'click\\', function() {    if (engineInitialized && !appStarted) {      startApp();    }  });});function showStartButton() {  const startAppBtn = document.getElementById(\\'start-app-btn\\');  startAppBtn.style.display = \\'inline-block\\';}function hideLoadingIndicator() {  const loader = document.querySelector(\\'.loader\\');  if (loader) {    loader.style.display = \\'none\\';  }}function hideLandingPage() {  const landingPage = document.getElementById(\\'landing-page\\');  landingPage.style.display = \\'none\\';}window.addEventListener(\\'flutter-initialized\\', function() {  engineInitialized = true;  hideLoadingIndicator();  showStartButton();});function startApp() {  appStarted = true;  hideLandingPage();  // Add any additional logic needed to start your Flutter app}

Customizing flutter_bootstrap.js

Update your flutter_bootstrap.js to work with the landing page:

{{flutter_js}}{{flutter_build_config}}_flutter.loader.load({  onEntrypointLoaded: async function(engineInitializer) {    let appRunner = await engineInitializer.initializeEngine();    await appRunner.runApp();    window.dispatchEvent(new Event(\\'flutter-initialized\\'));  }});

This setup creates a simple landing page that displays while your Flutter app is loading. Once the Flutter engine is initialized, the \\\"Start App\\\" button appears, allowing the user to launch the app when ready. This approach improves perceived performance and gives you control over when to transition from the landing page to the Flutter app.

To lazy load your Flutter app, you can further customize the flutter_bootstrap.js file to delay the initialization until user interaction or other conditions are met.

4. Utilizing the onEntrypointLoaded Callback

The onEntrypointLoaded callback allows you to perform custom logic at different stages of the initialization process:

_flutter.loader.load({  onEntrypointLoaded: async function(engineInitializer) {    updateLoadingStatus(\\\"Initializing engine...\\\");    const appRunner = await engineInitializer.initializeEngine();    updateLoadingStatus(\\\"Running app...\\\");    await appRunner.runApp();  }});function updateLoadingStatus(status) {  const loadingElement = document.getElementById(\\'loading-status\\');  if (loadingElement) {    loadingElement.textContent = status;  }}

5. Optimizing Asset Delivery and Caching

Ensure that your assets are optimized for web delivery:

Implement effective caching strategies using service workers:

_flutter.loader.load({  serviceWorkerSettings: {    serviceWorkerVersion: {{flutter_service_worker_version}},  },});

6. Preloading Essential Assets

Preload essential assets to start downloading them as soon as possible:

const preloadAssets = [  \\'/flutter.js\\',  \\'/main.dart.js\\',  \\'assets/fonts/Roboto-Regular.ttf\\'];preloadAssets.forEach(asset => {  const link = document.createElement(\\'link\\');  link.rel = \\'preload\\';  link.href = asset;  link.as = asset.endsWith(\\'.js\\') ? \\'script\\' :              (asset.endsWith(\\'.ttf\\') ? \\'font\\' : \\'fetch\\');  link.crossOrigin = \\'anonymous\\';  document.head.appendChild(link);});

7. Error Handling and Timeouts

Implement error handling and timeouts to prevent indefinite loading states:

const initPromise = _flutter.loader.load(/* config */);const timeoutPromise = new Promise((_, reject) =>  setTimeout(() => reject(new Error(\\'Initialization timed out\\')), 30000));Promise.race([initPromise, timeoutPromise])  .catch(error => {    console.error(\\'Initialization failed:\\', error);    showErrorMessage(\\'Initialization failed. Please refresh and try again.\\');  });

8. Full Example: Modern Counter App

Before we conclude, let\\'s look at a full example of a Flutter web app that implements some of the optimization techniques we\\'ve discussed. This example showcases a modern counter app with animations and a gradient background.

import \\'package:flutter/material.dart\\';void main() {  runApp(const MyApp());}class MyApp extends StatelessWidget {  const MyApp({Key? key}) : super(key: key);  @override  Widget build(BuildContext context) {    return MaterialApp(      title: \\'Modern Counter\\',      theme: ThemeData(        primarySwatch: Colors.teal,        brightness: Brightness.dark,        fontFamily: \\'Montserrat\\',      ),      home: const MyHomePage(),    );  }}class MyHomePage extends StatefulWidget {  const MyHomePage({Key? key}) : super(key: key);  @override  State createState() => _MyHomePageState();}class _MyHomePageState extends State with SingleTickerProviderStateMixin {  int _counter = 0;  late AnimationController _controller;  late Animation _animation;  @override  void initState() {    super.initState();    _controller = AnimationController(      duration: const Duration(milliseconds: 300),      vsync: this,    );    _animation = Tween(begin: 1, end: 1.2).animate(      CurvedAnimation(parent: _controller, curve: Curves.easeInOut),    );  }  void _incrementCounter() {    setState(() {      _counter  ;    });    _controller.forward().then((_) => _controller.reverse());  }  @override  void dispose() {    _controller.dispose();    super.dispose();  }  @override  Widget build(BuildContext context) {    return Scaffold(      body: Container(        decoration: BoxDecoration(          gradient: LinearGradient(            begin: Alignment.topLeft,            end: Alignment.bottomRight,            colors: [Colors.teal.shade800, Colors.teal.shade200],          ),        ),        child: SafeArea(          child: Center(            child: Column(              mainAxisAlignment: MainAxisAlignment.center,              children: [                const Text(                  \\'Modern Counter\\',                  style: TextStyle(                    fontSize: 32,                    fontWeight: FontWeight.bold,                    color: Colors.white,                  ),                ),                const SizedBox(height: 40),                ScaleTransition(                  scale: _animation,                  child: Container(                    padding: const EdgeInsets.all(20),                    decoration: BoxDecoration(                      color: Colors.white.withOpacity(0.2),                      borderRadius: BorderRadius.circular(20),                    ),                    child: Text(                      \\'$_counter\\',                      style: const TextStyle(                        fontSize: 72,                        fontWeight: FontWeight.bold,                        color: Colors.white,                      ),                    ),                  ),                ),                const SizedBox(height: 40),                ElevatedButton(                  onPressed: _incrementCounter,                  style: ElevatedButton.styleFrom(                    foregroundColor: Colors.teal,                    backgroundColor: Colors.white,                    padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 15),                    shape: RoundedRectangleBorder(                      borderRadius: BorderRadius.circular(30),                    ),                  ),                  child: const Text(                    \\'INCREMENT\\',                    style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),                  ),                ),              ],            ),          ),        ),      ),    );  }}

This example demonstrates a modern UI with animations, which can benefit from the optimization techniques we\\'ve discussed, such as choosing the appropriate renderer and optimizing asset delivery.

For a more in-depth exploration of this project, including the full directory structure and any additional optimizations, you can find the complete repository at: https://github.com/samuelkchris/initial_load

Conclusion

Optimizing Flutter web\\'s initial load time requires a multi-faceted approach. By leveraging Flutter\\'s new initialization APIs, customizing renderer selection, implementing effective loading strategies, and following web performance best practices, you can significantly improve your app\\'s initial load time and overall user experience.

Remember to regularly test your app\\'s performance using tools like Lighthouse or WebPageTest, and stay updated with the latest Flutter web optimizations and best practices.

","image":"http://www.luping.net/uploads/20241015/1728965176670dea385a4c3.jpg","datePublished":"2024-11-08T16:10:25+08:00","dateModified":"2024-11-08T16:10:25+08:00","author":{"@type":"Person","name":"luping.net","url":"https://www.luping.net/articlelist/0_1.html"}}
"Se um trabalhador quiser fazer bem o seu trabalho, ele deve primeiro afiar suas ferramentas." - Confúcio, "Os Analectos de Confúcio. Lu Linggong"
Primeira página > Programação > Otimizando o tempo de carregamento inicial do Flutter Web: um guia abrangente e atualizado

Otimizando o tempo de carregamento inicial do Flutter Web: um guia abrangente e atualizado

Publicado em 2024-11-08
Navegar:511

Optimizing Flutter Web

Flutter web applications can sometimes suffer from slow initial load times, which can negatively impact user experience and engagement. This updated article explores various techniques to optimize your Flutter web app's initial load time, based on best practices, official Flutter documentation, and real-world code examples.

1. Customizing Web App Initialization

Flutter 3.22 and later versions provide enhanced APIs for customizing web app initialization. The key to this process is the flutter_bootstrap.js file.

The flutter_bootstrap.js File

When you build your Flutter web app, the flutter build web command generates a flutter_bootstrap.js file in the build/web directory. This script contains the necessary JavaScript code to initialize and run your Flutter app.

You can include this script in your index.html file:

  
    
  

Alternatively, you can inline the entire contents using a template token:

  
    
  

Custom Initialization

To customize the initialization process, you can create your own flutter_bootstrap.js file in the web subdirectory of your project. This file can use several special tokens that the build step will substitute:

  • {{flutter_js}}: Makes the FlutterLoader object available.
  • {{flutter_build_config}}: Sets metadata produced by the build process.
  • {{flutter_service_worker_version}}: Provides a unique number for the service worker version.

A basic custom flutter_bootstrap.js might look like this:

{{flutter_js}}
{{flutter_build_config}}

_flutter.loader.load();

2. Optimizing Renderer Selection

Flutter web can use either the HTML or CanvasKit renderer. The CanvasKit renderer offers better performance for complex UIs but has a larger initial download size. You can customize the renderer selection based on various factors:

const searchParams = new URLSearchParams(window.location.search);
const renderer = searchParams.get('renderer');
const userConfig = {
  renderer: renderer || '',
  canvasKitVariant: 'auto',
  canvasKitMaximumSurfaces: getCanvasKitMaximumSurfaces(),
};

_flutter.loader.load({
  config: userConfig,
});

CanvasKit Surfaces

The canvasKitMaximumSurfaces option is particularly important for performance. It determines the maximum number of overlay surfaces that the CanvasKit renderer can use.

From the Flutter documentation:

"The maximum number of overlay surfaces that the CanvasKit renderer can use."

The optimal number of surfaces depends on the device's capabilities. Here's an example function to determine this:

function getCanvasKitMaximumSurfaces() {
  const memory = navigator.deviceMemory || 4;
  const cpuCores = navigator.hardwareConcurrency || 2;

  if (memory = 8 && cpuCores >= 6) {
    return 8; // High-end device
  } else {
    return 4; // Medium-range device
  }
}

3. Implementing a Landing Page and Lazy Loading

To improve perceived load time and provide a better user experience, implement a landing page that shows while your Flutter app initializes. This approach involves creating a simple HTML/CSS/JavaScript landing page that is displayed immediately, while the Flutter app loads in the background.

HTML Structure

First, update your index.html file to include the landing page structure:



  
  Your Flutter Web App
  


  

Welcome to Your App

Loading awesome content...

CSS Styling

Create a styles.css file in your web directory:

body, html {
  height: 100%;
  margin: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  font-family: Arial, sans-serif;
  background-color: #f0f0f0;
}

#landing-page {
  text-align: center;
}

.loader {
  border: 5px solid #f3f3f3;
  border-top: 5px solid #3498db;
  border-radius: 50%;
  width: 50px;
  height: 50px;
  animation: spin 1s linear infinite;
  margin: 20px auto;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

#start-app-btn {
  padding: 10px 20px;
  font-size: 16px;
  background-color: #4CAF50;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}

#start-app-btn:hover {
  background-color: #45a049;
}

JavaScript for Landing Page

Create a landing-page.js file in your web directory:

let engineInitialized = false;
let appStarted = false;

document.addEventListener('DOMContentLoaded', function() {
  const startAppBtn = document.getElementById('start-app-btn');

  startAppBtn.addEventListener('click', function() {
    if (engineInitialized && !appStarted) {
      startApp();
    }
  });
});

function showStartButton() {
  const startAppBtn = document.getElementById('start-app-btn');
  startAppBtn.style.display = 'inline-block';
}

function hideLoadingIndicator() {
  const loader = document.querySelector('.loader');
  if (loader) {
    loader.style.display = 'none';
  }
}

function hideLandingPage() {
  const landingPage = document.getElementById('landing-page');
  landingPage.style.display = 'none';
}

window.addEventListener('flutter-initialized', function() {
  engineInitialized = true;
  hideLoadingIndicator();
  showStartButton();
});

function startApp() {
  appStarted = true;
  hideLandingPage();
  // Add any additional logic needed to start your Flutter app
}

Customizing flutter_bootstrap.js

Update your flutter_bootstrap.js to work with the landing page:

{{flutter_js}}
{{flutter_build_config}}

_flutter.loader.load({
  onEntrypointLoaded: async function(engineInitializer) {
    let appRunner = await engineInitializer.initializeEngine();
    await appRunner.runApp();
    window.dispatchEvent(new Event('flutter-initialized'));
  }
});

This setup creates a simple landing page that displays while your Flutter app is loading. Once the Flutter engine is initialized, the "Start App" button appears, allowing the user to launch the app when ready. This approach improves perceived performance and gives you control over when to transition from the landing page to the Flutter app.

To lazy load your Flutter app, you can further customize the flutter_bootstrap.js file to delay the initialization until user interaction or other conditions are met.

4. Utilizing the onEntrypointLoaded Callback

The onEntrypointLoaded callback allows you to perform custom logic at different stages of the initialization process:

_flutter.loader.load({
  onEntrypointLoaded: async function(engineInitializer) {
    updateLoadingStatus("Initializing engine...");
    const appRunner = await engineInitializer.initializeEngine();

    updateLoadingStatus("Running app...");
    await appRunner.runApp();
  }
});

function updateLoadingStatus(status) {
  const loadingElement = document.getElementById('loading-status');
  if (loadingElement) {
    loadingElement.textContent = status;
  }
}

5. Optimizing Asset Delivery and Caching

Ensure that your assets are optimized for web delivery:

  • Compress images and use appropriate formats (e.g., WebP).
  • Minify JavaScript and CSS files.
  • Use gzip or Brotli compression on your server.

Implement effective caching strategies using service workers:

_flutter.loader.load({
  serviceWorkerSettings: {
    serviceWorkerVersion: {{flutter_service_worker_version}},
  },
});

6. Preloading Essential Assets

Preload essential assets to start downloading them as soon as possible:

const preloadAssets = [
  '/flutter.js',
  '/main.dart.js',
  'assets/fonts/Roboto-Regular.ttf'
];

preloadAssets.forEach(asset => {
  const link = document.createElement('link');
  link.rel = 'preload';
  link.href = asset;
  link.as = asset.endsWith('.js') ? 'script' : 
             (asset.endsWith('.ttf') ? 'font' : 'fetch');
  link.crossOrigin = 'anonymous';
  document.head.appendChild(link);
});

7. Error Handling and Timeouts

Implement error handling and timeouts to prevent indefinite loading states:

const initPromise = _flutter.loader.load(/* config */);
const timeoutPromise = new Promise((_, reject) =>
  setTimeout(() => reject(new Error('Initialization timed out')), 30000)
);

Promise.race([initPromise, timeoutPromise])
  .catch(error => {
    console.error('Initialization failed:', error);
    showErrorMessage('Initialization failed. Please refresh and try again.');
  });

8. Full Example: Modern Counter App

Before we conclude, let's look at a full example of a Flutter web app that implements some of the optimization techniques we've discussed. This example showcases a modern counter app with animations and a gradient background.

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Modern Counter',
      theme: ThemeData(
        primarySwatch: Colors.teal,
        brightness: Brightness.dark,
        fontFamily: 'Montserrat',
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State createState() => _MyHomePageState();
}

class _MyHomePageState extends State with SingleTickerProviderStateMixin {
  int _counter = 0;
  late AnimationController _controller;
  late Animation _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(milliseconds: 300),
      vsync: this,
    );
    _animation = Tween(begin: 1, end: 1.2).animate(
      CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
    );
  }

  void _incrementCounter() {
    setState(() {
      _counter  ;
    });
    _controller.forward().then((_) => _controller.reverse());
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        decoration: BoxDecoration(
          gradient: LinearGradient(
            begin: Alignment.topLeft,
            end: Alignment.bottomRight,
            colors: [Colors.teal.shade800, Colors.teal.shade200],
          ),
        ),
        child: SafeArea(
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                const Text(
                  'Modern Counter',
                  style: TextStyle(
                    fontSize: 32,
                    fontWeight: FontWeight.bold,
                    color: Colors.white,
                  ),
                ),
                const SizedBox(height: 40),
                ScaleTransition(
                  scale: _animation,
                  child: Container(
                    padding: const EdgeInsets.all(20),
                    decoration: BoxDecoration(
                      color: Colors.white.withOpacity(0.2),
                      borderRadius: BorderRadius.circular(20),
                    ),
                    child: Text(
                      '$_counter',
                      style: const TextStyle(
                        fontSize: 72,
                        fontWeight: FontWeight.bold,
                        color: Colors.white,
                      ),
                    ),
                  ),
                ),
                const SizedBox(height: 40),
                ElevatedButton(
                  onPressed: _incrementCounter,
                  style: ElevatedButton.styleFrom(
                    foregroundColor: Colors.teal,
                    backgroundColor: Colors.white,
                    padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 15),
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(30),
                    ),
                  ),
                  child: const Text(
                    'INCREMENT',
                    style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

This example demonstrates a modern UI with animations, which can benefit from the optimization techniques we've discussed, such as choosing the appropriate renderer and optimizing asset delivery.

For a more in-depth exploration of this project, including the full directory structure and any additional optimizations, you can find the complete repository at: https://github.com/samuelkchris/initial_load

Conclusion

Optimizing Flutter web's initial load time requires a multi-faceted approach. By leveraging Flutter's new initialization APIs, customizing renderer selection, implementing effective loading strategies, and following web performance best practices, you can significantly improve your app's initial load time and overall user experience.

Remember to regularly test your app's performance using tools like Lighthouse or WebPageTest, and stay updated with the latest Flutter web optimizations and best practices.

Declaração de lançamento Este artigo foi reproduzido em: https://dev.to/samuelkchris/optimizing-flutter-webs-initial-load-time-an-updated-comprehensive-guide-4j84?1 Se houver alguma violação, entre em contato com study_golang@163 .com para excluí-lo
Tutorial mais recente Mais>

Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.

Copyright© 2022 湘ICP备2022001581号-3