3

I have been trying to save a list of type Contact (which is a class from contacts_service) package by serializing it using toJosn and fromJson and then saving it as String in Shared Preferences

to & fromJosn :

 Contact.fromJson(Map<String, dynamic> json):
        identifier = json['identifier'],
        displayName = json['displayName'],
        givenName = json['givenName'],
        middleName = json['middleName'],
        prefix = json['prefix'],
        suffix = json['suffix'],
        familyName = json['familyName'],
        company = json['company'],
        avatar = json['avatar'],
        androidAccountType = json['androidAccountType'],
        jobTitle = json['jobTitle'],
        androidAccountTypeRaw = json['androidAccountTypeRaw'],
        androidAccountName = json['androidAccountName'],
        emails = json['emails'], phones = json['phones'],
        postalAddresses = json['postalAddresses'],
        birthday = json['birthday'];
  Map<String, dynamic> toJson() => {
    'identifier':identifier,
    'displayName':displayName,
    'givenName':givenName,
    'middleName':middleName,
    'prefix':prefix,
    'suffix':suffix,
    'familyName':familyName,
    'company':company,
    'avatar':avatar,
    'jobTitle':jobTitle,
    'androidAccountTypeRaw':androidAccountTypeRaw,
    'androidAccountName':androidAccountName,
    'emails':emails,
    'phones':phones,
    'postalAddresses':postalAddresses,
    'birthday':birthday,
    'androidAccountType':androidAccountType};

saving & loading:

  void saveContacts() async{
    SharedPreferences prefs = await SharedPreferences.getInstance();
    final String List = json.encode(contacts.map((contact) => contact.toJson()));
    await prefs.setString('contactList', List);
  }
  void loadConatcs() async{
    SharedPreferences prefs = await SharedPreferences.getInstance();
    final String saved = prefs.getString('contactList');
    final List<dynamic> decoded = json.decode(saved);
    contacts = decoded.map((contact) => Contact.fromJson(contact));
  }

but I am gettign an error Converting object to an encodable object failed: Instance of 'MappedListIterable<dynamic, Item>'

also note that this is my first time trying to serialize and object so might have missed some things up in to & fromJosn

edit:

I also tried using toEncodable

final String List = jsonEncode(contacts, toEncodable: (c)=> c.toJson());        

but it would return an exception : type 'MappedListIterable<dynamic, Contact>' is not a subtype of type 'List<Contact>'

5
  • Please share your Contact model. Probably you have another model object in your contact. Commented Aug 26, 2021 at 15:50
  • Contact is actually an object from contacts_services plugin and thus you can find the full model here and it contains toMap and fromMap methods but I couldn't get them to work, toJosn and fromJson are additions made by me and they also didn't work so the problem appears to be in the loadContacts part @ישו אוהב אותך Commented Aug 27, 2021 at 10:35
  • It would be easier if you use sqflite or Hive rather than Shared Preferences because it used to save small variables not a List and you will get an error if you try to save large variables. Commented Aug 28, 2021 at 22:12
  • Where do you stuck now, on ` final List<dynamic> decoded = json.decode(saved);` or next line of code.? Commented Aug 31, 2021 at 15:06
  • The next line, converting the String back to a list of Contact @Arul Mani Commented Sep 2, 2021 at 20:22

3 Answers 3

1

this probably will solve your problem:

var contacts = decoded.map((c) => Contact.fromJson(Map<String, dynamic>.from(c)));
Sign up to request clarification or add additional context in comments.

3 Comments

the problem above was when trying to encode, although it's my fault for not clarifying, anyway I solved it by using toEncodeable in josnDecode but now I am getting Unhandled Exception: type 'MappedListIterable<dynamic, Contact>' is not a subtype of type 'List<Contact>' when decoding although I am using the code you provided above @Emre Akçadağ
this is my method to use jsonlist to modelList static List<User>? fromJsonList(List? data) => data?.map((m) => m == null ? null : User.fromJson(Map<String, dynamic>.from(m))).toList(); hope it helps you.
Please explain about how this code works and is helpful rather than just posting the code.
1

It took me a while to parse through these answers to get to a block of working code for serializing the Contacts returned from contacts_service.

I used json_serializable to generate the toJson methods, added them to the existing classes using extension, and used the json.encode @Silris posted.

Here's the working block of code:

import 'package:contacts_service/contacts_service.dart';
import 'dart:convert';

// Copied generated toJson methods
extension ContactToJson on Contact {
  Map<String, dynamic> toJson() => <String, dynamic>{
        'identifier': identifier,
        'displayName': displayName,
        'givenName': givenName,
        'middleName': middleName,
        'prefix': prefix,
        'suffix': suffix,
        'familyName': familyName,
        'company': company,
        'jobTitle': jobTitle,
        'androidAccountTypeRaw': androidAccountTypeRaw,
        'androidAccountName': androidAccountName,
        'AndroidAccountType': androidAccountType,
        'emails': emails?.map((item) => item.toJson()).toList(),
        'phones': phones?.map((item) => item.toJson()).toList(),
        'postalAddresses':
            postalAddresses?.map((address) => address.toJson()).toList(),
        'birthday': birthday?.toIso8601String(),
        'avatar': avatar,
      };
}

extension ItemToJson on Item {
  Map<String, dynamic> toJson() => {
        'value': value,
        'label': label,
      };
}

extension PostalAddressToJson on PostalAddress {
  Map<String, dynamic> toJson() => <String, dynamic>{
        'label': label,
        'street': street,
        'city': city,
        'postcode': postcode,
        'region': region,
        'country': country,
      };
}

serializeContacts(List<Contact> contacts) => json.encode(contacts.map((contact) => contact.toJson()).toList());

Using this with the supporting permissions and services in a button looks like:

onPressed: () async {
  // Either the permission was already granted before or the user just granted it.
  if (await Permission.contacts.request().isGranted) {
    List<Contact> contacts = await ContactsService.getContacts(
        withThumbnails: false, photoHighResolution: false);
    // Imported from above file
    print(serializeContacts(contacts));
  }
},

Pressing this button in the iphone simulator will print:

[{"identifier":"F57C8277-585D-4327-88A6-B5689FF69DFE","displayName":"Anna Haro","givenName":"Anna","middleName":"","prefix":"","suffix":"","familyName":"Haro","company":"","jobTitle":"","androidAccountTypeRaw":null,"androidAccountName":null,"AndroidAccountType":null,"emails":[{"value":"[email protected]","label":"home"}],"phones":[{"value":"555-522-8243","label":"home"}],"postalAddresses":[{"label":"home","street":"1001  Leavenworth Street","city":"Sausalito","postcode":"94965","region":"CA","country":"USA"}],"birthday":"1985-08-28T00:00:00.000","avatar":null},{"identifier":"AB211C5F-9EC9-429F-9466-B9382FF61035","displayName":"Daniel Higgins Jr.","givenName":"Daniel","middleName":"","prefix":"","suffix":"Jr.","familyName":"Higgins","company":"","jobTitle":"","androidAccountTypeRaw":null,"androidAccountName":null,"AndroidAccountType":null,"emails":[{"value":"[email protected]","label":"home"}],"phones":[{"value":"555-478-7672","label":"home"},{"value":"(408) 555-5270","label":"mobile"},<…>

3 Comments

thanks for your answer, I wasn't able to make this work by my own and gave up, I'll check this solution later
You might also want to do a pull request since the serializing method included in the package doesn't work
Happy to help. I hope that it works for you. Someone opened an issue asking for serialization to be included in the package and it didn't get any traction github.com/lukasgit/flutter_contacts/issues/162. I'll post a link to this thread and see if they're interested in the contribution.
0
+50

UPDATE: You can use the refactored package from using Iterable to List (Still waiting for approval when I write this update) here, you still need to change your code as I have posted below to make it work. It will remove the conflict between JSON type (which uses [] in its format) and Iterable type (uses ()) because List type is also using [] in its format.

This is how to add another version of a package to your project:

Change this line in pubspec.yaml from:

dependencies:
  contacts_service: any

to:

dependencies:
  contacts_service:
    git: https://github.com/Abion47/flutter_contacts

YOU STILL NEED TO DO THIS TO MAKE IT WORK:

You just need to change this line from:

final String list = json.encode(contacts.map((contact) => contact.toJson()));

to

final String list = json.encode(contacts.map((contact) => contact.toJson()).toList());

If you print the first list you will get something like below which is not a JSON type:

({name: a, number: 123}, {name: b, number: 123})

and the second will look like this:

[{name: a, number: 123}, {name: b, number: 123}]

The .toList() will change the runtimeType from MappedListIterable<Contact, dynamic> to List<dynamic>


Example: dartpad

8 Comments

you're code is returning Converting object to an encodable object failed: Instance of 'MappedListIterable<dynamic, Item>, I have also tried toEncodable to encode the list, like this: final String List = jsonEncode(contacts, toEncodable: (c)=> c.toJson() ); but it would return when loading Unhandled Exception: type 'MappedListIterable<dynamic, Contact>' is not a subtype of type 'List<Contact>'.@Things of N
@AnanSaadi I just updated dartpad example. Please give it a try and let me know the issue.
I have copied your code exactly yet still receiving the same error Converting object to an encodable object failed: Instance of 'MappedListIterable<dynamic, Item>', I think the issue is that class Contact includes a list of Iterable and type called Uint8List, can you take a look at the full model and see if it needs something else in order to encode it ? @Things of N
@AnanSaadi I have updated the code that including SubContact List in Contact class. I think you have to refactor your code from using Iterable to List (which is extended from Iterable with a lot of benefit features). The code here
Can you please explain what SubContact does and what should I do to emails and phones in order to get them working ? @Things of N
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.