I need to access the call log of android mobile phone in my project and I don't have that much experience in Flutter. Found a package named 'call_log' but don't know how to implement. I would very much appreciate any help here.
Here is the readme file of 'call_log' package:
// IMPORT PACKAGE
import 'package:call_log/call_log.dart';
// GET WHOLE CALL LOG
Iterable<CallLogEntry> entries = await CallLog.get();
// QUERY CALL LOG (ALL PARAMS ARE OPTIONAL)
var now = DateTime.now();
int from = now.subtract(Duration(days: 60)).millisecondsSinceEpoch;
int to = now.subtract(Duration(days: 30)).millisecondsSinceEpoch;
Iterable<CallLogEntry> entries = await CallLog.query(
dateFrom: from,
dateTo: to,
durationFrom: 0,
durationTo: 60,
name: 'John Doe',
number: '901700000',
type: CallType.incoming,
);
What I could understand from your question is, you are unable to work with the output of CallLog.get() here.
After adding the package to pubspec.yaml file dependencies and importing it, you can call the get() function using the following line of code -
Iterable<CallLogEntry> entries = await CallLog.get();
It returns an Iterable of type CallLogEntry. An iterable is simply a collection of values, or "elements", that can be accessed sequentially.
The output gets stored in entries which can then be iterated over to access the values such as -
void _callLogs() async {
Iterable<CallLogEntry> entries = await CallLog.get();
for (var item in entries) {
print(item.name);
}
}
The above code snippet would print the names of all CallLog entries. Try replacing item.name with item.number, item.duration, item.callType.
Also, do not forget to add the following line to AndroidManifest.xml
<uses-permission android:name="android.permission.READ_CALL_LOG" />
Instead of CallLog.get(), you can also use CallLog.query() to specify constraints on the response/output as mentioned in the question itself.
Related
This question already has answers here:
How to Async/await in List.forEach() in Dart
(7 answers)
Closed 1 year ago.
I wrote this piece of code and it is causing some problems:
static getFriendsRequests({required id}) async {
QuerySnapshot snapshot = await friendsRef
.doc(id)
.collection('received')
.get();
List<friendRequestItem> feedItems = [];
snapshot.docs.forEach((doc) async{
DocumentSnapshot extracted = await usersRef.doc(doc.reference.id).get();
MyUser tempUser = MyUser.fromJsonDocument(extracted);
String url = '';
tempUser.profilePictureURL=='' ? null : url=tempUser.profilePictureURL;
FriendRequest fr = FriendRequest(
userID: tempUser.userID, uniqueName: tempUser.uniqueName,
name: tempUser.name, mediaURL: url);
print(fr.uniqueName+fr.name+fr.userID+fr.mediaURL);
feedItems.add(friendRequestItem(friendReq: fr));
});
return feedItems;}
To help reading the code, the first query is to get a list of documents, each referring to a unique id. In the second, for each document I search again in the database for some data of the given id. Now I noticed that the problem should be related to the async methods because all the data I get are correct but probably the return happens before the end of the second method. How can I solve? Tell me if you need more infos about other parts of the code. Thank you
I had the same problem (How to await a Map.forEach() in dart).
To solve this issue, you need to use a for (var mapEntry in map) {}. The map would be the map would be the map you are looping through. This also works for Lists or any Iterable<T>. See the question I linked for more detail.
Please let me know if you need anymore help or anything with my answer is not working.
sorry I am new in using Flutter and using Hive local storage.
I am using
hive: ^2.0.4
hive_flutter: ^1.0.0
I open the box in main function like this
Future<void> main() async {
await Hive.initFlutter();
await Hive.openBox<List<Event>>("events");
}
after getting the data from the server, I save all the events to hive by using code like this
final eventsBox = Hive.box<List<Event>>("events");
final List<Event> eventsFromServer = await getEventsFromServer();
eventsBox.put("recommended_events", eventsFromServer);
but I have error when trying to read the data from the box, I read it like this
final eventsBox = Hive.box<List<Event>>("events");
// error in this one line below
final eventsFromHive = eventsBox.get("recommended_events", defaultValue: []) ?? [];
type 'List < dynamic > ' is not a subtype of type 'List< Event >?' in type
cast
how to solve this type casting error?
from the documentation in here it is said
Lists returned by get() are always of type List (Maps of type
Map<dynamic, dynamic>). Use list.cast() to cast them to a
specific type.
I don't know if it is the solution of my problem or not, but I don't know how to implement that in my code.
I tried it like this, but I still have the same error
final eventsFromHive = eventsBox.get("recommended_events")!.cast<Event>();
or maybe the way I write the syntax to save and read the list are totally wrong? please help :)
Is not necessary to open your box as a List, because it is a box and can store many objects of the type that you declare, for example:
await Hive.openBox<MyModel>(boxName);
To get all the objects or data stored in that box, you can query like this:
final box = Hive.box<CompanyModel>(boxName);
List<CompanyModel> interviews = box.values.toList();
In addition, you have to create an Adapter Model if you want to store your own Model with Hive.
There is two dev dependencies to auto generate the Model:
dev_dependencies:
hive_generator:
build_runner:
Importing that dependencies and running this command flutter packages pub run build_runner build will generate the Model, but also you have to create your Model as the documentation indicates.
I suggest you to check out the documentation.
Hive - Generate Adapter
I can finally solve it by using it like this. in main function
Future<void> main() async {
await Hive.initFlutter();
await Hive.openBox("events");
}
when saving data list
final eventsBox = Hive.box("events");
eventsBox.put("recommended_events", eventsFromServer);
and read it like this
final eventsBox = Hive.box("events");
final eventsFromHive = eventsBox.get("recommended_events")?.cast<Event>() ?? [];
I have faced this kind of problem. It was absolutely the same. I do not know how you got kinda problem. Maybe it was the same with mine. I have just cleaned the box. and it has worked on me.
**Cause: **
I started it immediately after I made the box (for testing). he had taken the List<dynamic> object as it store. Once I made it clear, it stocked up data I had just given and it worked
Try:
boxName.clear() inside initState() and re-run it. if it will work do not forget to delete the line!
if you forget, it will clear the box every time.
Done with getting Hive as an List Object :)
Future<List<CustomModel>> getModels() async {
//box = await Hive.openBox<CustomModel>(Constants.Hive);
return box?.values.toList(growable: false)?.cast<CustomModel>() ?? <CustomModel>[];
}
I am developing a barcode app and save the data to hive. What I need to know is there a way to export the saved hive database to a backup file and be able to retrieve it for instance if the app crashed or your phone is lost. This is for blind accessibility. Want to export the data to a file that I can save to my pc to store and if something happens I do not have to scan all the products again to build the database. If hive can not do this can someone point me in a direction of which flutter dart database can do this. Thank you
Ok the answer did not work for me. Here is a copy of my model file
import 'package:hive/hive.dart';
part 'product.g.dart';
#HiveType(typeId: 0)
class Product extends HiveObject{
#HiveField(0)
String itemName;
#HiveField(1)
String barCode;
#HiveField(2)
String bcType;
Product(this.itemName, this.barCode, this.bcType);
}
Then I call my box like
var box = Hive.box('products');
How to encode this to json for saving?
I use the next
Future<File> _createBackupFile() async {
/// This example uses the OS temp directory
File backupFile = File('${Directory.systemTemp.path}/backup_barcode.json');
try {
/// barcodeBox is the [Box] object from the Hive package, usually exposed inside a [ValueListenableBuilder] or via [Hive.box()]
var barcodeBox = Hive.box<Product>('products');
backupFile = await backupFile.writeAsString(jsonEncode(barcodeBox.values));
return backupFile;
} catch (e) {
// TODO: handle exception
print(e);
}
}
There is not a "out-of-the-box" solution for that as far as I know. It depends a lot on your use case of how you want to do that (since there are many ways). For a complete example of how I did that for my app, you can take a look here:
https://github.com/Kounex/obs_blade/blob/master/lib/views/settings/logs/log_detail/log_detail.dart (I made use of the share package in order to easily export it - but that's not necessary)
Flutter also has its own documentation on reading and writing files (https://flutter.dev/docs/cookbook/persistence/reading-writing-files) - I will add some information to round it up:
Storage location
First of all we have to think about where to store the "backup file". Flutter exposes common paths on its own which you can make use of (additionally the path_provider package gives you more flexibility). If you want this backup file to be temporarily, you can for example use:
Directory.systemTemp;
The documentation states: "This is the directory provided by the operating system for creating temporary files and directories in." The OS will make sure to delete them in different occasions so you don't have to worry about it. You can also create additional directories inside this temp directory to make it more distinguishable, like:
Directory.systemTemp.createTemp('my_app');
IMPORTANT: this applies to non-sensitive data. If whatever you are processing contains sensitive data (like names, addresses etc.), you have to ensure data security / data privacy. In such cases I would make use of the path_provider package as mentioned earlier and create those files in the documents directory (getApplicationDocumentsDirectory()) and make sure they are deleted immediately after usage / export. Even encrypting the content may be a good idea - but I'm not diving into this here.
File mangagement
Once we know where to store the file, we just need to create them. Chapter 3 and 4 of the flutter documentation earlier exactly states how to do that, so I'm rather focusing on what to write.
A common and very convenient way to compose your data is JSON. Flutter also has documentation for that: https://flutter.dev/docs/development/data-and-backend/json
Since you are using Hive, you probably already have classes representing entries in your boxes and you could easily just add the toJson() function where you return a Map<String, dynamic> (as seen in the documentation) and you can use that to finally write the needed information into a file.
Based on your Hive class, this is how to adjust it in otder to serialize it correctly:
import 'package:hive/hive.dart';
part 'product.g.dart';
#HiveType(typeId: 0)
class Product extends HiveObject{
#HiveField(0)
String itemName;
#HiveField(1)
String barCode;
#HiveField(2)
String bcType;
Product(this.itemName, this.barCode, this.bcType);
/// This function will automatically be used by the [jsonEncode()] function internally
Map<String, dynamic> toJson() => {
'itemName': this.itemName,
'barCode': this.barCode,
'bcType': this.bcType,
}
}
A small example implementation could look like this:
Future<File?> _createBackupFile() async {
/// This example uses the OS temp directory
File backupFile = File('${Directory.systemTemp.path}/backup_barcode.json');
try {
/// barcodeBox is the [Box] object from the Hive package, usually exposed inside a [ValueListenableBuilder] or via [Hive.box()]
backupFile = await backupFile.writeAsString(jsonEncode(barcodeBox.values));
return backupFile;
} catch (e) {
// TODO: handle exception
}
}
This will save the JSON representation of your Hive box inside the temporary OS directory. You can swap the directory with whatever suits you best (on Android for example on the external storage for easier accessibility).
Now you have to think about how and when to trigger this. You can do this manually by triggering a button press for example or automatically after a certain action (like adding a new barcode) and choose a way that works for you to access the file. As stated earlier, saving the file somewhere easily accessible like the external storage on Android or making use of the share package are possible solutions.
Android Manifest should contain these:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:requestLegacyExternalStorage="true"
You will need this package and this package to proceed.
Now a method to backup the data to a desired location:
Future<void> createBackup() async {
if (Hive.box<Product>('products').isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('No Products Stored.')),
);
return;
}
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Creating backup...')),
);
Map<String, dynamic> map = Hive.box<Product>('products')
.toMap()
.map((key, value) => MapEntry(key.toString(), value));
String json = jsonEncode(map);
await Permission.storage.request();
Directory dir = await _getDirectory();
String formattedDate = DateTime.now()
.toString()
.replaceAll('.', '-')
.replaceAll(' ', '-')
.replaceAll(':', '-');
String path = '${dir.path}$formattedDate.json';//Change .json to your desired file format(like .barbackup or .hive).
File backupFile = File(path);
await backupFile.writeAsString(json);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Backup saved in folder Barcodes')),
);}
Future<Directory> _getDirectory() async {
const String pathExt = 'Barcodes/';//This is the name of the folder where the backup is stored
Directory newDirectory = Directory('/storage/emulated/0/' + pathExt);//Change this to any desired location where the folder will be created
if (await newDirectory.exists() == false) {
return newDirectory.create(recursive: true);
}
return newDirectory;
}
Finally, call this function using a button and it will save a backup with the current time as the name in JSON format.
createBackup()
After this to restore the data back to Hive,
Future<void> restoreBackup() async {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Restoring backup...')),
);
FilePickerResult? file = await FilePicker.platform.pickFiles(
type: FileType.any,
);
if (file != null) {
File files = File(file.files.single.path.toString());
Hive.box<Product>('products').clear();
Map<String, dynamic> map = jsonDecode(await files.readAsString());
for (var i = 0; i < map.length; i++) {
Product product = Product.fromJson(i.toString(), map);
Hive.box<Product>('products').add(product);
}
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Restored Successfully...')),
);
}
}
Finally, call this function using a button and it will open the file picker where you can select the backup file and it will remove the existing data and add every item from the backup in a loop.
restoreBackup()
Help is much appreciated how to trace down this issue, because I am running out of ideas.
I am calling the function getOrderCollection, below, but it aborts after the first line var myCompanyDoc = await FirebaseFirestore.instance.collection('companies').doc(myCompany).get(); Without trowing anything to the console or jumping into some library when debugging. When I click next statement it jumps back to the calling function.
I am authenticated to the database, companyCollection = FirebaseFirestore.instance.collection('companies') provides an initialized object pointing to the collection and myCompany is a constant with the document id entered by copy/paste.
If some rules for the database but I can't see successful or denied queries with the monitor.
Any ideas how I can proceed tracing down the issue?
Future<void> getOrderCollection() async {
var myCompanyDoc = await FirebaseFirestore.instance.collection('companies').doc(myCompany).get();
print("companyDoc fetched");
final myDeliveryDocRef = myCompanyDoc.data()['delivery'].toString();
orderCollection = FirebaseFirestore.instance.collection('companies').doc(myCompany).collection('features').doc(myDeliveryDocRef).collection('orders');
orderBriefDoc = FirebaseFirestore.instance.collection('companies').doc(myCompany).collection('features').doc(myDeliveryDocRef);
}
UPDATE: This is collection > document what corresponds to final String myCompany = '4U4kZKXkr3rHA6B04S5K';
As we discussed in your comments, the issue was that you forgot to await the getOrderCollection() function. Even though, as you mentioned, your caller function _deliveryRepository.initRepository() was awaited, you still had to await getOrderCollection() inside your caller method to make sure that the code is waiting for the getOrderCollection() to be executed before it proceeds to the next line.
In general, you want to have some error handling and to type the known types/classes (avoid using var).
Error handling - for async/await place the code inside a try/catch.
Typing - Dart is type safe, which is really great to prevent runtime errors.
Depending on your setup, you might be able to hover over the Firestore.instance.collection(...).doc(...) to see the return type. .doc(...).get() returns a DocumentSnapshot and .collection(...).get() returns a CollectionSnapshot.
Using the above, it should be easier to debug:
Future<void> getOrderCollection() async {
try {
DocumentSnapshot myCompanyDoc = await FirebaseFirestore.instance.collection('companies').doc(myCompany).get();
print("companyDoc fetched");
final myDeliveryDocRef = myCompanyDoc.data()['delivery'].toString();
} catch(e) {
print('Error: ' + e.toString());
}
}
Don't forget to await your other 2 Firestore queries.
I am programming a flutter app, in which I have a callback from the native system containing some nfc information. I register the callback via the following line of code:
eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);
The callback looks like this:
void _onEvent(Object event) {
//Receive Event
print("NFC Event received");
//Get the Payload
event['Payload']; //This does not work, it gives an error
}
But how can I parse out the data? I know it contains a field by the name Payload however, I cannot access it via event['Payload'].
I am very confused, I tried to cast it to another type like this: Map<String, String> eventMap = Map<String, String>.from(event); but that didnt work. Could someone please point out what I am doing wrong and how I can correctly extract the data?
Without too much information of native part, I suggest
you can reference this package or fork this https://github.com/akeblom/flutter-nfc-reader
directly
this repo fix some issue of pub.dev and work well in real device
the dart code you need reside in
https://github.com/akeblom/flutter-nfc-reader/blob/master/lib/flutter_nfc_reader.dart
void _onEvent(dynamic data) {
print("Event");
print(data);
}
Edit add more detail
akeblom has add Write NFC capability to IOS, so IOS part should work. please ue this fork https://github.com/akeblom/flutter-nfc-reader
For IOS MissingPluginException issue, I do not have IOS, I suggest you can ask akeblom
The data part you mentioned in comments if I do not misunderstand is line 77, please see describe below
In Android part.
https://github.com/akeblom/flutter-nfc-reader/blob/master/android/src/main/kotlin/it/matteocrippa/flutternfcreader/FlutterNfcReaderPlugin.kt
line 174, use kotlin mapOf returns a new read-only map with the specified contents and eventSink.success result to Dart
if (message != null) {
val data = mapOf(kId to id, kContent to message, kError to "", kStatus to "read")
eventSink?.success(data)
}
In https://github.com/akeblom/flutter-nfc-reader/blob/master/lib/flutter_nfc_reader.dart
line 22, with named constructor
factory NfcData.fromMap(Map data) {
NfcData result = NfcData(
id: data['nfcId'],
content: data['nfcContent'],
error: data['nfcError'],
statusMapper: data['nfcStatus'],
);
In line 77, NFC read start and _onEvent(dynamic data) get the data.
stream use this named constructor, and parse data, here with map((result)) transfer to NfcData
static Stream<NfcData> get read {
final resultStream = _channel
.invokeMethod('NfcRead')
.asStream()
.asyncExpand((_) => stream
.receiveBroadcastStream()
.map((result) => NfcData.fromMap(result)));
return resultStream;
In https://github.com/akeblom/flutter-nfc-reader/blob/master/example/lib/main.dart
line 33, response has transfered to NfCData, so example just use _nfcData = response;
FlutterNfcReader.read.listen((response) {
setState(() {
_nfcData = response;
});
});
The simplest way I found to parse an event to a Map is the following:
I encoded the variable to a String (but I use json.encode(event)instead of event.toString()as encode(event)returns a valid string.
Then I use this string to construct a map via json.decode. All of this is achieved with the flutter native library dart:convert. The complete code looks like this:
import 'dart:convert';
...
void _onEvent(dynamic event) {
//Receive Event
print("NFC Event received");
String str = json.encode(event);
Map eventMap = json.decode(str);
}
Have you tried event.toString()? It might return a string containing the field you are trying to get, from which you can easily parse the value.
You may also want to try:
Class<?> clazz = event.getClass();
Field payload= clazz.getField("Payload"); //Note, this can throw an exception if the field doesn't exist.
String fieldValue = payload.toString();
If it isn't returning what you want, you may need to make an interface for the object type, with a get method, or override the toString method to get return the Payload value.