Flutter web PWA install prompt from within the app - flutter

I'm working on a Flutter Web PWA app and having trouble with triggering the Add To Home Screen prompt from within the flutter app. I understand it can be triggered using Javascript with the code below, but how do I do this from my Flutter dart file?
buttonInstall.addEventListener('click', (e) => {
// Hide the app provided install promotion
hideMyInstallPromotion();
// Show the install prompt
deferredPrompt.prompt();
// Wait for the user to respond to the prompt
deferredPrompt.userChoice.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
console.log('User accepted the install prompt');
} else {
console.log('User dismissed the install prompt');
}
})
});

Currently, Flutter web can only prompt PWA installs from the browser. The issue with displaying PWA install prompt is it breaks the compatibility/design with Android/iOS builds as the feature is web-specific.
A different approach that you can take here is by displaying "PWA install" reminders when the app is run on web. Otherwise, it's best to file this as a feature request.

I created the pwa_install package specifically for this purpose.
1. Update index.html
<!-- Capture PWA install prompt event -->
<script>
let deferredPrompt;
window.addEventListener('beforeinstallprompt', (e) => {
deferredPrompt = e;
});
function promptInstall(){
deferredPrompt.prompt();
}
// Listen for app install event
window.addEventListener('appinstalled', () => {
deferredPrompt = null;
appInstalled();
});
// Track how PWA was launched (either from browser or as PWA)
function getLaunchMode() {
const isStandalone = window.matchMedia('(display-mode: standalone)').matches;
if(deferredPrompt) hasPrompt();
if (document.referrer.startsWith('android-app://')) {
appLaunchedAsTWA();
} else if (navigator.standalone || isStandalone) {
appLaunchedAsPWA();
} else {
window.appLaunchedInBrowser();
}
}
</script>
2. Call PWAInstall().setup()
You can call this method in main.dart before calling runApp()
Future<void> main() async {
// Add this
PWAInstall().setup(installCallback: () {
debugPrint('APP INSTALLED!');
});
runApp(MaterialApp(home: App()));
}
3. Check if the Install Prompt is enabled
Before calling the promptInstall_() method, you can check if the Install Prompt is available using PWAInstall().installPromptEnabled.
installPromptEnabled will be true if:
The app was launched in a browser (It doesn't make sense to prompt a PWA install if the app is already running as a PWA)
The beforeinstallprompt event was captured.
promptInstall_() won't do anything if installPromptEnabled is false so you should check this flag before attempting to call the prompt.
4. Call PWAInstall().promptInstall_()
Finally, call PWAInstall().promptInstall_() to show the install prompt.
Note that this will not work on Safari since Safari does not implement the beforeinstallprompt method this package relies on.

Related

is there a way to check if the PWA was launched through a file or not?

I'm using the file handle API to give my web app the optional capability to launch through double-clicking files in the file explorer.
Writing the code below, I expected if (!("files" in LaunchParams.prototype)) to check if a file was used to launch the app, but apparently, it checks if the feature is supported. OK, that makes sense.
After that, I thought the setConsumer callback would be called in any launch scenario, and files.length would be zero if the app was launched in other ways (like by typing the URL in the browser). But on those use cases, the callback was not called at all, and my init logic was never executed.
if (!("launchQueue" in window)) return textRecord.open('a welcome text');
if (!("files" in LaunchParams.prototype)) return textRecord.open('a welcome text');
launchQueue.setConsumer((launchParams) => {
if (launchParams.files.length <= 0) return textRecord.open('a welcome text');
const fileHandle = launchParams.files[0];
textRecord.open(fileHandle);
});
I've also followed the Launch Handler API article instructions and enabled the experimental API.
The new code confirms that "targetURL" in LaunchParams.prototype is true, but the setConsumer callback is not executed if the user accesses the web app through a standard browser tab.
function updateIfLaunchedByFile(textRecord) {
if (!("launchQueue" in window)) return;
if (!("files" in LaunchParams.prototype)) return;
console.log({
'"targetURL" in LaunchParams': "targetURL" in LaunchParams.prototype,
});
// this is always undefined
console.log({ "LaunchParams.targetURL": LaunchParams.targetURL });
// setConsumer does not trigger if the app is not launched by file, so it is not a good place to branch what to do in every launch situation
launchQueue.setConsumer((launchParams) => {
// this never run in a normal tab
console.log({ setConsumer: launchParams });
if (launchParams.files.length <= 0) return;
const fileHandle = launchParams.files[0];
textRecord.open(fileHandle);
});
}
This is the result...
Is there a universal way to check if the web app was launched through a file?
Check out the Launch Handler origin trial. It lets you determine the launch behavior exactly and lets your app detect how the launch happened. This API works well together with the File Handling API that you already use. You could, for example, check the LaunchParams.targetURL to see how the app was launched. Your feedback is very welcome.
Since I was not able to guarantee that the setConsumer callback was called in every situation (especially when the app is launched in a regular browser tab), I hacked it through setTimeout:
function wasFileLaunched() {
if (!("launchQueue" in window)) return;
if (!("files" in LaunchParams.prototype)) return;
return new Promise((resolve) => {
let invoked = false;
// setConsumer does not triggers if the app is not launched by file, so it is not a good place to branch what to do in every launch situation
launchQueue.setConsumer((launchParams) => {
invoked = true;
if (launchParams.files.length <= 0) return resolve();
const fileHandle = launchParams.files[0];
resolve(fileHandle);
});
setTimeout(() => {
console.log({ "setTimeout invoked =": invoked });
if (!invoked) resolve();
}, 10);
});
}

Run action before Ionic Vue closes or app runs in background

How can i detect in Ionic Vue if the app closes or if the app is send to the background?
this.platformor platform isn't available or does not work?!
You can subscribe to the appStateChange event, like described here: https://capacitorjs.com/docs/apis/app
Example:
import { App } from '#capacitor/app'
export default {
created () {
App.addListener('appStateChange', state => {
if (!state.isActive) { // if isActive is true, App got sent to foreground, if it is false, it got sent to the background
...
}
})
}
}

I can't receiving notifications when the app is closed or swiped away on huawei device?

I'm using onsignal notifications with my flutter app,I've tested my app on three Samsunge devices and notifcations working perfectly in all these devices when the app on foreground, background, and also when I swiped it away.
after that I tested the app on huawei device which using EMUI 9.0.1 Os
the notifications only works if the app is active or on background
if I swiped it away I can't receiving any notifications.
Any help would be greatly appreciated, I've been struggling with this for a long time. I'll post my code for setting onesignal below
Future<void> initPlatformState() async {
if (!mounted) return;
OneSignal.shared.setLogLevel(OSLogLevel.verbose, OSLogLevel.none);
OneSignal.shared.setRequiresUserPrivacyConsent(true);
OneSignal.shared.consentGranted(true);
var settings = {
OSiOSSettings.autoPrompt: false,
OSiOSSettings.promptBeforeOpeningPushUrl: true
};
OneSignal.shared.setNotificationReceivedHandler((notification) {
this.setState(() {
print('Notifiaction received');
});
});
OneSignal.shared
.setNotificationOpenedHandler((OSNotificationOpenedResult result) {
this.setState(() {
newUrl = result.notification.payload.additionalData['url'].toString();
});
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (context) => WebNotification(newUrl)));
});
// NOTE: Replace with your own app ID from https://www.onesignal.com
await OneSignal.shared
.init("xxxx-xxxx-xxxx-xxxx-xxxx", iOSSettings: settings);
OneSignal.shared
.setInFocusDisplayType(OSNotificationDisplayType.notification);
OneSignal.shared.inFocusDisplayType();
}
You need to set up a service extension. Take a look at our docs on Background Notifications. Also, consider Notification Behavior when designing your implementation
make sure you sue latest version of onesignal
for huwaii use HMS if possible ( onesignal support HMS )
In your root build.gradle, under buildscript, add the following 2 new lines to your existing repositories and dependencies sections
buildscript {
repositories {
// ...
maven { url 'https://plugins.gradle.org/m2/' } // Gradle Plugin Portal
}
dependencies {
// ...
// OneSignal-Gradle-Plugin
classpath 'gradle.plugin.com.onesignal:onesignal-gradle-plugin:[0.12.8, 0.99.99]'
}
}
Add the following to the top of your app/build.gradle
apply plugin: 'com.onesignal.androidsdk.onesignal-gradle-plugin'
you need to make user add your app to ignore battry optmztion in huwaii it is (protacted app )
in flutter you can make button and attach it to the app setting ( use these plugin https://pub.dev/packages/app_settings)

How can I keep the BarcodeScanner screen open without redirecting me to another page?

I am making an application that allows me to scan QR codes, and I managed to make the code work but my doubt is that when I scan the plugin phonegap-plugin-barcodescanner takes me out of the camera interface, what I want to do is to stay in it and show me an alert with the scanned code, and to give it ok in the alert to stay in it, to avoid being clicked to enter it.
I'm working with the plugin phonegap-plugin-barcodescanner, in ionic 3
public scanQR2() {
this._ButtonText = "Escanear";
this._barcodeScanner.scan().then((barcodeData) => {
if (barcodeData.cancelled) {
console.log("User cancelled the action!");
this._ButtonText = "Escanear";
return false;
}
console.log("Scanned successfully!");
console.log(barcodeData);
}, (err) => {
console.log(err);
});
}
I hope that the application when performing a scan stays on the same interface and does not redirect me to another page

Ionic 1 push notification, push.register never been fire up

I've been followed the instruction on: http://docs.ionic.io/services/push/
And I use the code below in my after login function:
$ionicPush.register().then(function(t) {
console.log(t);
return $ionicPush.saveToken(t);
}).then(function(t) {
console.log('Token saved:', t.token);
});
but it the register function with console.log never been fire up after I login.
Any idea?
Also I'm not using ionic Auth, so i didn't save the device token into user obj, and I think the auth user is not mandatory for the ionicPush, is this correct?
You can totally use Ionic Push without Ionic Auth.
Are you trying to run this code in your browser or on a device? It will only work on a device.
I suggest you to catch errors on your register() call first:
$ionicPush.register().then(function(t) {
console.log(t);
}, function(error) {
console.log(error)
});
Thus, you will see why it's not working.