Loading URL with unique Android or iOS device id in webview - android-studio

I know how to get the device unique id in flutter, but when I add it as a parameter to url and call it with webview, I can't get the result I want. When I call the following function, webview is loaded with empty device id parameter because it is working ASYNC and not yet completed and not returning any value.
Future<String> _getId() async {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
if (Theme.of(context).platform == TargetPlatform.iOS) {
IosDeviceInfo iosDeviceInfo = await deviceInfo.iosInfo;
return iosDeviceInfo.identifierForVendor; // unique ID on iOS
} else {
AndroidDeviceInfo androidDeviceInfo = await deviceInfo.androidInfo;
return androidDeviceInfo.androidId; // unique ID on Android
}
}
The webview loading function is below;
String webViewUrl;
_getId().then((id) {
webViewUrl = "http://websiteblabla.com/?deviceid="+id;
return WebviewScaffold(
url: webViewUrl,
withJavascript: true,
withZoom: false
);
}
How can I get URL with unique Android or iOS device id in webview when it is ready?

You can create Future function or method which will return WebviewScaffold widget when everything is ready. And the use it with FutureBuilder. It would look something like this.
FutureBuilder(
future: _buildWebview(),
builder: (context, snapshot) {
if(!snapshot.hasData) return Center(
child: CircularProgressIndicator(),
);
return snapshot.data;
},
),
Future<Widget> _buildWebview() async {
await _getId().then((id) {
webViewUrl = "http://websiteblabla.com/?deviceid="+id;
return WebviewScaffold(
url: webViewUrl,
withJavascript: true,
withZoom: false
);
}
}

Related

How to add or delete item from a list in MongoDB without refreshing the frontend

guys
I'm trying to Create a list to add and delete items which are already stored in my database(MongoDB). I already created an Animated list that once its clicked on it deletes the item but as soon as i add the function to remove the item from the database it begins to show a loader as I used a ListView.builder
FutureBuilder(
builder: (context, projectSnap) {
if (projectSnap.connectionState == ConnectionState.none &&
projectSnap.hasData == null) {
//print('project snapshot data is: ${projectSnap.data}');
return Text('Nothing To show Here');
}else if(
projectSnap.connectionState == ConnectionState.waiting
) {
return Center(child: CircularProgressIndicator());
} else if(
projectSnap.connectionState == ConnectionState.active
) {
return AnimatedList(
padding: EdgeInsets.zero,
key: _listKey,
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
initialItemCount: pairlenght,
itemBuilder: (BuildContext context, int index, animation) {
pair1 = pair[index].split(" ")[1];
pair0 = pair[index].split(" ")[0];
return _buildItem(pair, index, animation);
}
);
}
else{
return AnimatedList(
padding: EdgeInsets.zero,
key: _listKey,
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
initialItemCount: pairlenght,
itemBuilder: (BuildContext context, int index, animation) {
pair1 = pair[index].split(" ")[1];
pair0 = pair[index].split(" ")[0];
return _buildItem(pair, index, animation);
}
);
}
},
future: fetchlist(),
),
This is the code for the list
void _removeSingleItems(int index) {
int removeIndex = index;
String removedItem = pair.removeAt(removeIndex);
// This builder is just so that the animation has something
// to work with before it disappears from view since the original
// has already been deleted.
AnimatedListRemovedItemBuilder builder = (context, animation) {
// A method to build the Card widget.
return _buildItem(removedItem, index, animation);
};
_listKey.currentState?.removeItem(removeIndex, builder);
}
This is the code to delete the item using Animated list
This is the how it works
So what I actually need is just a better alternative if listview.builder won't do the job.
Cause whenever I delete it loads again which I don't want

how to stream user query instead of awaiting it

so, I am using a search Delegate to allow user search, however, when user is inputting data, it's really slow, it lags and skips frames as they type, because my search delegate loops through a list that contains about 2000 objects and checks user query against each object's title and main text, making about 4000 search criteria in total, this was causing the lag, so to solve this, I simply spawned a new isolate to handle the suggestions building, as such
Future<List<Data>> allData(String query) async {
List<Data> suggestions;
List<Data> myData(String message) {
return allData.where((data) {
final result = data.dataText.replaceAll(RegExp('[^A-Za-z0-9 ]'), '').toLowerCase();
final result1 = data.dataText.replaceAll(RegExp('[^A-Za-z0-9]'), '').toLowerCase();
final result2 = data.dataText.toLowerCase();
final result3 = data.dataText.replaceAll("th'incarnate", "the incarnate").toLowerCase();
final input = query.toLowerCase();
final result4 = data.dataTitle.toLowerCase();
final result5 = data.dataTitle.replaceAll(RegExp('[^A-Za-z0-9 ]'), '').toLowerCase();
final result6 =data.dataTitle.replaceAll(RegExp('[^A-Za-z0-9]'), '').toLowerCase();
return result.contains(input) ||
result1.contains(input) ||
result2.contains(input) ||
result3.contains(input) ||
result4.contains(input) ||
result5.contains(input) ||
result6.contains(input);
}).toList();
}
query.isEmpty
? suggestions = []
: suggestions = await compute<String, List<Data>>(
myData,
"",
);
if (query.isNotEmpty && suggestions.isEmpty) {
return [
Data(
DataTitle: "No Results",
DataText: "No Results",
),
];
} else {
return suggestions;
}
}
then in buildSuggestions I did this
#override
Widget buildSuggestions(BuildContext context) {
return FutureBuilder<List<Data>>(
future: DataBrain().allData(query),
builder: (context, snapshot) {
List<Data> suggestions;
query.isEmpty ? suggestions = [] : suggestions = snapshot.data!;
return ListView.builder(
itemCount: suggestions.length,
itemBuilder: (context, suggestIndex) {
final suggestion = suggestions[suggestIndex];
if (snapshot.connectionState == ConnectionState.done) {
if (suggestion.dataText.toLowerCase() == "no results") {
return const ListTile(
title: Text("No results"),
subtitle: Text("Please check for spelling mistakes"),
);
} else {
return ListTile(
title: Text(suggestion.dataTitle),
trailing: Text(
'${suggestion.dataBookName} ${suggestion.dataBookNumber == 0 ? '30a' : suggestion.dataBookNumber}',
style: const TextStyle(
fontStyle: FontStyle.italic,
fontWeight: FontWeight.w300,
),
),
onTap: () {
close(context, suggestion);
},
);
}
} else {
return const ListTile(
title: Text('Loading...'),
);
}
});
});
}
it works fine without lags, however now, the suggestions don't build as user types, it waits till user is no longer typing, for like 2 seconds, then it builds suggestions, I think this is because it's a future and in a future builder, I'm not sure why. I'm thinking of turning it into a stream rather and listen to query change and build immediately instead of awaiting for two seconds after user stops querying, is there any workaround, what's a good way to eliminate the delay, i need it to filter the list and build suggestions with each keystroke, as it used to do before i moved it to a new isolate and made it a future

Creating multiple Flutter nofitications with flutter_local_notification

Currently I'm using a loading page to avoid having a big lag when the user is being redirected to the home page while the notifications are being created on the same thread (the UI thread which leads to lots of dropped frames).
I tried using the compute dart function but the issue is that this function requires using static methods and you can't pass it objects.
So I would appreciate some hints on how to use a thread to create the notifications.
PS: in the worst scenario the app is creating 7*24 notifications(24 for each day of the week) which is slow even on high end devices.
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter/material.dart';
import '../pages/home_page/home_page.dart';
import 'package:timezone/data/latest.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
import 'data.dart';
import 'package:flutter_native_timezone/flutter_native_timezone.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:auto_size_text/auto_size_text.dart';
class NotificationLoading extends StatefulWidget {
const NotificationLoading({Key? key}) : super(key: key);
#override
_NotificationLoadingState createState() => _NotificationLoadingState();
}
class _NotificationLoadingState extends State<NotificationLoading> {
#override
void initState() {
super.initState();
manageNotifications();
}
Future<void> manageNotifications() async {
await Future.delayed(
const Duration(seconds: 1),
); // Let time to build the widget
await Notifications(ctx: context).manageNotifications();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
CircularProgressIndicator(),
Padding(
padding: EdgeInsets.fromLTRB(0, 15, 0, 0),
child: AutoSizeText(
"Loading notifs",
style: TextStyle(fontSize: 30),
),
)
],
),
),
);
}
}
class Notifications {
static const channelId = "coolID";
static const channelName = "cool";
Data data = Data();
int id = 0;
BuildContext ctx;
Notifications({required this.ctx});
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
// check if notifications are already setup, if not setup notifications
// otherwise notifications only need to be changed inside the timer_page
Future<void> manageNotifications() async {
final prefs = await SharedPreferences.getInstance();
bool isNotificationSetup = prefs.getBool('isNotificationSetup') ?? false;
if (!isNotificationSetup) {
await _initialization();
await _scheduleNotifications();
await prefs.setBool('isNotificationSetup', true);
Navigator.pop(ctx);
await Navigator.push(
ctx,
MaterialPageRoute<void>(builder: (context) => const HomePage()),
);
}
}
Future<void> _initialization() async {
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('app_icon');
const InitializationSettings initializationSettings =
InitializationSettings(
android: initializationSettingsAndroid,
);
await flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: _selectNotification);
}
// Schedule notifications based on user settings
Future<void> _scheduleNotifications() async {
// Init the time zone, needed for notification scheduling
tz.initializeTimeZones();
final String? timeZoneName = await FlutterNativeTimezone.getLocalTimezone();
tz.setLocalLocation(tz.getLocation(timeZoneName!));
await data.getData();
int delta = (data.endTime.minute + data.endTime.hour * 60) -
(data.startTime.minute + data.startTime.hour * 60);
double interval = delta / data.reminderNumber;
data.checkedDays.forEach((day, values) {
if (values[1]) {
double minute = data.startTime.minute + (data.startTime.hour * 60);
for (int reminder = 0; reminder < data.reminderNumber; reminder++) {
int tmpHour = (minute - minute % 60) ~/ 60;
int tmpMinute = (minute.round()) % 60;
_createScheduledNotification(
_nextInstanceOfDayHourMinute(tmpHour, tmpMinute, values[0]), id);
minute += interval;
id++;
}
}
});
}
// Create a scheduled notification
void _createScheduledNotification(tz.TZDateTime time, int id) async {
await flutterLocalNotificationsPlugin.zonedSchedule(
id,
AppLocalizations.of(ctx)!.notificationTitle,
AppLocalizations.of(ctx)!.notificationMessage,
time,
const NotificationDetails(
android: AndroidNotificationDetails(
'weekly notification channel id',
'New citation message',
channelDescription:
'Notifications for new citations configured in the timer page.',
sound: RawResourceAndroidNotificationSound('notification_sound'),
groupKey: "meditation invitation",
),
),
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
matchDateTimeComponents: DateTimeComponents.dayOfWeekAndTime);
}
// Find next instance DateTime object
tz.TZDateTime _nextInstanceOfHourMinute(int hour, int minute) {
final tz.TZDateTime now = tz.TZDateTime.now(tz.local);
tz.TZDateTime scheduledDate =
tz.TZDateTime(tz.local, now.year, now.month, now.day, hour, minute);
if (scheduledDate.isBefore(now)) {
scheduledDate = scheduledDate.add(const Duration(days: 1));
}
return scheduledDate;
}
// Find next instance DateTime object
tz.TZDateTime _nextInstanceOfDayHourMinute(int hour, int minute, int day) {
tz.TZDateTime scheduledDate = _nextInstanceOfHourMinute(hour, minute);
while (scheduledDate.weekday != day) {
scheduledDate = scheduledDate.add(const Duration(days: 1));
}
return scheduledDate;
}
// triggered function when the user tap on a notification
void _selectNotification(String? payload) async {
if (payload != null) {
debugPrint('notification payload: $payload');
}
await Navigator.push(
ctx,
MaterialPageRoute<void>(builder: (context) => const HomePage()),
);
}
}
You can't handle too many notifications at time in iOS platform. Instead of that you need again set or schedule notifications after few old notification received otherwise old notifications overlaps & it will not schedule.
You can check this link & my answer I hope it will help you.
Dart/Flutter is single threaded and not possible multi threading. As each isolate has its own memory,space and everything. To make it work like multi threaded you have to use isolates and the communication will be used through ports by sending message to one another. If you not want to use Future you can use isolates.
Read
https://medium.com/flutter-community/flutter-threading-5c3a7b0c065f
https://www.tutorialspoint.com/dart_programming/dart_programming_concurrency.htm
https://pub.dev/packages/threading
So you can use Future Or isolate.
Great François. You already in a good path. I guess i could give you some hints to accomplish what you want.
First of all Flutter require top level functions to run isolates so you have to put it outside of a class.
To transfer data to an isolate it must be serialized. In my example i use jsonEncode to send it as a string and parse it with jsonDecode to retrieve as a dynamic list inside the isolate runner.
When i wrote this code i read about some limitations of the use of plugins inside isolate (I don`t know about the current state). So i found a solution for this using Flutter Isolate plugin (https://pub.dev/packages/isolate_handler)
I use the function killScheduleNotifications as a way to control when the code is running and avoid to create duplications. I always cancel all the scheduled and recreate it every time.
For reference (https://gist.github.com/taciomedeiros/50472cf94c742befba720853e9d598b6)
final IsolateHandler isolateHandler = IsolateHandler();
void scheduleNotificationsIsolate(String _reminders) async {
await new Future.delayed(new Duration(milliseconds: 500));
// ... (describe settings)
flutterLocalNotificationsPlugin.initialize(
settings,
onSelectNotification: onSelectNotification,
);
await flutterLocalNotificationsPlugin.cancelAll();
List<dynamic> _remindersParsed = jsonDecode(_reminders);
for (// iterate over your entities to show the message) {
int generatedId = id ?? random.nextInt(1000000000);
await flutterLocalNotificationsPlugin.schedule(
generatedId,
title,
message,
scheduledNotificationDateTime,
platformSpecifics,
payload: payload,
);
}
killCurrentScheduleNotifications();
}
startScheduleNotifications(String _remindersAsString) {
killCurrentScheduleNotifications();
isolateHandler.spawn<String>(
entryPoint,
name: "scheduleNotifications",
onReceive: scheduleNotificationsIsolate,
onInitialized: () => isolateHandler.send(
_remindersAsString,
to: "scheduleNotifications",
),
);
}
void killCurrentScheduleNotifications() {
if (isolateHandler.isolates.containsKey('scheduleNotifications'))
isolateHandler.kill('scheduleNotifications');
}
void entryPoint(Map<String, dynamic> context) {
final messenger = HandledIsolate.initialize(context);
messenger.listen((message) {
messenger.send(message);
});
}

how to implement string list from _ListItem to assetPath?

i've been working with wallpaper app.
i manage to call the path list for wallpaper,
i can make this work on image card list but i can't manage to do it work with assetPath for WallpaperManager
Please help, is there any way to do it?
Future<void> setWallpaperFromAsset() async {
setState(() {
_wallpaperAsset = "Loading";
});
String result;
String assetPath = ('Load List here');
You can add a parameter assetPath to setWallpaperFromAsset
Future<void> setWallpaperFromAsset(String assetPath) async {
setState(() {
_wallpaperAsset = "Loading";
});
String result;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
result = await WallpaperManager.setWallpaperFromAsset(
assetPath, WallpaperManager.HOME_SCREEN);
} on PlatformException {
result = 'Failed to get wallpaper.';
}
}
And you register the onPressed callback with:
RaisedButton.icon(
onPressed: () => setWallpaperFromAsset(item),
icon: Icon (Icons.wallpaper, size: 20),
label: Text('homescreen')),
)

How to define different context menus for different objects in autodesk forge

I want to define different context menus for different objects in forge viewer,this is my code
viewer.addEventListener(Autodesk.Viewing.AGGREGATE_SELECTION_CHANGED_EVENT,function(e){
if(viewer.getSelection().length==0){return;}
var selectId=viewer.getSelection()[0];
viewer.search("Cabinet",function(ids){
if(ids.indexOf(selectId)!=-1){
viewer.registerContextMenuCallback('CabinetMsg', function (menu, status) {
if (status.hasSelected) {
menu.push({
title: "CabinetMsg",
target: function () {
openLayer('CabinetMsg','954','775','CabinetMsg.html')
}
});
}
});
}else{
viewer.registerContextMenuCallback('CabinetMsg', function (menu, status) {
if (status.hasSelected) {
menu.forEach(function(el,index){
if(el.title=="CabinetMsg"){
menu.splice(menu.indexOf(index),1)
}
})
}
});
}
})
});
But push elements to the array is always later than the context menus show. My custom context menu is always show when I select another object. What I can do?
The codes you provided will create 2 new sub items to the context menu. Here is a way for this case, i.e. you have to write your own ViewerObjectContextMenu. In addition, you need do hitTest in ViewerObjectContextMenu.buildMenu to get dbId selected by the mouse right clicking. Here is the example for you:
class MyContextMenu extends Autodesk.Viewing.Extensions.ViewerObjectContextMenu {
constructor( viewer ) {
super( viewer );
}
isCabinet( dbId ) {
// Your logic for determining if selected element is cabinet or not.
return false;
}
buildMenu( event, status ) {
const menu = super.buildMenu( event, status );
const viewport = this.viewer.container.getBoundingClientRect();
const canvasX = event.clientX - viewport.left;
const canvasY = event.clientY - viewport.top;
const result = that.viewer.impl.hitTest(canvasX, canvasY, false);
if( !result || !result.dbId ) return menu;
if( status.hasSelected && this.isCabinet( result.dbId ) ) {
menu.push({
title: 'CabinetMsg',
target: function () {
openLayer( 'CabinetMsg', '954', '775', 'CabinetMsg.html' );
}
});
}
return menu;
}
}
After this, you could write an extension to replace default viewer context menu with your own menu. Here also is the example:
class MyContextMenuExtension extends Autodesk.Viewing.Extension {
constructor( viewer, options ) {
super( viewer, options );
}
load() {
this.viewer.setContextMenu( new MyContextMenu( this.viewer ) );
return true;
}
unload() {
this.viewer.setContextMenu( new Autodesk.Viewing.Extensions.ViewerObjectContextMenu( this.viewer ) );
return true;
}
}
Hope this help.

Resources