What’s the best way to upload data in the background, but don’t cancel this upload when the user switches screens? I would also like to show Snackbar when the upload finishes (even if I'm on a different screen already).
On Android, I’ve been using Service + BroadcastReceiver or WorkManager. I Can’t find the way of doing it in Flutter.
You can use package https://pub.dev/packages/flutter_uploader
This plugin is based on WorkManager in Android and NSURLSessionUploadTask in iOS to run upload task in background mode.
Create new upload task:
final uploader = FlutterUploader();
final taskId = await uploader.enqueue(
url: "your upload link", //required: url to upload to
files: [FileItem(filename: filename, savedDir: savedDir, fieldname:"file")], // required: list of files that you want to upload
method: UploadMethod.POST, // HTTP method (POST or PUT or PATCH)
headers: {"apikey": "api_123456", "userkey": "userkey_123456"},
data: {"name": "john"}, // any data you want to send in upload request
showNotification: false, // send local notification (android only) for upload status
tag: "upload 1"); // unique tag for upload task
);
Listen for upload progress
final subscription = uploader.progress.listen((progress) {
//... code to handle progress
});
For Snackbar , you can use ScaffoldMessenger to make Snackbar persist across routes https://flutter.dev/docs/release/breaking-changes/scaffold-messenger
The ScaffoldMessenger now handles SnackBars in order to persist across routes and always be displayed on the current Scaffold. By default, a root ScaffoldMessenger is included in the MaterialApp
The ScaffoldMessenger creates a scope in which all descendant Scaffolds register to receive SnackBars, which is how they persist across these transitions. When using the root ScaffoldMessenger provided by the MaterialApp
code snippet
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: const Text('snack'),
duration: const Duration(seconds: 1),
action: SnackBarAction(
label: 'ACTION',
onPressed: () { },
),
));
You can use this dart package workmanager
Related
I am using awesome notifications in Flutter to show local notifications. My plan is to have an action button in a notification, which changes some values in the shared preferences in the background (without opening the app) when pressed. Is this even possible to do?
I tried using onActionReceivedMethod-listener and getInitialNotificationAction-method in my main function after initializing awesome notifications:
AwesomeNotifications().setListeners(onActionReceivedMethod: (action) async{
print(action.body);
});
// OR
ReceivedAction? receivedAction = await AwesomeNotifications().getInitialNotificationAction(
removeFromActionEvents: false
);
if (receivedAction?.body != null){
print(receivedAction.body);
}
Both of them worked (separetly used) only when the app had already started, but they also gave this error:
Awesome Notifications: A background message could not be handled in Dart because there is no dart background handler registered. (BackgroundService:58)
But how I could get it working when the app is not opened? Can I create a backgroung handler without Firebase, or is there some other way to achieve this?
Here is my code, how I create the notification:
static Future<void> createSimpleNotification() async {
await AwesomeNotifications().createNotification(
content: NotificationContent(
id: 1,
channelKey: 'important_channel',
title: 'Title',
body: 'Test',
largeIcon: 'asset://assets/iconPhoto.png'),
actionButtons: [
NotificationActionButton(
key:'key',
label: 'label',
actionType: ActionType.SilentBackgroundAction)]
);
}
I found the answer from here: Cannot find AwesomeNotifications().actionStream
I implemented it like this:
#pragma("vm:entry-point")
Future<void> _onActionReceivedMethod(ReceivedAction action) async {
print('It works');
}
And it worked both when the app was in the background or in the foreground.
Currently when I tap on an image, it should open a web view as the image object has an associated url. I am using the url_launcher library for Flutter, and I have implemented the code as follows:
onTap: () async {
final url = image.url;
if (await canLaunch(url)) {
await launch(
url,
forceSafariVC: true,
forceWebView: true,
enableJavaScript: true,
);
}
},
My understanding is that this launches a WebView, which is an in-app browser rather than taking the user out of the app and into a separate browser app. This works fine, but the page loads much slower and Javascript elements do not work properly (e.g. animated text on websites). On the other hand,
onTap: () async {
final url = banners[index].url;
if (await canLaunch(url)) {
await launch(
url
);
}
},
works much better as it is faster and everything loads in properly, but it opens an external browser, which is not what I want.
Another issue is that I would like to add a Done button on the top left corner of the WebView to completely exit the WebView and return to where I was in the app. I only have a back button on the bottom left (on the Android emulator), that lets me go to the previous page I was at in the browser.
How do I customise the layout of the WebView, if I do not have any access to it? The url_launcher seems to handle the WebView creation internally, so I'm wondering how can I gain access from the above code to add that button?
Thank you!
If you use the native webview in this manner then you can't customise the ui. But instead, since you are displaying image you can use image.network from flutter.
Image.network(imgURL,fit: BoxFit.fill,
loadingBuilder:(BuildContext context, Widget child,ImageChunkEvent loadingProgress) {
if (loadingProgress == null)
return child;
return Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null ?
loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes
: null,
),
);
},
),
Or, you can also use official webview plugin from flutter team to create in app web view.
webview_flutter: ^2.3.1
In this approaches if you add a back button in ui you can the use Navigator.pop(context); on onPressed property in button to go back
I am working on testing how my navigator 2.0 setup handles url changes in the browser in flutter web.
The closest i have come to being able to test how my app handles url changes is to manually update state in the RouterDelegate by calling the setNewRoutePath with a config from the RouteInformationParser.
I would really like to test the navigator closer to the origin of the url change.
Any ideas and pointers would be appreciated.
My current code looks like this:
//Pass routeInformation to RouterInformationParser
RouteInformation selectShopRoute = RouteInformation(location: '/selectshop?token=321');
RouterConfig selectShopConfig = await app.myRouteParser.parseRouteInformation(selectShopRoute);
await app.myRouterDelegate.setNewRoutePath(selectShopConfig);
await tester.pumpAndSettle();
//Verify that navigator state is select shop
expect(app.myRouterDelegate.currentScreen, RouterEnum.selectshop);
//Verify that navigator token is set correctly
expect(app.myRouterDelegate.token, '321');
I had the same question and could not find a good approach. I came up with a way to test our code and wanted to share it to you.
Basically, we have a custom RouteInformationParser, in which a location is added only for the testing purpose.
class MyRouteInformationParser
extends RouteInformationParser<PageConfiguration> {
String? customPath; // only use for testing
#override
Future<PageConfiguration> parseRouteInformation(
RouteInformation routeInformation,
) async {
final location = customPath ?? routeInformation.location;
// Compute the configuration based on the location
return PageConfiguration()
}
}
In the widget test, we just create the route information parser and use it with the MaterialApp. Changing the customPath during testing has similar effect as changing the URL of the web browser.
final informationParser = MyRouteInformationParser();
informationParser.customPath = "my/expected/path";
I am developing a mobile app and I want to open a splash screen or open startup page when I receive a notification.
Currently, I am using the following code
static Future<dynamic> myBackgroundMessageHandler(
Map<String, dynamic> message) async {
print("_backgroundMessageHandler");
_callBgHandler = 1;
if (message.containsKey('data')) {
// Handle data message
final dynamic data = message['data'];
print("_backgroundMessageHandler: ${data}");
DeviceApps.openApp("com.example.my_flutter_app");
}
if (message.containsKey('notification')) {
// Handle notification message
final dynamic notification = message['notification'];
print("_backgroundMessageHandler notification: ${notification}");
}
return Future<void>.value();
}
and the following error displayed
Attempt to invoke virtual method 'android.content.pm.PackageManager
android.app.Activity.getPackageManager()' on a null object reference
The last one month tried but no luck. If anyone knows let me share the code from flutter dart language
But Button press event it will work.
RaisedButton(
onPressed: () {
DeviceApps.openApp("com.example.my_flutter_app");
},
child: Text("Open App")
)
Default applications like a map, chrome, play store, email app, etc... All are open with no issue but the external app does not open notification background function.
I want to transite to Instagram profile, when I am tapping on button. I use this library url_launcher. But there I can use only Web Browser for this. What will I do, in order to reach my goal?
To open Native and WebView Instagram:
Add to your iOS/Runner/Info.plist:
<key>LSApplicationQueriesSchemes</key>
<array>
...
<string>instagram</string>
</array>
Install url_launcher ( https://pub.dev/packages/url_launcher )
Import
import 'package:url_launcher/url_launcher.dart';
Create some method like this;
Inside Widget for example:
_launchInstagram() async {
const nativeUrl = "instagram://user?username=severinas_app";
const webUrl = "https://www.instagram.com/severinas_app/";
if (await canLaunch(nativeUrl)) {
await launch(nativeUrl);
} else if (await canLaunch(webUrl)) {
await launch(webUrl);
} else {
print("can't open Instagram");
}
}
Well, I don't know if it's too late, but this works for me using the url_launcher library (tested on android only):
var url = 'https://www.instagram.com/<INSTAGRAM_PROFILE>/';
if (await canLaunch(url)) {
await launch(
url,
universalLinksOnly: true,
);
} else {
throw 'There was a problem to open the url: $url';
}
2022 update:
_launchInstagram() async {
var nativeUrl = "instagram://user?username=balkan.exe";
var webUrl = "https://www.instagram.com/balkan.exe";
try {
await launchUrlString(nativeUrl, mode: LaunchMode.externalApplication);
} catch (e) {
print(e);
await launchUrlString(webUrl, mode: LaunchMode.platformDefault);
}
}
I am not sure if you can launch the profile activity, unless you know the name (which might change in the future). But, if you can try launching the app by using Intent plugin:
Try adding the Intent plugin:
https://pub.dev/packages/intent
Add intent in you in your pubspec.yaml file.
You can call the specific app with the package name (in this case Instagram).
Intent()
..setAction(Action.ACTION_SHOW_APP_INFO)
..putExtra(Extra.EXTRA_PACKAGE_NAME, "instagram package name")
..startActivity().catchError((e) => print(e));
You can also make use Android flutter plugin for Intent:
https://pub.dev/packages/android_intent
This will support for Android :
Use it by specifying action, category, data and extra arguments for the intent. It does not support returning the result of the launched activity
For IOS url_launcher plugin can be used for deep linking
You can create a platform channel and use the intent to achieve that or use social_share package.
Install url_launcher ( https://pub.dev/packages/url_launcher )
They now have a mode parameter, which you set to LaunchMode.externalApplication;
Call it with the regular https url, if they have the app installed it will open the app, otherwise it will open in Safari. Don't worry about doing an app link and a website url, the package and the OS handle it accordingly.
For example, here are my links in our About section of the app to our socials..
SettingsListTileModel(
// This can be any website's url that has a corresponding app, we us it with Twitter, Facebook, Instagram, and Youtube.
onTap: () => launchUrl(
Uri.parse('https://www.instagram.com/forwheel_app/'),
mode: LaunchMode.externalApplication,
),
title: Text(
'Instagram',
style: context.bodyLarge),
),
trailing: Icon(
FontAwesomeIcons.squareArrowUpRight,
),
),