enter image description hereI want to fetch api and send response as notification everyday, even when my flutter app is not opened. So, I use flutter local notification and flutter workmanager packages. But it can not exactly works. **Here is my main.dart code : **
import 'package:flutter/services.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:message_app/constants/constants.dart';
import 'package:message_app/controller_bindings.dart';
import 'package:message_app/routes.dart';
import 'package:message_app/screens/welcome_screen.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:message_app/services/getx_api_services.dart';
import 'package:shurjopay/utilities/functions.dart';
import 'package:get/get_navigation/src/root/get_material_app.dart';
import 'package:workmanager/workmanager.dart';
const simplePeriodicTask = "simplePeriodicTask";
// flutter local notification setup
void _showNotificationWithDefaultSound(v, flp) async {
var androidPlatformChannelSpecifics = AndroidNotificationDetails(
'channel id', 'channel NAME',
channelDescription: 'CHANNEL DESCRIPTION',
priority: Priority.high,
importance: Importance.max);
// var iOS = IOSNotificationDetails();
var platform = NotificationDetails(android: androidPlatformChannelSpecifics);
await flp.show(0, 'Message', '$v', platform, payload: 'Loading!!');
}
Future main() async {
await dotenv.load();
initializeShurjopay(environment: "live");
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(statusBarColor: Constants.primaryTealDark),
);
WidgetsFlutterBinding.ensureInitialized();
await Workmanager().initialize(callbackDispatcher,
isInDebugMode:
true); //to true if still in testing lev turn it to false whenever you are launching the app
await Workmanager().registerPeriodicTask("5", simplePeriodicTask,
existingWorkPolicy: ExistingWorkPolicy.replace,
frequency: Duration(minutes: 15), //when should it check the link
initialDelay:
Duration(seconds: 5), //duration before showing the notification
constraints: Constraints(
networkType: NetworkType.connected,
));
runApp(const MyApp());
}
void callbackDispatcher() {
Workmanager().executeTask((task, inputData) async {
FlutterLocalNotificationsPlugin flp = FlutterLocalNotificationsPlugin();
var android = AndroidInitializationSettings('#mipmap/ic_launcher');
// var iOS = IOSInitializationSettings();
var initSetttings = InitializationSettings(android: android);
flp.initialize(initSetttings);
GetXApiServices api = GetXApiServices();
api
.fetchDailyAyah((api.mon % 3) == 0
? api.num * api.mon + api.year - 2000
: api.num * api.mon + api.year + 500)
.then((_) {
_showNotificationWithDefaultSound(flp, api.ayah.last.arText);
});
return Future.value(true);
});
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
return GetMaterialApp(
title: 'Message',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.teal,
primaryColor: Colors.teal.shade700,
scaffoldBackgroundColor: Colors.white70,
),
// initialRoute: '/',
routes: customRoutes,
initialBinding: ControllerBindings(),
home: const WelcomeScreen(),
);
}
}
And here is my getx_api_services.dart code:
import 'package:message_app/constants/constants.dart';
import 'package:message_app/models/aya_of_the_day.dart';
import 'package:http/http.dart' as http;
import 'package:message_app/models/surah.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:message_app/models/prayer_time.dart';
class GetXApiServices extends GetxController {
int num = int.parse(DateFormat('d').format(DateTime.now()));
int mon = int.parse(DateFormat('MM').format(DateTime.now()));
int year = int.parse(DateFormat('yyyy').format(DateTime.now()));
#override
void onInit() async {
super.onInit();
await fetchDailyAyah(
(mon % 3) == 0 ? num * mon + year - 2000 : num * mon + year + 500);
}
List<Surah> surahList = [];
List<Surah> msgSurah = [
Surah(
number: 0,
name: " ",
englishName: " ",
englishNameTranslation: " ",
numberofAyahs: 0,
revelationType: " ",
),
];
var isLoading = true.obs;
var ayah = <AyaOfTheDay>[].obs;
var prayerTime = <PrayerTime>[].obs;
var bhasha = false.obs;
// late Rx<AyaOfTheDay> ayahlatest;
Future<void> fetchDailyAyah(int day) async {
String url = "http://api.alquran.cloud/v1/ayah/$day/editions/bn.bengali";
String enUrl = "https://api.alquran.cloud/v1/ayah/$day/editions/en.asad";
String urlforSurah = "http://api.alquran.cloud/v1/surah";
final response = await http.get(Uri.parse(url));
final enResponse = await http.get(Uri.parse(enUrl));
if (response.statusCode == 200 && enResponse.statusCode == 200) {
AyaOfTheDay albumModel = AyaOfTheDay.fromJson(jsonDecode(response.body));
AyaOfTheDay albumModelEn =
AyaOfTheDay.fromJson(jsonDecode(enResponse.body));
final res = await http.get(Uri.parse(urlforSurah));
if (response.statusCode == 200 && res.statusCode == 200) {
// print("from API Call");
Map<String, dynamic> jsonSurah = jsonDecode(res.body);
jsonSurah['data'].forEach((element) {
if (surahList.length < 114) {
surahList.add(Surah.fromJson(element));
}
});
Constants.surahBackup = surahList;
ayah.add(
AyaOfTheDay(
arText: albumModel.arText,
surEnName: albumModel.surEnName,
surahSerial: albumModel.surahSerial,
surNumber: albumModel.surNumber,
revelationType: albumModel.revelationType,
),
);
ayah.add(
AyaOfTheDay(
arText: albumModelEn.arText,
surEnName: albumModelEn.surEnName,
surahSerial: albumModelEn.surahSerial,
surNumber: albumModelEn.surNumber,
revelationType: albumModelEn.revelationType,
),
);
isLoading.value = false;
update();
} else {
Get.snackbar('Error Loading data!',
'Sever responded: ${response.statusCode}:${response.reasonPhrase.toString()}');
}
}
}
}
**here is my debug console log: **
Building with Flutter multidex support enabled.
√ Built build\app\outputs\flutter-apk\app-debug.apk.
D/FlutterGeolocator(10751): Attaching Geolocator to activity
D/FlutterGeolocator(10751): Creating service.
D/FlutterGeolocator(10751): Binding to location service.
D/FlutterLocationService(10751): Creating service.
D/FlutterLocationService(10751): Binding to location service.
D/FlutterGeolocator(10751): Geolocator foreground service connected
D/FlutterGeolocator(10751): Initializing Geolocator services
D/FlutterGeolocator(10751): Flutter engine connected. Connected engine count 1
I/ple.message_ap(10751): ProcessProfilingInfo new_methods=2499 is saved saved_to_disk=1 resolve_classes_delay=5000
I/ple.message_ap(10751): Compiler allocated 4670KB to compile void android.view.ViewRootImpl.performTraversals()
Connecting to VM Service at ws://127.0.0.1:4096/-u3EjpN6CA4=/ws
[GETX] Instance "GetXApiServices" has been created
[GETX] Instance "GetXApiServices" has been initialized
[GETX] Instance "GetMaterialController" has been created
[GETX] Instance "GetMaterialController" has been initialized
E/ion (10751): ioctl c0044901 failed with code -1: Invalid argument
E/libEGL (10751): Invalid file path for libcolorx-loader.so
I/chatty (10751): uid=10842(com.example.message_app) identical 1 line
E/libEGL (10751): Invalid file path for libcolorx-loader.so
E/libEGL (10751): Invalid file path for libcolorx-loader.so
I/chatty (10751): uid=10842(com.example.message_app) 2.io identical 58 lines
E/libEGL (10751): Invalid file path for libcolorx-loader.so
D/FlutterGeolocator(10751): Geolocator foreground service connected
D/FlutterGeolocator(10751): Initializing Geolocator services
D/FlutterGeolocator(10751): Flutter engine connected. Connected engine count 2
W/FlutterJNI(10751): FlutterJNI.loadLibrary called more than once
W/FlutterJNI(10751): FlutterJNI.prefetchDefaultFontManager called more than once
I/ResourceExtractor(10751): Found extracted resources res_timestamp-1-1674402227534
W/FlutterJNI(10751): FlutterJNI.init called more than once
D/FlutterGeolocator(10751): Flutter engine disconnected. Connected engine count 1
D/FlutterGeolocator(10751): Disposing Geolocator services
E/FlutterGeolocator(10751): Geolocator position updates stopped
E/FlutterGeolocator(10751): There is still another flutter engine connected, not stopping location service
I/WM-WorkerWrapper(10751): Worker result SUCCESS for Work [ id=121d6b93-46cb-49a7-b71c-98c31114d5c8, tags={ be.tramckrijte.workmanager.BackgroundWorker } ]
I/GED (10751): ged_boost_gpu_freq, level 100, eOrigin 2, final_idx 31, oppidx_max 31, oppidx_min 0
I can not Understand why it is not working.
here is also my app notification screenshot:
Related
Service works properly on Android 9 and above facing issues on devices 8<=
Notification disappears in some duration and location services stops
I have also disabled battery optimization but it did not work
Is there anyway to perform background services on android 8 and below
My Code
#pragma('vm:entry-point')
Future<void> onStart(ServiceInstance service) async {
// Only available for flutter 3.0.0 and later
DartPluginRegistrant.ensureInitialized();
final DatabaseService databaseService = DatabaseService();
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
if (service is AndroidServiceInstance) {
service.on('setAsForeground').listen((event) {
service.setAsForegroundService();
});
service.on('setAsBackground').listen((event) {
service.setAsBackgroundService();
});
}
service.on('stopService').listen((event) {
service.stopSelf();
});
final stream = Geolocator.getPositionStream(
locationSettings: const LocationSettings(
accuracy: LocationAccuracy.high,
distanceFilter: 0,
timeLimit: Duration(seconds: 10)
),
).listen((position) async {
if (kDebugMode) {
print('Position : $position');
}
flutterLocalNotificationsPlugin.show(
notificationId,
'COOL SERVICE',
position.latitude.toString() + position.longitude.toString() + DateTime.now().toString(),
const NotificationDetails(
android: AndroidNotificationDetails(
notificationChannelId,
'MY FOREGROUND SERVICE',
icon: 'ic_bg_service_small',
ongoing: true,
),
),
);
var now = DateTime.now();
//Month is capital MM to distinct 'minute and month'
//HH means 24 hour format
var formatter = DateFormat('yyyy-MM-dd HH:mm:ss');
var route = MapRoute(createdAt:formatter.format(now),latitude: position.latitude, longitude: position.longitude);
try {
await databaseService.insertBreed(route);
} catch (e) {
if (kDebugMode) {
print(e);
}
}});
}
Thankyou
I found the solution to make Background services work on Android Devices less than 9. I had to disable AllBatteryOptimzation for current app.
Do this
DisableBatteryOptimization.showDisableAllOptimizationsSettings('App Process', 'Enable App Battery Usage', 'Battery Optimization', 'Enable process');
Instead of this
DisableBatteryOptimization.showDisableBatteryOptimizationSettings();
I'm creating a chat app (kind of WhatsApp-like messaging) using Flutter.
First, the notifications mechanism is working as intended, whenever I send a message from 1 device to another device, the notification would pop up.
I created a local_notification_service.dart to handle the foreground notification & sending a not
import 'dart:math';
import 'package:get/get.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
class LocalNotificationService extends GetConnect {
String serverKey ='xxxxxxxxxxxxxxxxxxxx'
static final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
static void initialize() {
const InitializationSettings initializationSettings = InitializationSettings(android: AndroidInitializationSettings("#mipmap/ic_launcher"));
_flutterLocalNotificationsPlugin.initialize(initializationSettings);
}
static void display(RemoteMessage message) async {
try {
print("Display notification");
// int id = DateTime.now().microsecondsSinceEpoch ~/1000000;
Random random = Random();
int id = random.nextInt(1000);
const NotificationDetails notificationDetails = NotificationDetails(
android: AndroidNotificationDetails(
"mychanel",
"my chanel",
importance: Importance.max,
priority: Priority.high,
));
print("my id is ${id.toString()}");
await _flutterLocalNotificationsPlugin.show(
id,
message.notification!.title,
message.notification!.body,
notificationDetails,
);
} on Exception catch (e) {
print('Error>>>$e');
}
}
Future<void> sendNotification({
String? title,
String? message,
String? token,
String? uniqueId,
String? action,
String? channelId,
String? channelName,
String? channelDesc,
}) async {
final data = {
"click_action": "FLUTTER_NOTIFICATION_CLICK",
"action": action,
"uniqueId": uniqueId,
"message": message,
"channelId": channelId ?? 'my channel id',
"channelName": channelName ?? 'my channel Name',
"channelDesc": channelDesc ?? 'my channel description',
};
try {
final response = await post(
'https://fcm.googleapis.com/fcm/send',
{
'notification': {'title': title, 'body': message},
'priority': 'high',
'data': data,
'to': '$token',
'direct_boot_ok': true,
},
headers: {
'Content-Type': 'application/json',
'Authorization': 'key=$serverKey',
},
);
print('response body : ${response.body}');
} catch (e) {}
}
}
Then, I'm trying to validate the users in my flutter application whenever they receive FCM notification, here's the logic that I want to create:
If the user is not logged in, then the device could not receive the notification
If the user is logged in, but the specific user is not eligible to receive the message (in case there are some users with the same FCM token / device registered ) then the device could not receive the notification. I would want to solve this after the point number 1 is succeeded
Here's my main.dart file
void main() async {
await GetStorage.init();
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
GlobalController globalC = Get.put(GlobalController());
AuthController authC = Get.put(AuthController());
ErrorController errorC = Get.put(ErrorController());
ConnectivityResult connectivityResult = ConnectivityResult.none;
final Connectivity connectivity = Connectivity();
connectivityResult = await connectivity.checkConnectivity();
if (connectivityResult == ConnectivityResult.wifi || connectivityResult == ConnectivityResult.mobile) {
// Start FCM
final fcmToken = await FirebaseMessaging.instance.getToken();
globalC.fcmToken.value = fcmToken ?? ''; //set global fcm Token
final FirebaseMessaging fcmInstance = FirebaseMessaging.instance;
NotificationSettings settings = await fcmInstance.requestPermission(
alert: true,
announcement: true,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
/* Handle message when in foreground */
FirebaseMessaging.onMessage.listen((event) {
if (globalC.isAuthenticated.isTrue) {
LocalNotificationService.display(event); //display notification
}
});
/* Handle message when in background */
if (globalC.isAuthenticated.isTrue) {
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
}
fcmInstance.onTokenRefresh.listen((fcmToken) {
// Note: This callback is fired at each app startup and whenever a new
// token is generated.
}).onError((err) {
// Error getting token.
});
// End FCM
}
FirebaseAnalytics analytics = FirebaseAnalytics.instance;
runApp(MyApp());
}
as you can see, I'm trying to filter the non logged in user when in the foreground using the FirebaseMessaging.onMessage.listen with the globalC.isAuthenticated.isTrue validation. And it works (because the default of globalC.isAuthenticated is false whenever user is not logged in)
But for the FirebaseMessaging.onBackgroundMessage function does not seems to work with the validation. I've tried to search for the solution in the documentations, youtube but i couldn't find it till this question is made.
How can I make this kind of validation for background message?
Sorry for this newbie question, any help would be greatly appreciated.
Thank you .
so i'm creating an app like facebook,instagram ....
When i was in systeme notifications step, i managed to display notifications when the App state is Open or paused but not when the app is dettached (closed), i'm no longer able to recieve new local notifications.
i tried many packages like flutter_background_service and workmanager, i'm working with flutter_background_service now in debug mode all was good, but when i build the apk it's not working in background.
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
AwesomeNotifications().initialize('resource://drawable/appicon', [
NotificationChannel(
channelKey: 'basic_channel',
channelName: 'basic Notifications',
channelDescription: "channelDescription",
importance: NotificationImportance.High,
enableVibration: true,
channelShowBadge: true,
)
]);
await Firebase.initializeApp(
name: 'firebase',
options: const FirebaseOptions(//firebaseconfig),
);
await initializeService();
runApp(MyApp());
}
Future<void> initializeService() async {
final service = FlutterBackgroundService();
await service.configure(
androidConfiguration: AndroidConfiguration(
// this will be executed when app is in foreground or background in separated isolate
// auto start service
autoStart: true,
onStart: onStart,
isForegroundMode: false,
),
iosConfiguration: IosConfiguration(
// auto start service
autoStart: true,
// this will be executed when app is in foreground in separated isolate
onForeground: onStart,
// you have to enable background fetch capability on xcode project
onBackground: onIosBackground,
),
);
service.startService();
}
// to ensure this is executed
// run app from xcode, then from xcode menu, select Simulate Background Fetch
bool onIosBackground(ServiceInstance service) {
WidgetsFlutterBinding.ensureInitialized();
print('FLUTTER BACKGROUND FETCH');
return true;
}
void onStart(ServiceInstance service) async {
DartPluginRegistrant.ensureInitialized();
if (service is AndroidServiceInstance) {
service.on('setAsForeground').listen((event) {
service.setAsForegroundService();
});
service.on('setAsBackground').listen((event) {
service.setAsBackgroundService();
});
}
service.on('stopService').listen((event) {
service.stopSelf();
});
// bring to foreground
Timer.periodic(const Duration(seconds: 1), (timer) async {
/// you can see this log in logcat
print('FLUTTER BACKGROUND SERVICE is running : ${DateTime.now()}');
//////////////////////////////////////////////////////////////////////////////////////////
I want somthing like this: keep 2 getxcontroller working even if the app detached
//////////////////////////////////////////////////////////////////////////////////////////
await Firebase.initializeApp(
name: 'firebase',
options: const FirebaseOptions(//firebaseconfig),
).whenComplete(() {
final notifc = Get.put(Notificationsc());
final msgl = Get.put(MessageslistsController());
});
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
if (service is AndroidServiceInstance) {
service.setForegroundNotificationInfo(
title: "My App Service",
content: "Updated at ${DateTime.now()}",
);
}
// test using external plugin
final deviceInfo = DeviceInfoPlugin();
String? device;
if (Platform.isAndroid) {
final androidInfo = await deviceInfo.androidInfo;
device = androidInfo.model;
}
if (Platform.isIOS) {
final iosInfo = await deviceInfo.iosInfo;
device = iosInfo.model;
}
service.invoke(
'update',
{
"current_date": DateTime.now().toIso8601String(),
"device": device,
},
);
});
}
I am working on work manager to fetxh api data and initiate a notification in flutter,
//this is the name given to the background fetch
const simplePeriodicTask = "simplePeriodicTask";
Workmanager workmanager = Workmanager();
// flutter local notification setup
Future initializeWorkManagerAndPushNotification() async {
await workmanager.initialize(
callbackDispatcher,
isInDebugMode: false,
); //to true if still in testing lev turn it to false whenever you are launching the app
await workmanager.registerPeriodicTask(
"1", simplePeriodicTask,
existingWorkPolicy: ExistingWorkPolicy.replace,
frequency: Duration(minutes: 1), //when should it check the link
initialDelay:
Duration(seconds: 5), //duration before showing the notification
constraints: Constraints(
networkType: NetworkType.connected,
),
);
}
void callbackDispatcher() async {
workmanager.executeTask((task, inputData) async {
print('Ruuning - callbackDispatcher');
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
var androidSettings = AndroidInitializationSettings('#mipmap/ic_launcher');
var iOSSettings = IOSInitializationSettings();
var initSetttings =
InitializationSettings(android: androidSettings, iOS: iOSSettings);
//
// TODO: Permission
//
await flutterLocalNotificationsPlugin.initialize(initSetttings);
String message =
await WebServiceController.getInstance.getPushNotificationMessage();
print("here 1 ================");
// print(response);
// var convert = json.decode(response.body);
// if (convert['status'] == true) {
// showNotification(convert['msg'], flutterLocalNotificationsPlugin);
// } else {
// print("no messgae");
// }
print("messgae");
print(message);
await showNotification(message, flutterLocalNotificationsPlugin);
return Future.value(true);
});
}
Future showNotification(payloadmessage, flutterLocalNotificationsPlugin) async {
print("showing notification");
var androidDetails = AndroidNotificationDetails(
'channel id', 'channel NAME', 'CHANNEL DESCRIPTION',
priority: Priority.high, importance: Importance.max);
var iOSDetails = IOSNotificationDetails();
var platform = NotificationDetails(android: androidDetails, iOS: iOSDetails);
await flutterLocalNotificationsPlugin.show(
0, 'Message - Virtual intelligent solution', '$payloadmessage', platform,
payload: 'OnClick Payload -VIS \n $payloadmessage');
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
await initializeWorkManagerAndPushNotification();
runApp(MyApp());
}
this function callbackDispatcher() function is used to Initialize Notification and fetch api data from below method.
Future<String> getPushNotificationMessage() async {
print("getting - getPushNotificationMessage");
try {
var response = await _dio.get(APIURLConstants.pushNotification);
print(response);
print("here================ ${response.data}");
var convert = json.decode(response.data);
return convert['message'].toString() ?? "";
} on DioError catch (error) {
print("Dio Error ${error.message}");
} catch (error) {
print("Error - ${error.toString()}");
}
}
as per the below output , It seems that only print(response); of getPushNotificationMessage() is executed after that below code is not executing,
Performing hot restart...
Restarted application in 2,266ms.
W/WM-WorkSpec(31092): Interval duration lesser than minimum allowed value; Changed to 900000
I/flutter (31092): getting - getPushNotificationMessage
I/flutter (31092): {"message":"Hi There I am notification","seen":0,"notification_id":"111"}
I/WM-WorkerWrapper(31092): Worker result SUCCESS for Work [ id=6c935b3b-bfe4-47fa-b30a-0965299f5224, tags={ be.tramckrijte.workmanager.BackgroundWorker } ]
I founded the issue, to make the code execute, I needed to hot restart the application to execute code from
main() method
As next week will have importat launch for Rust 2018 and Flutter 1.0, I thought to build an app using Rust for the business logic and Flutter for the user interface, that can run at both Android and iOS, I built one and tested it at Android and it is working fine.
I just wonder how to measure the performance and compare it with native Android/iOS app.
The app flow is:
Main is in Flutter, that is calling native function through platform_channel
The native function is calling rust library through JNI (JNI wrapper is required to be call the rust library)
The structure is as below:
The code used is:
main.dart:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
static const platform = const MethodChannel('samples.flutter.io/battery');
String _batteryLevel = 'Unknown battery level.';
Future<void> _getBatteryLevel() async {
String batteryLevel;
try {
final String hello = await platform.invokeMethod('getText');
final int result = await platform.invokeMethod('getBatteryLevel');
batteryLevel = '$hello Battery level at $result %.';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
#override
Widget build(BuildContext context) {
return Material(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
RaisedButton(
child: Text('Get Battery Level'),
onPressed: _getBatteryLevel,
),
Text(_batteryLevel),
],
),
),
);
}
}
JNI wrapper - RustGreetings.kt
package com.mozilla.greetings
class RustGreetings {
companion object {
init {
System.loadLibrary("greetings")
}
}
private external fun greeting(pattern: String): String
fun sayHello(to: String): String = greeting(to)
}
And the Main Android activity is:
package com.example.batterylevel
import android.os.Bundle
import io.flutter.app.FlutterActivity
import io.flutter.plugins.GeneratedPluginRegistrant
import io.flutter.plugin.common.MethodChannel
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import lib.Library
import com.mozilla.greetings.RustGreetings
class MainActivity: FlutterActivity() {
private val CHANNEL = "samples.flutter.io/battery"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this)
MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == "getText") {
result.success(getText())
} else if (call.method == "getBatteryLevel") {
// result.success(getText())
val batteryLevel = getBatteryLevel()
if (batteryLevel != -1) {
result.success(batteryLevel)
} else {
result.error("UNAVAILABLE", "Battery level not available.", null)
}
}
else {
result.notImplemented()
}
}
}
private fun getBatteryLevel(): Int {
val batteryLevel: Int
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
} else {
val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
}
return batteryLevel
}
private fun getText(): String {
val x = Library().someLibraryMethod()
val g = RustGreetings()
val r = g.sayHello("My $x Rust")
return r
}
}
In the Android gradle.build I just added the below, as I'm interested to check also the impact of adding kotlin JVM library and getting it interacted with the Rust library within the mobile application:
dependencies {
implementation(files("src/main/libs/lib.jar"))
}
My question is:
How can check the performance and impact of each process when it is executed or called by another process
With the introduction of ffi in Dart, things became more smoother now, with a better performance as the interction now is Dart/Rust directly, without a need for Dart/Kotlin/Rust or Dart/Swift/Rust cycle, below a simple example:
First src/lib.rs
#[no_mangle]
pub extern fn rust_fn(x: i32) -> i32 {
println!("Hello from rust\nI'll return: {}", x.pow(2));
x.pow(2)
}
and Cargo.toml
[package]
name = "Double_in_Rost"
version = "0.1.0"
authors = ["Hasan Yousef"]
edition = "2018"
[lib]
name = "rust_lib"
crate-type = ["dylib"] # could be `staticlib` as well
[dependencies]
Running cargo build --release will generate target\release\rust_lib.dll copy/paste it into Dart application root directory
Write Dart code as below:
import 'dart:ffi';
import 'dart:io' show Platform;
// FFI signature of the hello_world C function
typedef ffi_func = Int32 Function(Int32 x); //pub extern fn rust_fn(x: i32) -> i32
// Dart type definition for calling the C foreign function
typedef dart_func = int Function(int x);
void main() {
// Open the dynamic library
var path = './rust_lib.so';
if (Platform.isMacOS) path = './rust_lib.dylib';
if (Platform.isWindows) path = 'rust_lib.dll';
final dylib = DynamicLibrary.open(path);
// Look up the Rust/C function
final my_func =
dylib.lookup<NativeFunction<ffi_func>>('rust_fn').asFunction<dart_func>();
print('Double of 3 is ${my_func(3)}');
}