Flutter 实时数据库 onValue 解析
Posted
技术标签:
【中文标题】Flutter 实时数据库 onValue 解析【英文标题】:Flutter realtime database onValue parsing 【发布时间】:2021-06-09 13:56:04 【问题描述】:我是第一次尝试使用 NoSQL(firebase 实时数据库),但在 Flutter 应用程序中构建和解析数据时遇到了一些问题。 起初我有一个带有一些属性的简单“人”模型,一切都很好,但后来我不得不引入一个 ID 作为节点并嵌套其他属性以执行 CRUD 操作,现在我无法解析我的“更新“不再是‘人’模型了。 我不知道这是否可以,但为简单起见(我知道这不是正确的 ID),我决定我的 ID('personName' 属性)是人名,因此 DB 上的当前结构是:
我正在使用 freezed 包,PersonDto 看起来像这样(省略域方法):
@freezed
class PersonDto with _$PersonDto
const PersonDto ._();
const factory PersonDto (
required String personName,
required int age,
required String genre,
required double height,
required String hobby,
required double weight,
) = _PersonDto ;
factory PersonDto.fromJson(Map<String, dynamic> json) =>
_$PersonDto FromJson(json);
在存储库中有一个方法负责从 firebase 接收、解析和流式传输数据。 我的问题基本上是我无法使用节点的键作为“名称”的属性来生成“人”模型。 这是我得到的最接近的:
Stream<Either<PersonFailure, List<Person>>> watchAll() async*
yield* _firebaseDatabase
.reference()
.child('persons')
.onValue
.map((event)
return right<PersonFailure, List<Person>>(
(event.snapshot.value as LinkedHashMap).values.map((personMap)
final json = Map<String, dynamic>.from(personMap as LinkedHashMap);
//
//this snippet works. I'm able to generate a proper 'Person' model but like this
//there's no way to retrieve the key from event.snapshot.value
//
json.addAll(
'personName': 'NAME OF THE PERSON',
);
return PersonDto.fromJson(json).toDomain();
).toList());
).onErrorReturnWith((e)
print('WATCH ERROR $e.toString()');
return left(const PersonFailure.unexpected());
);
应该是这样的,不幸的是,这同样不起作用:
yield* _firebaseDatabase
.reference()
.child('persons')
.onValue
.map((event)
(event.snapshot as LinkedHashMap).map((key, value)
final personName = key;
final json = value as Map<String,dynamic>;
json.addAll('personName':personName);
//
//error: The return type 'Person' isn't a 'MapEntry<_, _>', as required by the closure's context.
//
return PersonDto.fromJson(json).toDomain();
);
);
【问题讨论】:
【参考方案1】:好的,我设法让它工作。这有点难看,但我找不到更漂亮的方法,所以我们开始吧。
首先,“PersonDto”与 firebase 1:1 映射,并且 freezed 将处理 fromJson/toJson 方法。为了将 DTO 转换为域模型,只是缺少“personName”属性,因此一旦从映射键中提取,我将手动将其提供给“toDomain”方法:
@freezed
class PersonDto with _$PersonDto
const PersonDto ._();
const factory PersonDto (
required int age,
required String genre,
required double height,
required String hobby,
required double weight,
) = _PersonDto ;
Person toDomain(String personName)
return Person(
personName:personName,
age:age,
genre:genre,
height:height,
hobby:hooby,
weight:weight,
)
现在是令人讨厌的部分。在存储库方法中,我必须执行大量转换才能生成 Person 对象列表。
关键部分已将从 firebase 收到的 LinkedHashMap 转换为 Map
Stream<Either<PersonFailure, List<Person>>> watchAll() async*
yield* _firebaseDatabase
.reference()
.child('persons')
.onValue
.map((event)
final firebaseMap = Map<String, dynamic>.from(event.snapshot.value as LinkedHashMap);
final firebaseMapList = List<MapEntry<String, dynamic>>.from(firebaseMap.entries);
final personsList = firebaseMapList.map((personMap)
final personName = personMap.key;
final json = personMap.value as LinkedHashMap<dynamic, dynamic>;
final personDto =
PersonDto.fromJson(Map<String, dynamic>.from(json));
return personDto.toDomain(personName);
).toList();
return right<PersonFailure, List<Person>>(personsList );
).onErrorReturnWith((e) =>
left(const PersonFailure.unexpected()),
);
我现在可以“轻松”按爱好(或按流派或其他)将 firebase 上的人员记录分开,使用 .reference().child('persons').child('hobby') 进行查询,删除从 PersonDto 获取 hobby 参数,然后使用上面的 sn-p 并手动将 hobby 参数提供给 toDomain 方法。
如果有人可以提供更简洁的版本,我会很高兴看看它!
【讨论】:
【参考方案2】:只需要添加一个空检查。像这样;
.....
.onValue
.map((event)
List<ListsModel> _offers = <ListsModel>[];
if(event.snapshot.value != null)
final _resultList =
Map<String, dynamic>.from(e.snapshot.value as LinkedHashMap);
for (var key in _resultList.keys)
Map<dynamic, dynamic> map = Map.from(_resultList[key]);
ListsModel listsModel = ListsModel.fromMap(map);
_offers.add(listModel);
.....
【讨论】:
以上是关于Flutter 实时数据库 onValue 解析的主要内容,如果未能解决你的问题,请参考以下文章