Flutter数据处理

一、文件IO

PathProvider 插件提供了一种平台透明的方式来访问设备文件系统上的常用位置。该类当前支持访问两个文件系统位置:

  • 临时目录: 系统可随时清除的临时目录(缓存)。在iOS上,这对应于NSTemporaryDirectory() 返回的值。在Android上,这是getCacheDir()返回的值。
  • 文档目录: 应用程序的目录,用于存储只有自己可以访问的文件。只有当应用程序被卸载时,系统才会清除该目录。在iOS上,这对应于NSDocumentDirectory。在Android上,这是AppData目录。

    1)在pubspec.yaml中添加path_provider库

dependencies:
path_provider: 0.4.1

2)在dart文件中实现文件读写方法

import 'dart:io';
import 'package:path_provider/path_provider.dart';

读文件:

Future<File> _getLocalFile() async {
  // get the path to the document directory.
  String dir = (await getApplicationDocumentsDirectory()).path;
  return new File('$dir/counter.txt');
}

Future<int> _readCounter() async {
  try {
    File file = await _getLocalFile();
    // read the variable as a string from the file.
    String contents = await file.readAsString();
    return int.parse(contents);
  } on FileSystemException {
    return 0;
  }
}

getApplicationDocumentsDirectory这一行代码的路径对应到Android路径下是:

/data/data/com.example.flutterhello/app_flutter

写文件:

await (await _getLocalFile()).writeAsString('$_counter');

通过库中的接口可以直接实现从File读写string

二、Json操作

Flutter有一个内置dart:convert库,其中包含一个简单的JSON编码器和解码器。

{
  "name": "John Smith",
  "email": "john@example.com"
}

有了dart:convert,我们可以用两种方式来序列化这个JSON model。

1)内连序列化JSON

Map<String, dynamic> user = JSON.decode(json);
print('Howdy, ${user['name']}!');
print('We sent the verification link to ${user['email']}.');

JSON.decode()仅返回一个Map<String, dynamic>,这意味着我们直到运行时才知道值的类型,这样就失去了大部分静态类型语言特性:类型安全、自动补全和最重要的编译时异常。这样一来,代码可能会变得非常容易出错。

2)在模型类中序列化JSON

可以通过引入一个简单的模型类(model class)来解决前面提到的问题,称之为User。在User类内部,我们有: 一个User.fromJson 构造函数, 用于从一个map构造出一个 User实例 map structure 一个toJson 方法, 将 User 实例转化为一个map.

class User {
  final String name;
  final String email;

  User(this.name, this.email);

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

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

现在,序列化逻辑移到了模型本身内部。采用这种新方法,可以非常容易地反序列化user。

Map userMap = JSON.decode(json);
var user = new User.fromJson(userMap);
print('Howdy, ${user.name}!');
print('We sent the verification link to ${user.email}.');

要序列化一个user,我们只是将该User对象传递给该JSON.encode方法。我们不需要手动调用toJson这个方法,因为JSON.encode已经为我们做了。 String json = JSON.encode(user);

3)使用代码生成库序列化JSON

这种json处理方式需要在项目中设置json_serializable,要包含json_serializable到我们的项目中,需要一个常规和两个开发依赖项。简而言之,开发依赖项是不包含在我们的应用程序源代码中的依赖项。

dependencies:
    json_annotation: ^2.0.0
dev_dependencies:
    build_runner: ^1.0.0
    json_serializable: ^2.0.0

将我们的User类转换为一个json_serializable:

import 'package:json_annotation/json_annotation.dart';
// user.g.dart 将在我们运行生成命令后自动生成
part 'user.g.dart';

///这个标注是告诉生成器,这个类是需要生成Model类的

@JsonSerializable()
class User {
  User(this.name, this.email);

  String name;
  String email;

  //不同的类使用不同的mixin即可
  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);

  Map<String, dynamic> toJson() => _$UserToJson(this);
}

有了这个设置,源码生成器将生成用于序列化name和email字段的JSON代码。 有两种生成序列化的方式:

第一种,一次性生成

项目根目录下运行flutter packages pub run build_runner build,我们可以在需要时为我们的model生成json序列化代码。 这触发了一次性构建,它通过我们的源文件,挑选相关的并为它们生成必要的序列化代码。 可以看到在user.dart所在的目录下生成了user.g.dart文件,内容如下:

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'user.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

User _$UserFromJson(Map<String, dynamic> json) {
  return User(json['name'] as String, json['email'] as String);
}

Map<String, dynamic> _$UserToJson(User instance) =>
    <String, dynamic>{'name': instance.name, 'email': instance.email};
第二种,持续生成

使用_watcher_可以使源代码生成的过程更加方便。它会监视项目中文件的变化,并在需要时自动构建必要的文件。可以通过flutter packages pub run build_runner watch在项目根目录下运行来启动_watcher_。只需启动一次观察器,然后并让它在后台运行,这是安全的。 可以动态的在user.dart中添加变量id,可以自动更新对应目下的user.g.dart文件:

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'user.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

User _$UserFromJson(Map<String, dynamic> json) {
  return User(
      json['name'] as String, json['email'] as String, json['id'] as String);
}

Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
      'name': instance.name,
      'email': instance.email,
      'id': instance.id
    };

4)使用json_serializable模型

要通过json_serializable方式反序列化JSON字符串,不需要对先前的代码进行任何更改。

Map userMap = JSON.decode(json);
var user = new User.fromJson(userMap);

序列化也一样。调用API与之前相同。

String json = JSON.encode(user);

有了json_serializable,我们可以在User类上忘记任何手动的JSON序列化 。源代码生成器创建一个名为user.g.dart的文件,它具有所有必需的序列化逻辑。

三、shared_preferences

shared_preferences 包含了Android的SharedPreferences接口和iOS的NSUserDefaults接口。

1)在pubspec.yaml添加库支持

https://pub.dartlang.org/packages/shared_preferences
dependencies:
  flutter:
    sdk: flutter
  shared_preferences: "0.4.3"

2)获取实例写数据

final prefs = await SharedPreferences.getInstance();
// Try reading data from the counter key. If it does not exist, return 0.
final counter = prefs.getInt('counter') ?? 0;

3)保存数据

// obtain shared preferences
final prefs = await SharedPreferences.getInstance();

// set value
prefs.setInt('counter', counter);

4)删除数据

final prefs = await SharedPreferences.getInstance();

prefs.remove('counter');

5)支持的数据类型

支持的数据类型是:int, double, bool, string and stringList 不支持写大量的数据。 另外需要注意的是,获取SharedPreferences是阻塞式的,因为get数据的时候如果mMap没有初始化就一直等着:

 _loadCounter() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    setState(() {
      _counter = (prefs.getInt('counter') ?? 0);
    });
  }

详细分析见文章:Android SharedPreferences