How Flutter Web Ensures Users Receive Updates
flutter doctor -v
Flutter assets will be downloaded from https://storage.flutter-io.cn. Make sure you trust this source!
[√] Flutter (Channel stable, 3.41.4, on Microsoft Windows [Version 10.0.26200.8039], locale zh-CN) [796ms]
• Flutter version 3.41.4 on channel stable at D:\flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision ff37bef603 (3 weeks ago), 2026-03-03 16:03:22 -0800
• Engine revision e4b8dca3f1
• Dart version 3.11.1
• DevTools version 2.54.1
• Pub download mirror https://pub.flutter-io.cn
• Flutter download mirror https://storage.flutter-io.cn
• Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android,
enable-ios, cli-animations, enable-native-assets, omit-legacy-version-file, enable-lldb-debugging,
enable-uiscene-migration
In older versions, Flutter Web controlled version updates using file hashing. It would fetch the latest JavaScript file whenever a hash mismatch was detected. However, this approach has been deprecated recently, and the main.dart.js file no longer has a hash suffix.
As a result, the client cannot determine the version of main.dart.js. With caching enabled, users may fail to update to the latest version until the cache expires or a different ETag is detected. Additionally, main.dart.js is quite large, so frequent downloads significantly degrade the user experience.
The simplest way to control the main.dart.js version is to append version information when requesting it, like main.dart.js?version=1.2.3. So how can we implement this?
If you ask an AI, it might tell you to modify the entrypointUrl parameter in the loadEntrypoint method in web/flutter_bootstrap.js:
entrypointUrl: '/main.dart.js?version=1.2.3',
In reality, this parameter is already deprecated, and incidentally, the loadEntrypoint method itself is also deprecated. Older tutorials used this function to customize Flutter Web initialization.
/**
* @deprecated
* Loads flutter main entrypoint, specified by `entrypointUrl`, and calls a
* user-specified `onEntrypointLoaded` callback with an EngineInitializer
* object when it's done.
*
* @param {*} options
* @returns {Promise | undefined} that will eventually resolve with an
* EngineInitializer, or will be rejected with the error caused by the loader.
* Returns undefined when an `onEntrypointLoaded` callback is supplied in `options`.
*/
async loadEntrypoint(options) {
const { entrypointUrl = resolveUrlWithSegments("main.dart.js"), onEntrypointLoaded, nonce } =
options || {};
return this._loadJSEntrypoint(entrypointUrl, onEntrypointLoaded, nonce);
}
In the current version, Flutter Web uses the _flutter.loader.load method for initialization. However, the new method does not have a parameter equivalent to entrypointUrl (be careful not to confuse it with entrypointBaseUrl).
We can see that Flutter’s actual logic for fetching main.dart.js is as follows:
const mainPath = build.mainJsPath ?? "main.dart.js";
const entrypointUrl = resolveUrlWithSegments(entrypointBaseUrl, mainPath);
return this._loadJSEntrypoint(entrypointUrl, onEntrypointLoaded, nonce);
We just need to manually modify the value of build.mainJsPath in web/flutter_bootstrap.js:
_flutter.buildConfig.builds[0].mainJsPath = 'main.dart.js?version=1.2.3';
How can we automate this version number change? Flutter Web actually has built-in template code support. First, update the code above to:
_flutter.buildConfig.builds[0].mainJsPath = 'main.dart.js?version={{version}}';
Then modify the build command to:
flutter build web --web-define=version=1.2.3
The {{version}} placeholder will be automatically replaced with the actual version number.