Menu
CoddyTech

Reading JSON

Part of the Data and the Network section of Coddy's Flutter journey. Lesson 15 of 27.

Servers rarely answer with a plain sentence. They send JSON: text that holds data, with names in quotes and values after a colon. The practice API's /user answers this:

{
  "name": "Maya Chen",
  "email": "maya@example.com",
  "age": 28,
  "isPro": true
}

To your app it is one long String. jsonDecode, from Dart's dart:convert library, turns that text into Dart values. Curly braces become a Map: the names are its keys.

import 'dart:convert';

final Map<String, dynamic> user = jsonDecode(body);
print(user['name']);   // Maya Chen

The keys are always strings. The values can be text, numbers, true or false, lists or more maps, so their type is dynamic: "any type, decided while the app runs". Keys match exactly: user['Name'] is not user['name'], and a missing key gives null.

Decode the answer where you fetch it, in its own function, so the screen gets ready-to-use data.

challenge icon

Challenge

Medium

The profile screen shows the raw JSON. Show the person instead:

  • loadUser returns a Future<Map<String, dynamic>>: it awaits fetch('/user') and returns jsonDecode of the body (import dart:convert)
  • _user and the FutureBuilder use that type too
  • when the data arrives, show a Column with user['name'] (size 28, bold) and under it user['email']

Try it yourself

import 'package:flutter/material.dart';

import 'api.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: ProfileScreen(),
    );
  }
}

class ProfileScreen extends StatefulWidget {
  const ProfileScreen({super.key});

  @override
  State<ProfileScreen> createState() => _ProfileScreenState();
}

class _ProfileScreenState extends State<ProfileScreen> {
  late Future<String> _user;

  @override
  void initState() {
    super.initState();
    _user = loadUser();
  }

  Future<String> loadUser() async {
    return fetch('/user');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Profile')),
      body: Center(
        child: FutureBuilder<String>(
          future: _user,
          builder: (context, snapshot) {
            if (snapshot.hasError) {
              return Text('Could not load: ${snapshot.error}');
            }
            if (snapshot.hasData) {
              return Text(snapshot.data!);
            }
            return const CircularProgressIndicator();
          },
        ),
      ),
    );
  }
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Data and the Network

Practice on your own: Online Flutter compiler