前语

在开发过程中,无论是用户信息保存,还是网络数据保存,难免会用到本地操作,在flutter中有一款 key-value 方式的本地操作(就像 iosNSUserDefaults似的)

另外,这儿也介绍下JSON与目标间的相互转化

JOSN快速生成各种语言Model入口

demo地址(内容在fileManager文件夹中,能够替换main中注释测验作用)

tips:假如寄存很多数据,需求联系型数据库,那么能够搜其他资源

耐久化储存(key-value)

导入shared_preferences

这个耐久化储存用的仓库,是flutter的团队开发的,叫shared_preferences,但他是以三方仓库存在的,因而需求手动导入

flutter pub add shared_preferences

导入之后就能够直接 import 直接运用了(一般运用提示导入)

import 'package:shared_preferences/shared_preferences.dart';

SharedPreferences介绍

SharedPreferences为一个单例,并且是一个 Future异步函数,因而获取的时分也需求 Future异步函数调用获取,经过 await 的方式获取到单例(前面有讲到过 Future)

大致原理:与其他缓存三方原理类似,文件信息保存的时分内存一份、本地一份,在初始化的时分会一次性读取一切数据到内存

能够显着看出优缺点:访问速度快,内存占用大,适合小信息保存

假如寄存很多数据,或许需求寄存杂乱的数据,那么需求选择其他数据库

数据库:对于联系型数据库SQLite、Core Data、GreenDao等;对于非联系型数据库,可选有Realm、UnQLite等;对于Key/Value存储,有比方Redis、Berkeley DB、Level DB等

能够根据自己的需求选择封装好的数据库,手机端一般是SQLiteRealm

SharedPreferences读写操作

寄存数据

经过传递keyvalue,能寄存各种基本数据,发现没有寄存目标的(不后边介绍)

//这儿只是经过泛型演示,实际上用哪个调用哪个即可,能够自己封装一下
static saveInfo<T>(String key, T value) async {
    //需求经过await获取单例目标
    final manager =  await SharedPreferences.getInstance();
    //默许的运用只有如下几种
    manager.setBool(key, value as bool);
    manager.setDouble(key, value as double);
    manager.setInt(key, value as int);
    manager.setString(key, value as String);
    manager.setStringList(key, value as List<String>);
}

读取数据

经过传递key值,能够直接调用对应的get办法即可,能够发现也没有读取目标的(后边介绍)

//这儿只是经过泛型演示,实际上用哪个调用哪个即可,能够自己封装一下
static Future<T> readInfo<T>(String key) async {
    final manager = await SharedPreferences.getInstance();
    return manager.getString(key) as T;
    return manager.getBool(key) as T;
    return manager.getDouble(key) as T;
    return manager.getInt(key) as T;
    return manager.getString(key) as T;
    return manager.getStringList(key) as T;
}

删去数据

删去不多说了,传一个key就删了

static remove(String key) async {
  final manager =  await SharedPreferences.getInstance();
  manager.remove(key);
}

json与目标相互转化与寄存目标

前面也看到了,shared_preferences没有寄存目标的本事,那我们就寄存目标变成 寄存json字符串即可

因而,需求了解的过程便是 json与目标的相互转化

flutter中现在还没有看到反射机制,因而还无法友爱的一键式转化,现在只能以 Map作为纽带jsonMap之间的相互转化,目标所属类负责 目标Map之间的相互转化

json和Map之间的转化

经过系统预制的 convert 库,来完成转化过程

经过 jsonEncodeMap 转化成 jsonString

final userMap = {
    name: "Marshal",
    age: 20
}
//将map转化成 jsonString
final jsonString = jsonEncode(userMap);

经过 jsonDecodejsonString 转化成 Map

final jsonString = "{"name": "Marshal", "age": 20}";
//jsonString 转化成 Map
final jsonMap = jsonDecode(jsonString);

目标和Map之间的转化

UserInfo类为例,创立两个办法(每个需求保存的类都要完成这两个办法)

jsonMap转化为目标:经过命名式结构办法,能够将传过来的 jsonMap 转化为 本目标

目标转化为jsonMap:经过目标办法或许类办法,能够将本类创立的目标,转化为 Map

//选中类名,摁着 ctl + enter,生成 Constructor,选择需求默许初始化的特点,能够生成带参结构办法
class UserInfo {
  String name;
  int age;
  UserInfo(this.name, this.age);
  //命名式结构办法,也能够是用工厂结构办法
  UserInfo.fromJson(Map<String, dynamic> json): name = json['name'], age = json['age'];
  //假如想写成协议,归档(json和目标互转时,为了运用方便)时,能够承继协议,那么能够用普通办法,而不是结构办法
  Map<String, dynamic> toJson() =>
      {'name': name, 'age': age};
}

目标和json的直接相互转化

目标直接转化为json字符串,以UserInfo为例

final userInfo = UserInfo("Marshal", 20);
//将 UserInfo 转化为 jsonMap
final userMap = userInfo.toJson();
//然后调用 jsonEncode 将 map 转化成 jsonString
final jsonString = jsonEncode(userMap);
//jsonEncode默许只能将map转化成json,因而需求使用二个回调,将目标转化为map,然后才进行的转化
//实际和上面相同
final jsonString2 = jsonEncode(value,
  toEncodable: (Object? value) => value is UserInfo
      ? value.toJson()
      : throw UnsupportedError('Cannot convert to JSON: $value'));

json字符串直接转化为目标,以UserInfo为例

final jsonString = "{"name": "Marshal", "age": 20}";
//将 jsonString 转化为 jsonMap
final jsonMap = jsonDecode(jsonString); //转化成 Map
//将 jsonMap 转化为 UserInfo
final userInfo = UserInfo.fromJson(jsonMap);

SharedPreferences存取目标

寄存目标,将前面的过程都结合起来,寄存目标转化的json字符串

static saveObject(String key, UserInfo value) async {
  final manager =  await SharedPreferences.getInstance();
  //实际上能够将 UserInfo设置为泛型束缚,承继(or多承继)某个类或许完成某一个接口,经过该接口调用toJson办法
  //将一个类转化为jsonString
  //此过程需求调用对应类的 toJson 办法,现将类信息放到 Map 中,然后在转化成json
  final userMap = value.toJson();
  //然后调用jsonEncode将map转化成 jsonString
  final jsonString = jsonEncode(userMap);
  //jsonEncode默许只能将map转化成json,因而需求使用二个回调,将目标转化为map,然后才进行的转化
  //实际和上面相同
  final jsonString2 = jsonEncode(value,
      toEncodable: (Object? value) => value is UserInfo
          ? value.toJson()
          : throw UnsupportedError('Cannot convert to JSON: $value'));
  if (jsonString.isEmpty) return;
  manager.setString(key, jsonString);
  log('jsonEncode --' + jsonString);
}

读取目标,将前面的过程都结合起来,读取json字符串转化的目标

static Future<dynamic> readObject(String key) async {
  final manager =  await SharedPreferences.getInstance();
  final jsonString = manager.getString(key);
  if (jsonString != null) {
    final jsonMap = jsonDecode(jsonString); //转化成 Map
    log('jsonDecode --' + jsonMap.toString());
    //实际运用中,能够直接回来 Map 调集,从外面给类赋值,以减少耦合
    return UserInfo.fromJson(jsonMap); //根据根据命名式结构办法转化
  }
  return null;
}

tips: FileManagerEx(存取目标改进版,现在无法处理结构办法的问题,只能略微约束一下,仅供参考哈)

最终

从速测试一下吧,这个 shared_preferences只适合寄存少量数据,例如用户基本信息,查找信息等,假如需求很多数据,或许联系型,能够切换其他的哈

这个 目标和json互转 才是本章最主要的介绍