Problem serializing a list of objects in flutter and saving it using shared preferences

311

Solution 1

this probably will solve your problem:

var contacts = decoded.map((c) => Contact.fromJson(Map<String, dynamic>.from(c)));

Solution 2

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"},<…>
Share:
311
Anan Saadi
Author by

Anan Saadi

Updated on December 31, 2022

Comments

  • Anan Saadi
    Anan Saadi over 1 year

    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>'

  • Anan Saadi
    Anan Saadi over 2 years
    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ğ
  • Emre Akçadağ
    Emre Akçadağ over 2 years
    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.
  • Anan Saadi
    Anan Saadi over 2 years
    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
  • Rahul Vashishtha
    Rahul Vashishtha over 2 years
    Please explain about how this code works and is helpful rather than just posting the code.
  • Vursin
    Vursin over 2 years
    @AnanSaadi I just updated dartpad example. Please give it a try and let me know the issue.
  • Anan Saadi
    Anan Saadi over 2 years
    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
  • Vursin
    Vursin over 2 years
    @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
  • Anan Saadi
    Anan Saadi over 2 years
    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
  • Vursin
    Vursin over 2 years
    The SubContact is similar to Item or PostalAddress in your script. I have seen a pull request that already refactored the code from using Iterable to List here. The way using List<Item> or List<PostalAddress> is the same with List<SubContact>. Please check it and let me know if it's usable or not. You can use iterable.toList() to convert any Iterable object to List object. @AnanSaadi
  • Vursin
    Vursin over 2 years
    I have updated the way to add the modified package to the current project.
  • Anan Saadi
    Anan Saadi over 2 years
    Hey, I didn't get the time to check your solution but I believe it's the correct answer so I have awarded you the bounty and will update you later on wither it has worked or not @Things of N
  • Anan Saadi
    Anan Saadi over 2 years
    thanks for your answer, I wasn't able to make this work by my own and gave up, I'll check this solution later
  • Anan Saadi
    Anan Saadi over 2 years
    You might also want to do a pull request since the serializing method included in the package doesn't work
  • Assaf
    Assaf over 2 years
    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.