0

我有一个列表contacts,其中存储Contact了从contact_services包中获取的类型

List<Contact> contacts = [];

SharedPrefrences我首先使用它存储列表jsonEncode,然后保存它

void saveContacts() async{
    SharedPreferences prefs = await SharedPreferences.getInstance();
    await prefs.setString('list', jsonEncode(contacts));
}

但是当我尝试加载列表时它返回一个异常type 'List<dynamic>' is not a subtype of type 'List<Contact>'

void loadList() async{
    SharedPreferences prefs = await SharedPreferences.getInstance();
    contacts = await jsonDecode(prefs.getString('list'));

}

更新代码以突出显示更改:

这是整个saveContacts功能:

  void saveContacts() async{
    SharedPreferences prefs = await SharedPreferences.getInstance();
    var json = jsonEncode(contacts, toEncodable: (e) => e.toMap());
    await prefs.setString('list', jsonEncode(json));
  }

但我收到错误:The method 'toMap' isn't defined for the type 'Object'.

联系人只是一个存储Contact类型的列表

List<Contact> contact;

最初contact存储在单独的文件夹(全局)中以便于访问,但这不会影响结果jsonEncode

4

2 回答 2

1

您需要进行转换,Map<String, dynamic>然后将 json 转换为您的Contact. 这是一个将列表转换Contact为 json 文本并将其作为联系人列表再次读取的工作示例。(转到parseContacts下面的代码上的功能)。

import 'dart:convert';

class Contact {
  final String name;
  final String phone;

  Contact(this.name, this.phone);

  Contact.fromJson(Map<String, dynamic> json)
      : name = json['name'],
        phone = json['phone'];

  Map<String, dynamic> toJson() => {
        'name': name,
        'phone': phone,
      };
}

void main() {
  List<Contact> contacts = [];
  for (int i = 0; i < 10; i++) {
    contacts.add(Contact("name$i", i.toString()));
  }
  
  var jsonText = jsonEncode(contacts);
  print(jsonText);

  var contactsFromJson = parseContacts(jsonText);
  for (var item in contactsFromJson) {
    print(item.name);
  }
}

List<Contact> parseContacts(String jsonText) {
  final parsed = jsonDecode(jsonText).cast<Map<String, dynamic>>();

  return parsed.map<Contact>((json) => Contact.fromJson(json)).toList();
}

有关更多详细信息,您可以在https://flutter.dev/docs/cookbook/networking/background-parsing#complete-example阅读示例

于 2021-07-18T20:06:21.477 回答
1

更新:

我原本以为问题可以通过casting 解决,但潜入 ing json.dart,我发现以下评论:

  /// If value contains objects that are not directly encodable to a JSON
  /// string (a value that is not a number, boolean, string, null, list or a map
  /// with string keys), the [toEncodable] function is used to convert it to an
  /// object that must be directly encodable.
  ///
  /// If [toEncodable] is omitted, it defaults to a function that returns the
  /// result of calling `.toJson()` on the unencodable object.
  /// Directly serializable values are [num], [String], [bool], and [Null], as
  /// well as some [List] and [Map] values. For [List], the elements must all be
  /// serializable. For [Map], the keys must be [String] and the values must be
  /// serializable.
  ///
  /// If a value of any other type is attempted to be serialized, the
  /// `toEncodable` function provided in the constructor is called with the value
  /// as argument. The result, which must be a directly serializable value, is
  /// serialized instead of the original value.

TLDR;如果参数包含一个对象,如果类没有实现value,我们也必须提供。而且由于在您的情况下您无法为该类提供实现,因此正确编码对象的唯一方法是提供. 我已经实现了一个示例供您理解:toEncodabletoJsontoJsontoEncodable

import 'dart:convert';

class Class {
  String name;
  Class(this.name);

  @override
  String toString() => name;
}

void main() {
  final list = <Class>[
    Class('a'),
    Class('b'),
    Class('c'),
  ];

  final encoded = json.encode(list, toEncodable: (c) {
    if (c is Class) {
      return {'name': c.name};
    }
  });
  print(encoded);
  
  // Save and retreive `encoded`
  
  print((json.decode(encoded) as List).map((map) {
    if (map.containsKey('name')) {
      return Class(map['name']);
    }
  }));
}

附录:与其提供自定义实现并从 jsontoEncodable创建一个,不如试试已经定义的。ContacttoMapfromMap


原答案:

您可以使用casta 上的方法转换列表的类型List

因此,首先将解码后的数据转换as为 a List,然后cast对其应用以获得所需的类型。

contacts = await (jsonDecode(prefs.getString('list')) as List).cast<Contact>();
于 2021-07-15T17:13:53.350 回答