Skip to content

Example: Employees

This material demonstrates how to load and parse JSON data in a Flutter App.

Employees 01

Create Employees Flutter App

Create a new Employees Flutter app with a VS Code.

  • Launch Visual Studio Code and open the command palette (with F1 or Ctrl+Shift+P or Shift+Cmd+P). Start typing flutter new. Select the Flutter: New Project command.
  • Next, select Application and then a folder in which to create your project.
  • Finally, name your project. Something like employees_app.

Launch Android Emulator

Use Android Studio and launch one Android Emulator. Run your code from VS Code and look that tempalate is running correctly.

You should see a basic UI with AppBar, Text's and FloatingActionButton.

Employees 02

Fetch data

Fetching data from the internet is necessary for most apps. Luckily, Dart and Flutter provide tools, such as the http package, for this type of work.

Add the http package

The http package provides the simplest way to fetch data from the internet. To install the http package, add it to the dependencies section of the pubspec.yaml file. Look installation information from http package page.

Basicly you will need to give a following command to add http to your project.

1
flutter pub add http

This will add a below line like this to your package's pubspec.yaml file.

pubspec.yaml
1
2
dependencies:
  http: <latest_version>

For example 1.1.0 version

pubspec.yaml
1
2
3
4
5
6
dependencies:
  flutter:
    sdk: flutter
  ...  
  http: ^1.1.0 
  ...

Next you need to import the http package.

lib/main.dart
1
import 'package:http/http.dart' as http;

Add the Internet permission to your AndroidManifest.xml file

android/app/main/AndroidManifest.xml
1
2
3
4
5
6
7
8
9
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.employee_app">

    <uses-permission android:name="android.permission.INTERNET" />

   <application>
    ...
   </application>
</manifest>

JSON structure

This example covers how to fetch a JSON document that contains a list of employees objects from the JSON file stored in server side using the http.get() method.

Data url is https://ptm.fi/data/flutter_employees.json. Look the structure of JSON:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[
    {
        "id": 1,
        "firstName": "Violante",
        "lastName": "Longhi",
        "email": "vlonghi0@wikispaces.com",
        "phone": "633-736-5682",
        "title": "Structural Analysis Engineer",
        "department": "Training",
        "image": "https://randomuser.me/api/portraits/med/men/8.jpg"
    },
...

Parse and convert the JSON into a list of employees

First, create an Employee class that contains the data from the network request. It includes a factory constructor that creates an Employee from JSON.

Add a below Employee class to your main.dart file just below import lines.

lib/main.dart
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Employee {
  final int id;
  final String firstName;
  final String lastName;
  final String email;
  final String phone;
  final String title;
  final String department;
  final String image;

  const Employee({
    required this.id,
    required this.firstName,
    required this.lastName,
    required this.email,
    required this.phone,
    required this.title,
    required this.department,
    required this.image,
  });

  factory Employee.fromJson(Map<String, dynamic> json) {
    return Employee(
      id: json['id'] as int,
      firstName: json['firstName'] as String,
      lastName: json['lastName'] as String,
      email: json['email'] as String,
      phone: json['phone'] as String,
      title: json['title'] as String,
      department: json['department'] as String,
      image: json['image'] as String,
    );
  }
}

Make a network request

Add a following code to your lib/main.dart file. Just below above Employee class.

lib/main.dart
1
2
3
Future<http.Response> fetchEmployees(http.Client client) async {
  return client.get(Uri.parse('(https://ptm.fi/data/flutter_employees.json'));
}

The client.get() method returns a Future that contains a Response. Future is a core Dart class for working with async operations. A Future object represents a potential value or error that will be available at some time in the future. The http.Response class contains the data received from a successful http call.

Note

You’re providing a http.Client into the function in this example. This makes the function easier to test and use in different environments. Read more from here http.Client.

Convert the response into a list of employees

Now, use the following instructions to update the above fetchEmployees() function so it will return a Future<List<Employee>>:

  • Create a parseEmployees() function that converts the response body into a List<Employee>.
lib/main.dart
1
2
3
4
List<Employee> parseEmployees(String responseBody) {
  final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
  return parsed.map<Employee>((json) => Employee.fromJson(json)).toList();
}
  • Use the parseEmployees() function in the fetchEmployees() function.
  • Use compute() function to move the parsing and conversion to a background isolate.
lib/main.dart
1
2
3
4
Future<List<Employee>> fetchEmployees(http.Client client) async {
  final response =  await client.get(Uri.parse('https://ptm.fi/data/flutter_employees.json'));
  return compute(parseEmployees, response.body);
}

You need to use a few import lines to get compute and jsonDecode work.

  • import 'package:flutter/foundation.dart' for compute
  • import 'dart:convert' for jsonDecode

Show Employees

Isolates communicate by passing messages back and forth. These messages can be primitive values, such as null, num, bool, double, or String, or simple objects such as the List<Employee> in this example.

MyApp

Project has default for main and MyApp. You can modify those just to show correct App name and title.

lib/main.dart
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Employees App',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterials3: true,
      ),
      home: const MyHomePage(title: 'Employees Home Page'),
    );
  }
}

MyHomePage

To display the data on screen, use the FutureBuilder widget. The FutureBuilder widget comes with Flutter and makes it easy to work with asynchronous data sources.

You must provide two parameters:

  • The Future you want to work with. In this case, the future returned from the fetchEmployees() function.
  • A builder function that tells Flutter what to render, depending on the state of the Future: loading, success, or error.

Note that snapshot.hasData only returns true when the snapshot contains a non-null data value.

Modify your project MyHomePage to

lib/main.dart
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: FutureBuilder<List<Employee>>(
        future: fetchEmployees(http.Client()),
        builder: (context, snapshot) {
          if (snapshot.hasError) {
            return const Center(
              child: Text('An error has occurred!'),
            );
          } else if (snapshot.hasData) {
            return EmployeesList(employees: snapshot.data!);
          } else {
            return const Center(
              child: CircularProgressIndicator(),
            );
          }
        },
      ),
    );
  }
}

EmployeesList

Create a new EmployeesList class which will display a loaded JSON data in the screen.

EmployeesList will be a StatelessWidget. You can find more information from here: StatelessWidget. Now our data doesn't change dynamically and we don't need to use StatefulWidget. You can find more information from here StatefulWidget.

lib/main.dart
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class EmployeesList extends StatelessWidget {
  const EmployeesList({super.key, required this.employees});

  final List<Employee> employees;

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: employees.length,
      itemBuilder: (context, index) {
        return Padding(
          padding: const EdgeInsets.all(8.0),
          child: Row(
            children: [
              ClipRRect(
                  borderRadius: BorderRadius.circular(12.0),
                  child: Image.network(employees[index].image)),
              const SizedBox(width: 10),
              Column(
                mainAxisAlignment: MainAxisAlignment.start,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    "${employees[index].lastName} ${employees[index].firstName}",
                    style: const TextStyle(fontWeight: FontWeight.bold),
                  ),
                  Text(employees[index].title),
                  Text(employees[index].department),
                  Text(employees[index].email),
                ],
              ),
            ],
          ),
        );
      },
    );
  }
}

Running

Click CTRL-F5 to launch your application.

Read more