You print an API response in Flutter, copy it from the console, paste it into a JSON tool, and it is rejected. That is because print(map) does not print JSON. This guide shows what Dart actually prints, how to print real JSON (compact or pretty), how to handle values that JSON cannot represent, and what to do when all you have is a log that was already printed the wrong way.
All output below was produced with Dart 3.13.7, which is the Dart shipped with current Flutter.
Why print(map) is not JSON
print calls toString(), and Map.toString() is meant for people, not parsers:
final user = {
'userId': 1024,
'name': 'Ada Lovelace',
'roles': ['admin', 'editor'],
'verified': true,
'lastLogin': null,
'address': {'city': 'Phnom Penh', 'zip': '12000'},
};
print(user);
// {userId: 1024, name: Ada Lovelace, roles: [admin, editor], verified: true, lastLogin: null, address: {city: Phnom Penh, zip: 12000}}No key or string is quoted. debugPrint takes a String, so debugPrint('$user') prints the same text. Worse, the output loses information: you cannot tell whether a value was a string or a number.
print({'id': 7, 'code': '7', 'zip': '007', 'flag': 'true', 'note': null, 'empty': ''});
// {id: 7, code: 7, zip: 007, flag: true, note: null, empty: }7 and '7' print the same, and so do the boolean true and the string 'true'. A comma inside a value is ambiguous too: {'name': 'Doe, John', 'city': 'Phnom Penh'} prints as {name: Doe, John, city: Phnom Penh}.
Print real JSON with jsonEncode
jsonEncode from dart:convert produces valid, compact JSON with every type preserved:
import 'dart:convert';
print(jsonEncode(user));
// {"userId":1024,"name":"Ada Lovelace","roles":["admin","editor"],"verified":true,"lastLogin":null,"address":{"city":"Phnom Penh","zip":"12000"}}Compare the last field: "zip":"12000" keeps its quotes, so you can see it is a string. The ambiguous example above also comes out exactly:
{"id":7,"code":"7","zip":"007","flag":"true","note":null,"empty":""}Pretty-print JSON in Flutter
For readable logs, use JsonEncoder.withIndent:
print(const JsonEncoder.withIndent(' ').convert(user));
// {
// "userId": 1024,
// "name": "Ada Lovelace",
// "roles": [
// "admin",
// "editor"
// ],
// "verified": true,
// "lastLogin": null,
// "address": {
// "city": "Phnom Penh",
// "zip": "12000"
// }
// }A small helper keeps call sites short. It uses debugPrint, which Flutter's lints prefer over print in app code:
import 'dart:convert';
import 'package:flutter/foundation.dart';
void debugPrintJson(Object? value) {
debugPrint(const JsonEncoder.withIndent(' ').convert(value));
}Errors you will hit, and the fixes
Converting object to an encodable object failed: Instance of 'DateTime'
JSON has no date type, so jsonEncode refuses a DateTime:
jsonEncode({'at': DateTime.utc(2026, 11, 5, 9, 30)});
// Converting object to an encodable object failed: Instance of 'DateTime'Pass a toEncodable function that converts what JSON cannot hold:
jsonEncode(
{'at': DateTime.utc(2026, 11, 5, 9, 30)},
toEncodable: (o) => o is DateTime ? o.toIso8601String() : o,
);
// {"at":"2026-11-05T09:30:00.000Z"}For date handling in models, see DateTime and JSON in Flutter.
Your own classes: add toJson()
jsonEncode calls a toJson() method when an object has one, including objects nested in maps and lists:
class User {
User(this.id, this.name, this.joined);
final int id;
final String name;
final DateTime joined;
Map<String, dynamic> toJson() => {
'id': id,
'name': name,
'joined': joined.toIso8601String(),
};
}
jsonEncode({'users': [User(1, 'Ada', DateTime.utc(2026, 1, 2))]});
// {"users":[{"id":1,"name":"Ada","joined":"2026-01-02T00:00:00.000Z"}]}Without toJson(), the same call fails with Converting object to an encodable object failed: Instance of 'Plain'. The JSON to Dart generator writes fromJson and toJson for you from a sample response.
Instance of 'User' in the log
Printing an object that has no toString() override gives nothing useful:
print([User(1, 'Ada', DateTime.utc(2026, 1, 2))]);
// [Instance of 'User']The field values are not in that text, so no tool can recover them. Print jsonEncode(value) instead.
Map keys must be strings
JSON object keys are always strings. A map with int keys fails:
jsonEncode({1: 'a'});
// Converting object to an encodable object failed: _Map len:1Convert the keys first, for example with map.map((k, v) => MapEntry('$k', v)).
Already have a printed Map? Convert it back
Sometimes the only copy of a response is a log line, from a teammate, a crash report or an HTTP logging interceptor that printed toString() output. The JSON formatter can convert it: paste the text and press Try to repair. It adds the quotes, keeps nested maps and lists, and removes log prefixes such as flutter: or I/flutter (12345):. For the first example above it returns:
{
"userId": 1024,
"name": "Ada Lovelace",
"roles": [
"admin",
"editor"
],
"verified": true,
"lastLogin": null,
"address": {
"city": "Phnom Penh",
"zip": 12000
}
}Look at zip: it was the string '12000' in Dart, but the printed text does not say so, and the repair reads it as the number 12000. The same happens to code: 7 and flag: true in the ambiguous example. Values with leading zeros, such as 007, stay strings. The tool lists these guesses after each repair; check IDs, phone numbers and postal codes before relying on the result. A comma inside a value is kept when what follows does not look like the next key, so {name: Doe, John, city: Phnom Penh} becomes {"name": "Doe, John", "city": "Phnom Penh"}.
The repair runs in your browser and nothing you paste is uploaded, which matters for logs that contain user data or tokens.
Summary
print(map)anddebugPrint('$map')printtoString()output, not JSON, and lose the difference between strings, numbers and booleans.- Log
jsonEncode(value), orJsonEncoder.withIndent(' ').convert(value)for pretty output. - Give your classes a
toJson()and passtoEncodablefor values likeDateTime. - For a log that was already printed, repair it in the JSON formatter and check the values it had to guess.
If the JSON is valid but decoding it in Flutter fails with errors such as type 'String' is not a subtype of type 'Map<String, dynamic>', see Flutter JSON type errors.