Networking in Dart

Networking is an essential part of modern application development, enabling your applications to communicate with servers, fetch data, and interact with various APIs. In this guide, we'll explore how to perform networking in Dart, focusing particularly on working with REST APIs and parsing JSON data.

Making HTTP Requests

To start making HTTP requests in Dart, you need to use the http package, which provides a simple interface for executing HTTP calls. If you haven’t added the http package to your pubspec.yaml, you can do so by adding the following dependency:

dependencies:
  http: ^0.13.3

Run dart pub get to install the package.

Importing the http Package

In your Dart file, import the http package:

import 'package:http/http.dart' as http;

Making a GET Request

A GET request is a fundamental operation when you want to fetch data from the server. Here's how you can implement a simple GET request:

Future<void> fetchData() async {
  final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'));

  if (response.statusCode == 200) {
    print('Response data: ${response.body}');
  } else {
    throw Exception('Failed to load data');
  }
}

In this example, we’re fetching a list of posts from a placeholder API. The response.body contains the data returned from the server, and a successful statusCode means we received the data correctly.

Making a POST Request

Sometimes you'll need to send data to the server. In such cases, you can use a POST request. Here's a basic example:

Future<void> postData() async {
  final response = await http.post(
    Uri.parse('https://jsonplaceholder.typicode.com/posts'),
    headers: <String, String>{
      'Content-Type': 'application/json; charset=UTF-8',
    },
    body: jsonEncode(<String, String>{
      'title': 'foo',
      'body': 'bar',
      'userId': '1',
    }),
  );

  if (response.statusCode == 201) {
    print('Data posted successfully: ${response.body}');
  } else {
    throw Exception('Failed to post data');
  }
}

In the POST request, we are sending a JSON object. Note how we set Content-Type and convert the Dart object to a JSON string using jsonEncode.

Updating Data with PUT and PATCH Requests

If you want to update existing data, you can use PUT or PATCH requests. Here's an example using PUT:

Future<void> updateData() async {
  final response = await http.put(
    Uri.parse('https://jsonplaceholder.typicode.com/posts/1'),
    headers: <String, String>{
      'Content-Type': 'application/json; charset=UTF-8',
    },
    body: jsonEncode(<String, String>{
      'id': '1',
      'title': 'foo',
      'body': 'bar',
      'userId': '1',
    }),
  );

  if (response.statusCode == 200) {
    print('Data updated successfully: ${response.body}');
  } else {
    throw Exception('Failed to update data');
  }
}

For a PATCH request, you'd do something similar but typically send only the fields you want to update.

Deleting Data

To delete a resource, you can perform a DELETE request. Here’s an example:

Future<void> deleteData() async {
  final response = await http.delete(Uri.parse('https://jsonplaceholder.typicode.com/posts/1'));

  if (response.statusCode == 200) {
    print('Data deleted successfully');
  } else {
    throw Exception('Failed to delete data');
  }
}

Error Handling

When dealing with networking, it's vital to handle any potential errors gracefully. Here’s how you can manage exceptions:

Future<void> fetchDataWithErrorHandling() async {
  try {
    final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'));
    
    if (response.statusCode == 200) {
      print('Response data: ${response.body}');
    } else {
      print('Error: ${response.statusCode}');
    }
  } catch (e) {
    print('Caught an error: $e');
  }
}

Parsing JSON Data

Once you get the response from a server, it often comes in JSON format. Dart provides a built-in dart:convert library to help you decode JSON into Dart objects.

Decoding JSON

To decode the JSON response, you simply need to import the library:

import 'dart:convert';

Next, let's take a look at how to decode and parse JSON data:

Future<void> fetchAndParseData() async {
  final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'));

  if (response.statusCode == 200) {
    List<dynamic> data = jsonDecode(response.body);
    for (var item in data) {
      print('Post Title: ${item['title']}');
    }
  } else {
    throw Exception('Failed to load data');
  }
}

In this code, we're decoding the JSON string into a Dart List and iterating over it to print out each post title.

Encoding Dart Objects to JSON

You can also convert Dart objects to JSON format using jsonEncode. Here's how you might structure an object and send it:

class Post {
  final String title;
  final String body;
  final int userId;

  Post({required this.title, required this.body, required this.userId});

  Map<String, dynamic> toJson() => {
        'title': title,
        'body': body,
        'userId': userId,
      };
}

Future<void> postDartObject() async {
  final post = Post(title: 'foo', body: 'bar', userId: 1);

  final response = await http.post(
    Uri.parse('https://jsonplaceholder.typicode.com/posts'),
    headers: <String, String>{
      'Content-Type': 'application/json; charset=UTF-8',
    },
    body: jsonEncode(post.toJson()),
  );

  if (response.statusCode == 201) {
    print('Data posted successfully: ${response.body}');
  } else {
    throw Exception('Failed to post data');
  }
}

Conclusion

In this guide, we covered the essentials of networking in Dart, including making HTTP requests, handling different types of requests like GET, POST, PUT, and DELETE, as well as parsing and encoding JSON data. With these tools, you will be well-equipped to build applications that interact with external APIs and handle data efficiently.

Feel free to explore further into advanced topics like error handling, asynchronous programming, and working with other types of APIs. Happy coding!