An API response often has two layers: a shared envelope describing success or errors, and data whose type depends on the endpoint. This older Dart design explored that distinction.
Read the envelope first
{
"error": true,
"data": null,
"errors": [
{
"code": 1029,
"message": "User not found!."
}
]
}
The source’s BaseResponse<T> grouped an error flag, typed data, and a list of structured errors. Subclasses supplied data conversion in each direction. That moves shared envelope parsing into one place, but introduces a contract between the base constructor and subclass conversion methods.
Keep endpoint data explicit
The brand-list example sketched this shape:
{
"error": true,
"data": [{"name": name, "id": id}],
"errors": [
{
"code": 1029,
"message": "User not found!."
}
]
}
name and id are schematic values in that historical listing, not valid standalone JSON literals. The implementation mapped each record into a BrandModel and serialized the list back through toJson.
Different endpoints can have different envelopes
The login example placed token fields at the top level:
Error
{
"error": true,
"data": null,
"errors": [
{
"code": 1029,
"message": "User not found!."
}
]
}
Successful
{
"token_type": "Bearer",
"expires_in": 1295998,
"access_token": "EXAMPLE_ACCESS_TOKEN",
"refresh_token": "EXAMPLE_REFRESH_TOKEN"
}
Those values are generic labels. An endpoint with this shape cannot be assumed to follow the same nested-data contract as the brand list.
The old example’s error flow also accessed the first error and mixed parsing with loading-state handling. An empty error list, malformed response, timeout, and HTTP error need distinct handling.
The original implementation listings carried real-name and company metadata, so this revision keeps the design explanation and response examples rather than importing those modules. No new model implementation or API request is claimed.