Skip to content

Instantly share code, notes, and snippets.

@AlexV525
Last active June 12, 2024 21:08
Show Gist options
  • Save AlexV525/49bb37fe9a2b5127d45fcd39a7ac0d2e to your computer and use it in GitHub Desktop.
Save AlexV525/49bb37fe9a2b5127d45fcd39a7ac0d2e to your computer and use it in GitHub Desktop.
Resolve response model with generic type and base model
///
/// [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