Flutter (web) - how to reload the Flutter PWA - flutter

This is the similar question (with wrong title): Flutter Web - How to reload currently Active Page
Running the Flutter web app as PWA I need to allow users to initiate refresh and thus update the (web) app.
Unfortunately import 'dart:html' as html; is not allowed anymore when having the same codebase for web and mobile native apps. So following code is not the option:
RaisedButton(
child: Text("Update me"),
onPressed: () {
if (kIsWeb) html.window.location.reload();
},
),
What is the correct approach?
EDIT:
I have managed to use 'dart:js' in the same codebase for pwa and mobile using conditional imports. This means I can call JavaScript from within the Flutter code. Unfortunately location.reload(true); does not reload the PWA.
Ideally I would have the Flutter approach for the PWA reload / update or JavaScript workaround.
EDIT2:
The whole issue is within the PWA handling of refresh button / window reload.
Unfortunately service worker's skipWaiting() can be called only from within service worker ( https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#skip_the_waiting_phase )
The correct approach seams to be sending the skipWaiting message to the new instance of the service worker.
However skipWaiting is not yet fully supported on some browsers (iOS?) so the safer approach seams to be just unregistering the worker...
I ended up using following to stop the worker and reload.
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistration().then(swr => {swr.unregister().then(
() => {location.reload(true)}
)});
} else {location.reload(true)}
Related:
Access service worker skipWaiting from within App build with Webpack+Workbox
https://medium.com/#nekrtemplar/self-destroying-serviceworker-73d62921d717
https://redfin.engineering/how-to-fix-the-refresh-button-when-using-service-workers-a8e27af6df68
https://deanhume.com/displaying-a-new-version-available-progressive-web-app/

Flutter 2.10.3
In the newest version of Flutter (2.10.3 at the time of writing this), PWAs will automatically update when the serviceWorkerVersion changes in your index.html file.
From this issue, Jonah Williams comments:
"The app will update after the new version has been downloaded and the page revisited."
You can read more about general PWA support by Flutter here. For the pull request that added server worker support, see this.
If you want to allow users to manually fetch a new version of your app, you can use the universal_html package which works on all platforms.
import 'package:universal_html/html.dart' as html;
onTap: () {
html.window.location.reload();
},
Keep an eye on this issue for an improvement to how server worker caching is handled.

A work around would be to navigate to your apps entry point route '/'
`Navigator.of(context).pushNamedAndRemoveUntil(
'/,
(Route<dynamic> route) => false
);`

I maintain a set of web versions myself. When I upgrade my web application, I will upgrade the version level. Every time I start homePage, I will request the version number of the remote configuration to match the version number of the current PWA application. When the local version number is lower than the remote version number, I will refresh
//pubspec.yaml universal_html: ***
import 'package:universal_html/html.dart' as html;
getWebVersion() async{
var res= await dio.get(web_version);
if (locaVersion < res.version) {
html.window.location.reload();
}
}

Related

Firebase 9 & Flutter: How to initializeApp?

I am desperately trying to figure out how to initializeApp with Flutter (not React Native).
I know about the functions to use, but I can not find the firebaseConfig I need to pass into the function.
And no matter what I search for, every resources references to React Native, like as if nobody codes with Flutter since Firebase 9 has been released anymore (or I am the only dummy which is not able to resolve this by myself).
Can someone tell me where to get the firebaseConfig object from?
If I add a new app to my project, I only get the google-services.json, which does NOT include the firebaseConfig object I need to pass.
I understand your confusion now, let me explain. When the guy in the video talks about Firebase v9 he is talking about the SDK version which in the case of Javascript (which I suppose is his main topic in his channel) is currently 9.17.1 an the version 9 has been around since 2021 so it is not new. The different SDKs have their own versions for each platform so thinking it will be the same in every SDK is a mistake by itself. You can check the SDKS here. So there is no Firebase v9, there is a Firebase SDK for javascript version 9. They managed in that way in javascript and in flutter it is not the same. Being that the last update in the flutter SDK was literally yesterday I'm pretty sure they have their reasons to not implement the same functions in flutter since 2021.
Now, one of the thinks the guy talks in the video is deconstructing, which is something common in javascript. The way you do this in flutter is by using show.
So you would be doing this for example:
import 'package:cloud_firestore/cloud_firestore.dart' show FirebaseFirestore, QuerySnapshot; //Add everything you would be using
This way only the specific parts of the library will be imported and the amount of code the Dart VM has to load will be reduced.
As of the access to documents, it is still the same but you can easily create a helper class that contents your references to your collections and then just use that class to reduce the boilerplate code created by the firebase SDK.
You have to install the Firebase CLI and run firebase init.
You need to use the package firebase_core that will give you access to the class Firebase so you can use it to initialize your app Firebase.initializeApp() you can pass the default options for the current platform using Firebase.initilizeApp(options: DefaultFirebaseOptions.currentPlatform) usually your IDE will automatically import the corresponding package but in case it does not you would have to import 'firebase/firebase_options.dart';
An useful link to the documentation: Add Firebase to your Flutter App

Flutter Web - how to test flutter web application opens in Chrome on desktop?

There is a button on a web page that makes an API call to third party application. And in return third-party application gets rendered on a web page.
Now, the third-party application (https://goknow.me/#/) is developed in flutter and I know nothing about flutter. I'm using java, selenium and webdriver for end to end testing. I'm using same set of tools for the rest of the application and it's working fine.
While inspecting in chrome, the DOM look like this:
Flutter application has a form and I want to find an element so that I can send inputs during testing automation. By searching online I found this appium-flutter-driver. I've also included the required jar in my project. With selenium webdriver I'm not able to find an element in flutter application that renders in Chrome browser on desktop.
Here's the code:
import pro.truongsinh.appium_flutter.FlutterFinder;
import pro.truongsinh.appium_flutter.finder.FlutterElement;
protected FlutterFinder find;
WebElement iframe = driver.findElement(By.xpath("//iframe[#id='know-iframe']"));
driver.switchTo().frame(iframe);
find = new FlutterFinder(driver);
FlutterElement elm = find.text("Email");
elm.click();
elm.sendKeys("hello world");
During testing automation I want to select fields in form and send inputs to those fields.
How to find an element in flutter web application that renders in another web application in Chrome browser on desktop?
Flutter Web is very different from normal web frameworks such as React or Vue. Looking at the official doc, it renders either into HTML elements (but still not the usual elements you see everyday), or directly draw onto a Canvas.
In addition, since it is a third-party app, it is mostly likely that you are not able to change their code. Thus, your appium-flutter-driver mostly will not work, because it says:
Under the hood, Appium Flutter Driver use the Dart VM Service Protocol with extension ext.flutter.driver, similar to Flutter Driver, to control the Flutter app-under-test (AUT).
You know, Dart VM service is only available when you run the Flutter app by source code in debug mode, or at least when you have control to the source code.
Therefore, my suggestion is: Can you treat the Flutter application as a "picture" instead of a DOM tree, and try to locate the buttons?
you can try using io.github.sukgu that helps you to work on the shadow elements. I was able to automate the scenario that you mentioned. Below is the detailed code.
Step 1 add the below dependency
<!-- https://mvnrepository.com/artifact/io.github.sukgu/automation -->
<dependency>
<groupId>io.github.sukgu</groupId>
<artifactId>automation</artifactId>
<version>0.1.3</version>
</dependency>
Step 2 use the below import in the test file
import io.github.sukgu.*;
Step 3 Below is the entire code that worked for me
WebDriver driver = new ChromeDriver();
driver.get("https://goknow.me/#/");
WebDriverWait wait = new WebDriverWait(driver,20);
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(By.id("know-iframe")));
Shadow shadow = new Shadow(driver);
WebElement emailField = shadow.findElement("input[id='email']");
emailField.sendKeys("hello world");
Flutter team recommends using Flutter for "app-centric experiences" as "Progressive Web Apps, Single Page Apps, Existing Flutter mobile apps", but Flutter web app can also be embedded in a iframe tag.
They say:
At this time, Flutter is not suitable for static websites with text-rich flow-based content. For example, blog articles benefit from the document-centric model that the web is built around, rather than the app-centric services that a UI framework like Flutter can deliver.
You can read more about how a Flutter web app is deployed here.
When a Flutter app is built for the web, the page is rendered in 2 ways:
HTML renderer (on mobile browsers)
CanvasKit renderer (on desktop browsers)
I hope now you know a little more about Flutter framework. 🙂

Shared_Preferences for Flutter Web?

Shared_preferences (https://pub.dev/packages/shared_preferences) doesn't seem to work for Flutter for Web.
I have the following function that's called when a button is pressed.
getEmail() async {
print("reached 1st line");
SharedPreferences prefs = await SharedPreferences.getInstance();
print("reached 2nd line");
String _confirmedEmail = prefs.getString('_confirmedEmail') ?? "";
)
It prints "reached 1st line" but not "reached 2nd line", which means the program doesn't go past the await statement. Interestingly I don't get any error either. It seems to just ignore the rest of the function after the await statement.
What is the best alternative to store shared preferences in Flutter for Web?
Great news, from version 0.5.6 shared_prefs flutter supports web by default
Now it's includes shared_preferences for web
Your code should work without changes, just update dependency in pubspec.yaml
dependencies:
shared_preferences: ^0.5.6
Now the latest shared_preference package includes web support. But if you run on debug web mode you will see that the shared preference is not working on the web.
The reason is that Flutter Web runs on a random port every time and cannot recollect the shared or stored data.
The best way to test this is to run the flutter app on the web twice and check the URL it will show different ports always.
localhost:5050/#/
To solve this issue force flutter to run the app on the same port.
you can do this by adding a port while running the flutter app on the web from a terminal like this.
flutter run -d chrome --web-hostname localhost --web-port 5050
Or you can add the port in the lauch.json file of VS code like this.
"version": "0.2.0",
"configurations": [
{
"name": "appname",
"request": "launch",
"type": "dart",
"args": ["--web-port", "5050"]
},
This will help you to maintain the session and also retrieve the data.
shared_preferences are not supposed to work with flutter web, that's why value of instance never returns. For this purpose, you can use any key-value stores instead, for example, sembast
UPD: the package supports web now since version 0.5.6
I think it is supported now due to this. It depends on shared_preferences_web
image from pub dev
You probably check the tags when you search for a library in pub.dev.
For the web the best way to implement that is implementing cache for web and dcache for implement that using flutter_web.
import 'package:dcache/dcache.dart';
void main() {
Cache c = new SimpleCache(storage: new SimpleStorage(size: 20));
c.set("key", 42);
print(c.get("key")); // 42
print(c.containsKey("unknown_key")); // false
print(c.get("unknown_key")); // nil
}
As you can see, is very similar to shared_preferences for Flutter.
Hope this could help.
In latest version of shared_prefs, flutter supports web by default
SharedPreferences pref = await SharedPreferences.getInstance();
//for setting values locally:
await pref.setString("token", myJWTToken);
//for getting values:
dynamic token = pref.getString("token");
but still if you are looking for alternative then you can use flutter_session package for almost simillar purpose.
//for setting values locally:
await FlutterSession().set("token", myJWTToken);
//for getting values:
dynamic token = await FlutterSession().get("token");

Not getting any error output in Flutter web

I'm unable to get any error output from my Flutter web app. Printing to the console using
print('some text');
works fine, but no errors get printed. For example, throwing an exception
throw new Exception('testexception');
doesn't result in any output, neither in the browser console nor in IntelliJ. The log level settings in Chrome are set to [Info, Warnings, Errors].
I even tried implementing a custom error handler
void main(){
FlutterError.onError = (FlutterErrorDetails details) {
print('main.onError: details: ${details.toString()}');
};
runApp(new MyApp());
}
but no luck. Do I have to enable error outputs somewhere? I can't find any info about this in the documentation.
I tried running the app both using the Dart Dev Server (which is started when using Run from IntelliJ), as well as calling webdev serve and webdev serve --auto restart from the Terminal.
Flutter Web currently doesn't have a way to be debugged. Will generate a main.dart.js and you can debug it with Chrome console.
One cool trick to "debug" your Web App, is by showing a popup to your browser:
import 'dart:js' as js;
#override
void initState() {
super.initState();
js.context.callMethod("alert", <String>["Your debug message"]);
}
Did you look to see if it was a known issue? Flutter-web is only at developer preview level. If there's not a current issue, create one, and provide a minimal "how to reproduce". I'm sure the Flutter-web team would appreciate it.
I tried in both IDE and its correctly working in Intellij but not in VSCode. Its showing white blank screen give it try to run again flutter run -d chrome and wait in blank screen for 2minutes.
And check the SDK path of flutter in visual code is correct or not.
In JavaScript, the console.log method is used to output to the Web browser's JavaScript console.
So you can do it from Dart by getting the JavaScript console object and invoking that method on it.
import 'dart:js' as javascript; // ignore: avoid_web_libraries_in_flutter
/// Invoke the the JavaScript `console.log` method.
void logToBrowser(String message) {
final console = javascript.context['console'] as javascript.JsObject;
console.callMethod('log', [message]);
}
Since dart:js is only supported for Dart compiled to JavaScript, this means the Flutter app using it will only work as a Flutter Web app. Importing it will mean the code won't work for an app targeting iOS, Android, etc.
Tip: the JavaScript console object has other methods that can be invoked. Instead of log, use info, warn and error. Those last two highlight the entry when they appear in the console.

How to cause a chrome app to update as soon as possible?

Deploying a chrome packaged app and publishing updates on the chrome web store allows users to automatically receive application updates. There are situations where you want to know if the running application is the most current or not, and updating it. E.G.:
Just keeping the user on the most recent version.
Detecting a mismatch between the application and server side APIs, and requiring the client side application to update to use new server side APIs.
Documentation for chrome.runtime.requestUpdateCheck() offers a status of "throttled", "no_update", "update_available", but doesn't indicate what to do if a newer version is required.
Install a listener for chrome.runtime.onUpdateAvailable, which fires when the new .crx file has been downloaded and the new version is ready to be installed. Then, call chrome.runtime.requestUpdateCheck:
chrome.runtime.onUpdateAvailable.addListener(function(details) {
console.log("updating to version " + details.version);
chrome.runtime.reload();
});
chrome.runtime.requestUpdateCheck(function(status) {
if (status == "update_available") {
console.log("update pending...");
} else if (status == "no_update") {
console.log("no update found");
} else if (status == "throttled") {
console.log("Oops, I'm asking too frequently - I need to back off.");
}
});
Depending on your application, when an update is detected you may want to use something like setTimeout and call chrome.runtime.restart() or chrome.runtime.restart() later
According to the Google Chrome documentation you need to have
chrome.runtime.onUpdateAvailable.addListener(function(details) {
chrome.runtime.reload(); // To restart the chrome App instantaneously
});
But this take time to reflect JS changes into the chrome because background.js loaded into the background and it needs to be unloaded and loaded again
To cop this situation you need to include
chrome.runtime.onInstalled.addListener(function(details) {
chrome.runtime.reload();
});
as wel.
onInstalled called whenever google extension installed first time (fresh installation), google extension updated or google chrome updated.