Last active
June 12, 2024 21:08
-
-
Save AlexV525/49bb37fe9a2b5127d45fcd39a7ac0d2e to your computer and use it in GitHub Desktop.
Resolve response model with generic type and base model
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// | |
/// [Author] Alex (https://github.com/AlexV525) | |
/// [Date] 11/26/20 4:31 PM | |
/// | |
import 'dart:convert'; | |
import 'dart:core'; | |
import 'package:flutter/foundation.dart'; | |
import 'package:equatable/equatable.dart'; | |
abstract class DataModel extends Equatable { | |
const DataModel(); | |
Map<String, dynamic> toJson(); | |
@override | |
String toString() => GlobalJsonEncoder.convert(toJson()); | |
} | |
typedef DataFactory<T extends DataModel> = T Function( | |
Map<String, dynamic> json, | |
); | |
T makeModel<T extends DataModel>(Map<dynamic, dynamic> json) { | |
if (!dataModelFactories.containsKey(T)) { | |
throw TypeError(); | |
} | |
try { | |
return dataModelFactories[T]!(json) as T; | |
} catch (e) { | |
print( | |
'Error when making model with $T type: $e\n' | |
'${json.containsKey('id') ? 'Model contains id: ${json['id']}\n' : ''}' | |
'The raw data which make this error is:\n' | |
'${GlobalJsonEncoder.convert(json)}', | |
); | |
rethrow; | |
} | |
} | |
class EmptyDataModel extends DataModel { | |
const EmptyDataModel(); | |
factory EmptyDataModel.fromJson(dynamic _) => const EmptyDataModel(); | |
@override | |
Map<String, dynamic> toJson() => const <String, dynamic>{}; | |
@override | |
List<Object?> get props => <Object?>[null]; | |
} | |
@immutable | |
class ResponseModel<T extends DataModel> { | |
const ResponseModel({ | |
required this.code, | |
required this.msg, | |
this.rawData, | |
this.data, | |
this.pageNum, | |
this.pageSize, | |
this.total, | |
this.models, | |
}); | |
factory ResponseModel.fromJson( | |
Map<String, dynamic> json, { | |
bool isModels = false, | |
}) { | |
return ResponseModel<T>( | |
code: json['code'] as int? ?? 0, | |
msg: json['msg'] as String? ?? | |
json['detail'] as String? ?? | |
'_ExternalError (0)', | |
data: !isModels && json['data'] != null | |
? makeModel<T>(json['data'] as Map<dynamic, dynamic>) | |
: null, | |
rawData: json['data'], | |
pageNum: json['page_num'] as int?, | |
pageSize: json['page_size'] as int?, | |
total: json['total'] as int?, | |
models: isModels && json['data'] != null && json['data'] is List | |
? (json['data'] as List<dynamic>) | |
.cast<Map<dynamic, dynamic>>() | |
.map((Map<dynamic, dynamic> item) => makeModel<T>(item)) | |
.toList() | |
: null, | |
); | |
} | |
factory ResponseModel.replaceWith({ | |
required ResponseModel<WithdrawRecordModel> model, | |
List<T>? models, | |
}) { | |
return ResponseModel<T>( | |
code: model.code, | |
msg: model.msg, | |
// data: model.data as T, | |
rawData: model.rawData, | |
pageNum: model.pageNum, | |
pageSize: model.pageSize, | |
total: model.total, | |
models: models, | |
); | |
} | |
final int code; | |
final String msg; | |
final T? data; | |
/// This is the raw data for the model. | |
final Object? rawData; | |
/// Below fields only works when requesting a list of data. | |
final int? pageNum; | |
final int? pageSize; | |
final int? total; | |
final List<T>? models; | |
bool get isSucceed => code == 1; | |
bool get isRequestError => msg.contains('_InternalRequestError') == true; | |
bool get canRequestMore => (pageNum! * pageSize!) < total!; | |
Map<String, dynamic> toJson() { | |
return <String, dynamic>{ | |
'code': code, | |
'msg': msg, | |
if (data != null) 'data': data!.toJson(), | |
if (pageNum != null) 'page_num': pageNum, | |
if (pageSize != null) 'page_size': pageSize, | |
if (total != null) 'count': total, | |
if (models != null) | |
'models': models!.map((T model) => model.toJson()).toList(), | |
}; | |
} | |
@override | |
String toString() => const JsonEncoder.withIndent(' ').convert(toJson()); | |
} | |
final Map<Type, Function> dataModelFactories = <Type, DataFactory>{ | |
EmptyDataModel: (Map<String, dynamic> json) => EmptyDataModel.fromJson(json), | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment