flutter 项目中 数据的管理是个很重要的环节, 今天这篇博客主要就是讲讲 flutter 中数据的存储和状态管理的几种方式,以及优缺点.
数据存储
- shared_preferences
- sqflite
- redux 和 redux_persist
下面说说各种存储方式的优缺点
shared_preferences
shared_preferences 其实就是个 key-value 的存储方式, 可以做一些开关配置的存储,比如是否首次登陆这种.
优点
存储简单,直接通过 put
和 get
key 值来存储和获取.
// 如果_sp已存在,直接返回,为null时创建
static Future<SharedPreferences> get sp async {
if (_sp == null) {
_sp = await SharedPreferences.getInstance();
}
return _sp!;
}
static Future<bool> save(String key, String value) async {
Logger.i("save key = $key , value = $value");
return (await sp).setString(key, value);
}
static dynamic get(String key) async {
return (await sp).get(key);
}
缺点
一般都是一些配置的存储, 如果是对象或者数组的存储,就会很麻烦.
sqflite
数据库存储, 一般都是存储对象和数组.
优点
能够存储复杂的数据和数组
缺点
操作麻烦, 要先创建表,管理数据库的版本信息, 以及更新库表操作, 如果不熟练 很容易出错.
代码
dbHelp
var databasesPath = await getDatabasesPath();
String path = join(databasesPath, '$dbName.db');
//根据数据库文件路径和数据库版本号创建数据库表
db = await openDatabase(path, version: dbVersion, onConfigure: onConfigure,
onCreate: (Database db, int version) async {
var batch = db.batch();
GlobalConfigDao.createGlobalConfigV1(batch);
await batch.commit();
Timer(Duration(seconds: 1), () {
GlobalConfigDao.insertGlobalConfig(
GlobalConfigData(fontFamily: 0, themeColor: 1));
});
}, onUpgrade: (db, oldVersion, newVersion) async {
// var batch = db.batch();
// switch (oldVersion) {
// case 1:
// WelcomeConfigDao.createWelcomeConfigV2(batch);
// break;
// }
// await batch.commit();
});
GlobalConfigDao
static void createGlobalConfigV1(Batch batch) {
batch.execute('DROP TABLE IF EXISTS globalConfig');
batch.execute('''CREATE TABLE globalConfig (
id INTEGER PRIMARY KEY AUTOINCREMENT,
themeColor INTEGER,
fontFamily INTEGER)''');
}
redux redux_persist
redux_persist
其实说到底也类似于 shared_preferences
, 他也是通过 key 来存储的, 只不过我们可以通过对象的 toJson 方法把对象转成 json,然后 redux_persist 把当前的 json 转成 string 存储到 key 里面去.
存储对象 readConfig
part 'readConfig.g.dart';
@JsonSerializable(explicitToJson: true)
class ReadConfig {
int textFontIndex;
int bgColorIndex;
ReadConfig({this.bgColorIndex: 2, this.textFontIndex: 2});
factory ReadConfig.fromJson(Map<String, dynamic> json) =>
_$ReadConfigFromJson(json);
Map<String, dynamic> toJson() => _$ReadConfigToJson(this);
// 命名构造函数
ReadConfig.empty();
}
readConfigRedux
class UpdateReaderConfigAction {
ReadConfig readConfig;
UpdateReaderConfigAction(this.readConfig);
}
ReadConfig _update(ReadConfig readConfig, action) {
readConfig = action.readConfig;
return readConfig;
}
final ReadConfigReducer = combineReducers<ReadConfig>([
TypedReducer<ReadConfig, UpdateReaderConfigAction>(_update),
]);
state
class DzState {
ReadConfig readConfig;
DzState(
{
this.readConfig,});
static DzState fromJson(dynamic json) => json != null
? DzState(
readConfig: ReadConfig.fromJson(json["readConfig"] ??
ReadConfig(bgColorIndex: 2, textFontIndex: 2).toJson()))
: DzState.empty();
dynamic toJson() => {
"readConfig": readConfig,
};
static DzState empty() {
return DzState(
readConfig: ReadConfig(bgColorIndex: 2, textFontIndex: 2),
);
}
}
DzState appReducer(DzState state, action) {
return new DzState(
readConfig: ReadConfigReducer(state.readConfig, action));
}
在 main.dart 中调用
final persistor = Persistor<DzState>(
storage: FlutterStorage(),
serializer: JsonSerializer<DzState>(DzState.fromJson),
);
// Load initial state
final initialState = await persistor.load();
final store = Store<DzState>(
appReducer,
initialState: initialState ?? DzState.empty(),
middleware: [persistor.createMiddleware()],
);
优点
既可以存简单的 key-value 的值, 也可以存储对象和数组.
缺点
创建一个对象比较复杂, 很容易初始化出错, 而且没法给对象中的单独变量修改,要修改的时候 必须把当前对象获取到,然后修改变量的值, 然后把整个对象给修改掉.
ReadConfig config = DzStore.getStore().state.readConfig;
config.textFontIndex = value.toInt();
DzStore.getStore().dispatch(UpdateReaderConfigAction(config));
数据状态管理
- redux
- provider
- bloc
数据的状态管理 一般用这三个.下面分别说说三个的优缺点.
redux
优点
只要 redux 里面的数值改变, 引用的地方都会刷新,比如启动多个页面 其中一个页面头像和昵称修改,那么回退回去后,页面的头像和昵称也会发生修改.数据及时同步
缺点
只要用 StoreBuilder<DzState>(builder: (storeContext, store)
的地方, 都会重新刷新,很容易造成没必要的性能浪费.
provider
优点
只要 provider 里面的数据发生改变, 引用当前的 provider 的页面才会重新刷新
缺点
如果当前 provider 有多个状态, 那么只要修改其中一个状态, 那页面也是都会刷新.也容易造成性能的浪费.
bloc
bloc 可以说是集成了 redux 和 provider 的所有优点, 并且优化了他们的缺点.
bloc 定义事件, 每个事件一个 state, 只要处理返回的对象 state 就行了,就减少了性能的浪费, 减少了没必要的刷新.
后面就在实战项目中来体现.