Skip to main content

Tutorial 8: Model in Flutter and Communication with Web Services

Platform-Based Development (CSGE602022) - Organized by Faculty of Computer Science University of Indonesia, Odd Term 2022/2023

Learning Objectives

After completing this tutorial, students are expected to:

  • Understand the structure and model creation in Flutter.
  • Understand how to retrieve, process, and display data from a web service.

Model in Flutter

In this tutorial, we will make a web service call to display the data from it to the Flutter page we created. However, before making a web service call, we need to define the model that we use when making a web service call. The model in Flutter uses the class principle as learned in the DDP2 OOP section.

The code below is an example, not required, but highly recommended to read as the concepts will be used in the following tutorial sections.

The example below is an example of a class in Flutter.

class Vehicle {
Vehicle({
this.id,
this.brand,
this.model
this.color
});

int id;
String brand;
String model;
String color;
}

Notes: If you encounter an error when creating a class, add the required keyword to each class parameter in the constructor.

Up to this point, we have successfully created a class named Vehicle. Next, we will add some code until we successfully create a Vehicle model. This Vehicle is a model that represents the response from the web service call.

Add the import from dart convert at the very first line of the file.

import 'dart:convert';

In the Vehicle class, add the following code.

factory Vehicle.fromJson(Map<String, dynamic> json) => Vehicle(
id: json["id"],
brand: json["brand"],
model: json["model"],
color: json["color"],
);

Map<String, dynamic> toJson() => {
"id": id,
"brand": brand,
"model": model,
"color": color,
};

Add the following code outside the Vehicle class.

Vehicle vehicleFromJson(String str) => Vehicle.fromJson(json.decode(str));
String vehicleToJson(Vehicle data) => json.encode(data.toJson());

The final code will be as follows to display one Vehicle object from the web service response.

import 'dart:convert';

Vehicle vehicleFromJson(String str) => Vehicle.fromJson(json.decode(str));
String vehicleToJson(Vehicle data) => json.encode(data.toJson());

class Vehicle {
Vehicle({
this.id,
this.brand,
this.model,
this.color,
});

int id;
String brand;
String model;
String color;

factory Vehicle.fromJson(Map<String, dynamic> json) => Vehicle(
id: json["id"],
brand: json["brand"],
model: json["model"],
color: json["color"],
);

Map<String, dynamic> toJson() => {
"id": id,
"brand": brand,
"model": model,
"color": color,
};
}

The explanation of the code above is as follows.

There are some additional method such as toJson and fromJson in the Vehicle class. We have to create this method because when we request a web service with method GET, we generally get the result of the call in the form of JSON response. Therefore, we need to convert the data with the fromJson method so that Flutter recognizes the JSON as a Vehicle class object. In addition, toJson method will be used when we send data to a web service (such as POST or PUT method).

Here is an example of a response from a web service with the GET method that can be converted to the Vehicle model class.

{
"id": 1,
"brand": "Honda",
"model": "Civic",
"color": "Yellow"
}

Then, what if the response from the web service is a collection of JSON objects? Actually, it is the same as the code above, except that there is a change in the vehicleFromJson and vehicleToJson method.

The example can be seen below:

List<Vehicle> vehicleFromJson(String str) => List<Vehicle>.from(json.decode(str).map((vehicle) => Vehicle.fromJson(vehicle)));

String vehicleToJson(List<Vehicle> data) => json.encode(List<dynamic>.from(data.map((vehicle) => vehicle.toJson())));

Here is an example of a response from a web service with a GET method that can be converted to the Vehicle model.

[
{
"id": 1,
"brand": "Honda",
"model": "Civic",
"color": "Yellow"
},
{
"id": 2,
"brand": "Toyota",
"model": "Supra",
"color": "Red"
}
]

Fetch Data from Web Service in Flutter

As a developer, of course we need the data to be displayed to the client. This requires you to know how to do fetching data from web service and then display it to the application that we have created before.

In general, there are several steps when you want to display data from the web services to the Flutter application, namely:

  1. Adding the http dependency to the project, this dependency is used to exchange data through HTTP requests, such as GET, POST, PUT, and others.
  2. Define the model from the response of the data coming from the web service.
  3. Make an http request to the web service using the http dependency.
  4. Convert the objects obtained from the web service to the model that we created in the second step.
  5. Display data that has been converted to an application with FutureBuilder.

You can read the details in the following link: https://docs.flutter.dev/cookbook/networking/fetch-data#5-display-the-data

Tutorial: File Refactor

Code refactoring (refactor code) is the process of restructuring existing program code without changing the behavior of the program. This process is done to improve readability, reduce code complexity, and facilitate future maintenance.

  1. Open the project that was previously created in the previous tutorial using your favorite IDE.
  2. Inside the lib folder, create two new folders named model and page.
  3. Move files other than main.dart into the page folder.

Tutorial: Creating Custom Models

In creating a model that adapts to JSON data, we can utilize the website Quicktype with the following steps.

  1. Open the link URL https://jsonplaceholder.typicode.com/todos?_start=0&_limit=10 to obtain the JSON data.

  2. Copy the JSON data from the previous link URL, then open the link URL Quicktype.

  3. From the Quicktype website, change the setup name to ToDo, source type to JSON, and language to Dart.

  4. Paste the copied JSON data into the textbox provided in the Quicktype.

    Here is an example of the result.

    Quicktype Example

  5. Click the Copy Code option in Quicktype.

After getting the ToDo model code via Quicktype, reopen the Flutter project and perform the following steps.

  1. Create a new file in the lib/model folder with the name to_do.dart.
  2. Paste the previously copied code into the to_do.dart file.

Notes: If you encounter an error when creating a model, add the required keyword to each model parameter in the constructor.

Tutorial: Adding HTTP Dependencies

To perform the HTTP request, we need an additional package named http.

  1. Run the command flutter pub add http on the Flutter project terminal to add the http package.

  2. In the android manifest file in android/app/src/main/AndroidManifest.xml, add the following code to allow Internet access in the Flutter application that we created.

    ...
    <application>
    ...
    </application>
    <!-- Required to fetch data from the Internet. -->
    <uses-permission android:name="android.permission.INTERNET" />
    ...

Tutorial: Retrieve and Process Data from Web Service

  1. Create a new file in the lib/page folder with the name to_do_page.dart.

  2. In the to_do_page.dart file, add the necessary imports. Change <APP_NAME> to the name of your Flutter project.

    import 'package:http/http.dart' as http;
    import 'dart:convert';
    import 'package:<APP_NAME>/model/to_do.dart';
    ...
  3. Create a stateful widget with the class name ToDoPage. An example of a stateful widget structure can be seen at the following link.

  4. Retrieve data from the URL https://jsonplaceholder.typicode.com/todos?_start=0&_limit=10 using the http.get method.

    class ToDoPage extends StatefulWidget {
    const ToDoPage({Key? key}) : super(key: key);

    @override
    _ToDoPageState createState() => _ToDoPageState();
    }

    class _ToDoPageState extends State<ToDoPage> {
    Future<List<ToDo>> fetchToDo() async {
    var url = Uri.parse('https://jsonplaceholder.typicode.com/todos?_start=0&_limit=10');
    var response = await http.get(
    url,
    headers: {
    "Access-Control-Allow-Origin": "*",
    "Content-Type": "application/json",
    },
    );

    // decode the response into the json form
    var data = jsonDecode(utf8.decode(response.bodyBytes));

    // convert the json data into ToDo object
    List<ToDo> listToDo = [];
    for (var d in data) {
    if (d != null) {
    listToDo.add(ToDo.fromJson(d));
    }
    }

    return listToDo;
    }
    ...
    }

Tutorial: Display Data from Web Service

  1. In the main.dart and form.dart files, add the following code to add the To Do menu to the drawer we have created (place it under the ListTile form menu).

    ListTile(
    title: const Text('To Do'),
    onTap: () {
    // Route the menu to the to do page
    Navigator.pushReplacement(
    context,
    MaterialPageRoute(builder: (context) => const ToDoPage()),
    );
    },
    ),

    So the code becomes like this:

    ...
    ListTile(
    title: const Text('Form'),
    onTap: () {
    // Route the menu to the form page
    Navigator.pushReplacement(
    context,
    MaterialPageRoute(builder: (context) => const MyFormPage()),
    );
    },
    ),
    ListTile(
    title: const Text('To Do'),
    onTap: () {
    // Route the menu to the to do page
    Navigator.pushReplacement(
    context,
    MaterialPageRoute(builder: (context) => const ToDoPage()),
    );
    },
    ),
    ...
  2. In the Widget(BuildContext context) section, add the following code after return.

    Scaffold(
    appBar: AppBar(
    title: const Text('To Do'),
    ),
    drawer: Drawer(
    child: Column(
    children: [
    // Adding clickable menu
    ListTile(
    title: const Text('Counter'),
    onTap: () {
    // Route the menu to the main page
    Navigator.pushReplacement(
    context,
    MaterialPageRoute(builder: (context) => const MyApp()),
    );
    },
    ),
    ListTile(
    title: const Text('Form'),
    onTap: () {
    // Route the menu to the form page
    Navigator.pushReplacement(
    context,
    MaterialPageRoute(builder: (context) => const MyFormPage()),
    );
    },
    ),
    ListTile(
    title: const Text('ToDo'),
    onTap: () {
    // Route the menu to the to do page
    Navigator.pushReplacement(
    context,
    MaterialPageRoute(builder: (context) => const ToDoPage()),
    );
    },
    ),
    ],
    ),
    ),
    body: FutureBuilder(
    future: fetchToDo(),
    builder: (context, AsyncSnapshot snapshot) {
    if (snapshot.data == null) {
    return const Center(child: CircularProgressIndicator());
    } else {
    if (!snapshot.hasData) {
    return Column(
    children: const [
    Text(
    "To do list is empty :(",
    style: TextStyle(
    color: Color(0xff59A5D8),
    fontSize: 20),
    ),
    SizedBox(height: 8),
    ],
    );
    } else {
    return ListView.builder(
    itemCount: snapshot.data!.length,
    itemBuilder: (_, index)=> Container(
    margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
    padding: const EdgeInsets.all(20.0),
    decoration: BoxDecoration(
    color:Colors.white,
    borderRadius: BorderRadius.circular(15.0),
    boxShadow: const [
    BoxShadow(
    color: Colors.black,
    blurRadius: 2.0
    )
    ]
    ),
    child: Column(
    mainAxisAlignment: MainAxisAlignment.start,
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
    Text(
    "${snapshot.data![index].title}",
    style: const TextStyle(
    fontSize: 18.0,
    fontWeight: FontWeight.bold,
    ),
    ),
    const SizedBox(height: 10),
    Text("${snapshot.data![index].completed}"),
    ],
    ),
    )
    );
    }
    }
    }
    )
    );
  3. Restart the application by pressing the r key on the command line or terminal where you are running the Flutter application. The result will be as shown below.

    Example App

Final Word

Congratulations, you've learned about models and web services in Flutter!

If you're up for a challenge, then try applying the following to this tutorial.

  • Refactor the fetchToDo method to a separate file.

Additional Reference

Contributor

  • Zuhal 'Alimul Hadi
  • Sabyna Maharani
  • Brandon Ivander
  • Muhammad Athallah
  • Firlandi A. R. Ansyari (EN Translator)