← → or space · progress saves for Continue on the roadmap
Goal
Serialize List<Todo> to a single JSON string and rebuild the list from that string.
Step 1 - Encode list
import 'dart:convert';
class Todo {
final String id;
final String title;
final bool done;
final String userId;
Todo({
required this.id,
required this.title,
this.done = false,
required this.userId,
});
factory Todo.fromJson(Map<String, dynamic> json) {
return Todo(
id: json['id'] as String,
title: json['title'] as String,
done: json['done'] as bool? ?? false,
userId: json['userId'] as String,
);
}
Map<String, dynamic> toJson() => {
'id': id,
'title': title,
'done': done,
'userId': userId,
};
}
String todosToJsonString(List<Todo> todos) {
final list = todos.map((t) => t.toJson()).toList();
return jsonEncode(list);
}Step 2 - Decode list
List<Todo> todosFromJsonString(String raw) {
final decoded = jsonDecode(raw);
if (decoded is! List<dynamic>) {
throw FormatException('expected JSON array');
}
return decoded
.map((e) => Todo.fromJson(e as Map<String, dynamic>))
.toList();
}
void main() {
final original = [
Todo(id: '1', title: 'a', userId: 'u1'),
Todo(id: '2', title: 'b', done: true, userId: 'u1'),
];
final text = todosToJsonString(original);
final restored = todosFromJsonString(text);
print(text);
print(restored.length);
}Step 3 - In-memory “persistence”
- Keep
String storagein a variable or tiny class field; assigntodosToJsonStringafter edits, calltodosFromJsonStringwhen loading. - Level 8 moves the same string to a real file.
Practice tasks
- Wrap
todosFromJsonStringinResult<List<Todo>>and mapFormatExceptionand cast failures to messages. - Add schema version key: encode
{"v":1,"items":[...]}and read both old raw array and new object for backward compatibility. - Pretty-print with
JsonEncoder.withIndent(' ')when saving for readable diffs in exercises.