Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flutter web - Need documentation on how to conditionally display a "Refresh. New Version is available." prompt when updated code is deployed to production. #104509

Open
Zelfapp opened this issue May 24, 2022 · 26 comments
Labels
d: api docs Issues with https://api.flutter.dev/ P3 Issues that are less important to the Flutter project platform-web Web applications specifically team-web Owned by Web platform team triaged-web Triaged by Web platform team

Comments

@Zelfapp
Copy link

Zelfapp commented May 24, 2022

We need a foolproof way to be able to force a download of main.dart.js when code is updated and deployed to web live. E.g. if a user is on my flutter website and I deploy a new build and they click to a new screen, they should see new code updates without having to refresh their browser, or in the service worker js in web/index.html, be able to conditionally display a prompt to the user to "Refresh. New Version is available".

Google Messenger on web and other web interfaces are able to detect new code is deployed without a browser refresh and a "Refresh for new version" prompt is displayed. We need this ability via the service worker js code in web/index.html.

In typical html sites the foolproof way to force js/css updates, while a user is actively on your site, is to change the js/css filename reference, which forces a download of the new js/css code immediately, when the user navigates to any new screen on your site. This pattern does not require a prompt at all, but if we can't do that in flutter web, having the ability to display a refresh now prompt would be just fine.

This is a serious issue, for obvious reasons. If users are not getting updated code after a build and deploy to live hosting and we have to hope that users will press their browser cache refresh button, at some point, then there will be all sorts of problems.

It would be great for this behavior in web/index.html to occur by simply changing the version number in pubspec.yaml, and the flutter build web and deploy to hosting would guarantee the user would get new code base when they navigate to a new screen or a refresh prompt.

This is web/index.html

var serviceWorkerVersion = null; after running flutter build web does get a new version number on each build, but this is not forcing the serviceWorker to refresh main.dart.js where all code updates should be after a new build and deploy to live hosting. Only a browser refresh where the user is required to press the browser refresh button or clear the cache does main.dart.js update with new deployed code.

The console.log('Installed new service worker.'); only runs when you do a full browser refresh. This never runs after a deploy with new code to hosting without a full browser refresh. E.g. if you deploy new code to hosting and then click around without a browser refresh it will not log a new service worker is installed.

<script>
  var serviceWorkerVersion = null;
  var scriptLoaded = false;
  function loadMainDartJs() {
    if (scriptLoaded) {
      return;
    }
    scriptLoaded = true;
    var scriptTag = document.createElement('script');
    scriptTag.src = 'main.dart.js';
    scriptTag.type = 'application/javascript';
    document.body.append(scriptTag);
  }

  if ('serviceWorker' in navigator) {
    // Service workers are supported. Use them.
    window.addEventListener('load', function () {
      // Wait for registration to finish before dropping the <script> tag.
      // Otherwise, the browser will load the script multiple times,
      // potentially different versions.
      var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
      navigator.serviceWorker.register(serviceWorkerUrl)
        .then((reg) => {
          function waitForActivation(serviceWorker) {
            serviceWorker.addEventListener('statechange', () => {
              if (serviceWorker.state == 'activated') {
                console.log('Installed new service worker.');
                loadMainDartJs();
              }
            });
          }
          if (!reg.active && (reg.installing || reg.waiting)) {
            // No active web worker and we have installed or are installing
            // one for the first time. Simply wait for it to activate.
            waitForActivation(reg.installing || reg.waiting);
          } else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
            // When the app updates the serviceWorkerVersion changes, so we
            // need to ask the service worker to update.
            console.log('New service worker available.');
            reg.update();
            waitForActivation(reg.installing);
          } else {
            // Existing service worker is still good.
            console.log('Loading app from service worker.');
            loadMainDartJs();
          }
        });

      // If service worker doesn't succeed in a reasonable amount of time,
      // fallback to plaint <script> tag.
      setTimeout(() => {
        if (!scriptLoaded) {
          console.warn(
            'Failed to load app from service worker. Falling back to plain <script> tag.',
          );
          loadMainDartJs();
        }
      }, 4000);
    });
  } else {
    // Service workers not supported. Just drop the <script> tag.
    loadMainDartJs();
  }
</script>

Logs

flutter doctor -v
[✓] Flutter (Channel stable, 3.0.1, on Manjaro Linux 5.4.192-1-MANJARO, locale en_US.UTF-8)
    • Flutter version 3.0.1 at /opt/flutter
    • Upstream repository https://github.com/flutter/flutter.gitFramework revision fb57da5f94 (5 days ago), 2022-05-19 15:50:29 -0700Engine revision caaafc5604
    • Dart version 2.17.1DevTools version 2.12.2

[✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0-rc2)
    • Android SDK at /home/nate/Android/SdkPlatform android-31, build-tools 31.0.0-rc2
    • ANDROID_HOME = /opt/android-sdk
    • ANDROID_SDK_ROOT = /home/nate/Android/SdkJava binary at: /opt/android-studio/jre/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)
    • All Android licenses accepted.

[✓] Chrome - develop for the web
    • CHROME_EXECUTABLE = /usr/bin/google-chrome-stable

[✓] Linux toolchain - develop for Linux desktop
    • clang version 13.0.1
    • cmake version 3.23.1
    • ninja version 1.10.2
    • pkg-config version 1.8.0

[✓] Android Studio (version 2021.2)
    • Android Studio at /opt/android-studio
    • Flutter plugin version 67.1.2Dart plugin version 212.5744Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)

[✓] Connected device (3 available)
    • sdk gphone x86 64 arm64 (mobile) • emulator-5554 • android-x64    • Android 11 (API 30) (emulator)
    • Linux (desktop)                  • linux         • linux-x64      • Manjaro Linux 5.4.192-1-MANJAROChrome (web)                     • chrome        • web-javascript • Google Chrome 101.0.4951.64

[✓] HTTP Host AvailabilityAll required HTTP hosts are available

• No issues found!

Note: I use firebase hosting. I tried adding the following headers to my firebase.json to prevent caching, but neither header forces a refresh of code updates deployed live.

"headers": [
  { "source":"/main.dart.js", "headers": [{"key": "Cache-Control", "value": "no-cache"}] }
]

"headers": [
  { "source":"/service-worker.js", "headers": [{"key": "Cache-Control", "value": "no-cache"}] }
]
@darshankawar darshankawar added the in triage Presently being triaged by the triage team label May 25, 2022
@darshankawar
Copy link
Member

@Zelfapp
Thanks for the report. Can you take a look at this similar issue and specifically this issue comment and check if it helps to answer your question ?

@darshankawar darshankawar added the waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds label May 25, 2022
@Zelfapp
Copy link
Author

Zelfapp commented May 25, 2022

Nothing is jumping out to me on either of those threads and I had visited those before posting my report. It would be great to be able to display a "new version refresh now" prompt. That is exactly what I'm looking for.

@darshankawar unless I'm missing something, this condition is never met without a browser refresh to see that there is new code deployed. I have been testing this and feel pretty confident this condition is never caught without a browser refresh. To test, I've been deploying code updates to web hosting, and clicking around my site watching the console logs, but this condition is never caught clicking around the site. It is only caught when there is a browser refresh, so displaying a prompt when this condition is caught is pointless because the browser has already been refreshed to hit this condition.

if (serviceWorker.state == 'activated') {
  // This does not run without a browser refresh, so newly deployed code will not be downloaded.
  console.log('Installed new service worker.');
  loadMainDartJs();
          // if there's an existing controller (previous Service Worker), show the prompt 
        // you can tweak this and delay the notification once the page is load you can show a notification and ask for a new refresh
            if (navigator.serviceWorker.controller) {
              // you have a better UI here, reloading is not a great user experince here.
              const confirmed = alert('New version of the app is available. Refresh now');
              if (confirmed == true) {
                window.location.reload();
              }
        }
}

This is interesting and appears to solve the issue of recognizing new code has been deployed without a browser refresh, and will display a prompt, but where is the documentation on flutter.dev to implement this for flutter web? Or this appears to be "non-flutter core" work that at one point was under development, but I can't seem to find it on pub.dev or anywhere else.

@github-actions github-actions bot removed the waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds label May 25, 2022
@darshankawar
Copy link
Member

Thanks. The PR you linked above was pretty old and seems he never came back to it, so the status still stands as-is.
I am going to label this as documentation request.

@darshankawar darshankawar added platform-web Web applications specifically documentation and removed in triage Presently being triaged by the triage team labels May 25, 2022
@Zelfapp Zelfapp changed the title Flutter web - Need documentation on how to programmatically clear cache Flutter web - Need documentation on how to conditionally display a "Refresh. New Version is available." prompt when updated code is deployed to production. May 25, 2022
@yjbanov yjbanov added the P3 Issues that are less important to the Flutter project label May 28, 2022
@MingSern
Copy link

Any updates on this?

@kevmoo
Copy link
Contributor

kevmoo commented Nov 8, 2022

I'm adding this to my tracking list. We'll either update the docs or fix the framework – or both if needed! Thanks for your patience!

@kevmoo kevmoo self-assigned this Nov 8, 2022
@yogeshPutman
Copy link

Any changes in version 3.x?

@kevmoo
Copy link
Contributor

kevmoo commented Dec 15, 2022

Nothing new. We're focused on some more core things.

I need to dig in through https://web.dev/learn/pwa/update/

@zs-dima
Copy link

zs-dima commented Mar 4, 2023

Here is a temporary solution, but I hope this issue will be closed soon finally:
https://zs-dima.medium.com/flutter-web-new-website-version-is-available-press-update-to-refresh-81b778d6a4fd

@faFrafa
Copy link

faFrafa commented Apr 4, 2023

Looking forward to have this feature for the web without much extra-coding and workarounds.

The strange thing is that sometimes our Flutter web apps stop working on Chrome after deploying a new version, even after clearing completely the Chrome's cache.
It's quite random, and cannot reproduce it, because it stops only for few Chrome users (that have completely cleared their cache). They have to switch to another browser.

@Caio-Rezende
Copy link

Looking forward to have this feature for the web without much extra-coding and workarounds.

The strange thing is that sometimes our Flutter web apps stop working on Chrome after deploying a new version, even after clearing completely the Chrome's cache. It's quite random, and cannot reproduce it, because it stops only for few Chrome users (that have completely cleared their cache). They have to switch to another browser.

You must be careful if only cache is being cleaned or the site data (ctrl+shift+backspace displays 3 options to clear: navigation history, site data, cache). Since it uses service worker to store data, it might still be using something from there and a simple cache clearing isn't enough. One must clear cache and site data to ensure a clean update in the next access.

@richardmcintyre
Copy link

Agreed , this needs to be in place before Flutter Web can be used for any kind of serious business application.

@kevmoo
Copy link
Contributor

kevmoo commented Apr 13, 2023

@zs-dima – great to see your demo!

Would you outline the bits needed that could help get this going? Looks like exposing the version information in the output is a good first step to enabling your work.

@zs-dima
Copy link

zs-dima commented Apr 13, 2023

@kevmoo sure,
There are several ways to obtain the application version:

  1. From the application scripts itself, for example using package_info_plus.
    In this case, you will get the application version related to the application java scripts currently loaded in the browser.
  2. By requesting 'https://your_app.com/version.json' with http.get(uri), for example. This version.json file Flutter generates by default.
    In this case you will get the currently deployed version number, which may be different from the (1) if a new version of a web application version has been deployed.

Note that html.window.location.reload() is used 2 times to completely reload all application scripts.

Please let me know if I can clarify this further.

@kj415j45
Copy link
Contributor

kj415j45 commented May 4, 2023

  1. From the application scripts itself, for example using package_info_plus.
    In this case, you will get the application version related to the application java scripts currently loaded in the browser.
  2. By requesting 'https://your_app.com/version.json' with http.get(uri), for example. This version.json file Flutter generates by default.
    In this case you will get the currently deployed version number, which may be different from the (1) if a new version of a web application version has been deployed.

@zs-dima Did not test your demo yet but I wonder if it actually works? I tried to dive into package_info_plus's source before and found that they use /version.json as app version too.

It may be useless as it is not cached by service worker, so the result may always be the latest version, while the main.dart.js is out-dated. (Image is an app using package_info_plus)

image

My opinion is that flutter/dart compile chain should inject two constant kBuildVersion and kBuildNumber at build-time just like other constant like kDebugMode and kIsWeb so that application can get precise build state (and may request a user update).

@zs-dima
Copy link

zs-dima commented May 4, 2023

@kj415j45, application actually works, although this solution of reloading the application twice is far from ideal.
Another improvement can be made by using --dart-define-from-file=keys.json.

@flutter-triage-bot flutter-triage-bot bot added the d: api docs Issues with https://api.flutter.dev/ label Jul 5, 2023
@flutter-triage-bot flutter-triage-bot bot added team-web Owned by Web platform team triaged-web Triaged by Web platform team labels Jul 8, 2023
@RoarGronmo
Copy link

I am not sure if any has asked before; will there be any "out-of-the-box" solution for this situation? Quite frustrating to tell users to hit "Ctrl+F5" each time we publish a new version of our web-app...

Even more tricky when the users are on a tablet, pad or mobile to guide them to refresh with correct method "forcer refresh", and that either isn't done the same way on different browsers (chrome vs safari etc).

And upon that again, when the users have "installed" the web app, how to "force" refresh the page then, to reload the "static parts" ?.

I really hope for an "out-the-box" solution in near future !! It should be prioritized !!

@AndryHTC
Copy link

I am not sure if any has asked before; will there be any "out-of-the-box" solution for this situation? Quite frustrating to tell users to hit "Ctrl+F5" each time we publish a new version of our web-app...

Even more tricky when the users are on a tablet, pad or mobile to guide them to refresh with correct method "forcer refresh", and that either isn't done the same way on different browsers (chrome vs safari etc).

And upon that again, when the users have "installed" the web app, how to "force" refresh the page then, to reload the "static parts" ?.

I really hope for an "out-the-box" solution in near future !! It should be prioritized !!

Absolutely agree. Flutter Web is considered stable but too many minimum features are missing

@flutter-triage-bot flutter-triage-bot bot added the Bot is counting down the days until it unassigns the issue label Nov 23, 2023
@flutter-triage-bot
Copy link

This issue is assigned to @kevmoo but has had no recent status updates. Please consider unassigning this issue if it is not going to be addressed in the near future. This allows people to have a clearer picture of what work is actually planned. Thanks!

@z8888-sharkfin
Copy link

z8888-sharkfin commented Jan 28, 2024

Don't really understand why this issue is not able to be solved soon?

@flutter-triage-bot flutter-triage-bot bot removed the triaged-web Triaged by Web platform team label Feb 14, 2024
@flutter-triage-bot
Copy link

This issue was assigned to @kevmoo but has had no status updates in a long time. To remove any ambiguity about whether the issue is being worked on, the assignee was removed.

@flutter-triage-bot flutter-triage-bot bot removed the Bot is counting down the days until it unassigns the issue label Feb 14, 2024
@adriank
Copy link

adriank commented Feb 15, 2024

We just need simple access to window.location.reload(). Apps shouldn't force update and auto-reload, because users could be in the middle of writing some text or filling up some form. Reload would probably reset the progress. Google cloud console does updates best and Flutter apps should provide similar UX - a button telling the user to refresh when they're ready for it.

@yjbanov yjbanov added the triaged-web Triaged by Web platform team label Feb 22, 2024
@Martin-QPT
Copy link

Agreed - this is something that really should be in place.

@NashIlli
Copy link

+1

2 similar comments
@diogoroos
Copy link

+1

@yogeshPutman
Copy link

+1

@felipeduarte26
Copy link

I'm still having the same problem, I'm testing some solutions, if it works I'll try to create a PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
d: api docs Issues with https://api.flutter.dev/ P3 Issues that are less important to the Flutter project platform-web Web applications specifically team-web Owned by Web platform team triaged-web Triaged by Web platform team
Projects
None yet
Development

No branches or pull requests