Dart Package Tutorial – Getting Started

Learn how to create your first Dart package using test-driven development, generate documentation and publish it to pub.dev. By Agustinus Theodorus.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Adding the Dependencies Through Terminal

Open the terminal and navigate genderizeio. Type the following commands to add the dependencies:

dart pub add HTTP
dart pub add build_runner --dev
dart pub add mockito --dev
dart pub get

Press Enter, and check the output.
You’ll see repeated similar messages for each command:

Resolving dependencies...
....
Changed ... dependencies!

This command helps add dependencies directly through the terminal.

Note: You’ll use the Mockito package to mock network responses. It’s bad practice to request remote data in tests. Several issues might give false errors while testing with a real API, like Network Error, Server Error and Access Error. For more on how to use this package, check out this documentation.

Mockito requires you to run build_runner to generate mocks for annotated classes. Look at genderizeio_test.dart and you’ll see @GenerateMocks([http.Client]). Mockito will generate test/genderizeio_test.mocks.dart with a mocked http.Client class.

To generate mocks, run the following command in the terminal:

dart run build_runner build

Press Enter, and check the output:

$ dart run build_runner build
[INFO] Generating build script completed, took 238ms
[INFO] Reading cached asset graph completed, took 26ms
[INFO] Checking for updates since last build completed, took 296ms
[INFO] Running build completed, took 6ms
[INFO] Caching finalized dependency graph completed, took 15ms
[INFO] Succeeded after 27ms with 0 outputs (0 actions)

Create a public interface for your package by replacing lib/src/genderizeio_base.dart with:

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


class Genderize {
  Genderize({
    required this.gender,
  });

  final String gender; // The gender prediction
}

class GenderizeAPI {
  GenderizeAPI([http.Client? client]) : client = client ?? http.Client();

  /// Http client dependency to send network requests.
  final http.Client client;

  Future<Genderize> send(String name) async {
    return Genderize(gender: 'unknown');
  }
}

Now all the errors have been fixed

This minimal public interface lets consumers call your package and run tests on it.

Rerun the tests using dart test test/genderizeio_test.dart:

$ dart test test/genderizeio_test.dart 
Building package executable... (2.5s)
Built test:test.
00:00 +0 -1: Genderize.io Peter is male [E]                                                                                                                       
  Expected: 'male'
    Actual: 'unknown'
     Which: is different.
            Expected: male
              Actual: unknown
                      ^
             Differ at offset 0
  
  package:test_api                 expect
  test/genderizeio_test.dart 55:7  main.<fn>.<fn>
  
00:00 +0 -2: Genderize.io API exception [E]                                                                                                                       
  Expected: throws <Instance of 'Exception'>
    Actual: <Instance of 'Future<Genderize>'>
     Which: emitted <Instance of 'Genderize'>
  
  test/genderizeio_test.dart 67:7  main.<fn>.<fn>
  
00:00 +0 -2: Some tests failed.                                                                                                                                   

Consider enabling the flag chain-stack-traces to receive more detailed exceptions.
For example, 'dart test --chain-stack-traces'.

Tests failed, but they were supposed to fail.

You finished the first part of the TTD process. The next sections help you implement business logic so that all the tests can pass.

Importing Dependencies

Note: If you finished the previous section, Test-Driven Development of the Dart Package, you already have all the dependencies that you’ll need. Feel free to go directly to the next section.

Since you need to interact with a REST API, you must add a dependency to communicate with HTTP services.

Open pubspec.yaml, and add the following line in the dependencies section:

dependencies:
  http: ^0.13.4

Open your terminal, and install your Dart dependencies:

dart pub get

Press Enter, and check the result:

$ dart pub get
Resolving dependencies... 
Got dependencies!

The above code adds all the packages mentioned in pubspec.yaml into our project.

Now that you have the HTTP package you can write HTTP functions to get a response from Genderize API.

Dart Package Business Logic

To pass the tests, you must implement the goals you previously defined. Go to lib/src/genderizeio_base.dart and replace its content with:

import 'dart:async';
import 'dart:convert';

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

//1
class Genderize {
  Genderize({
    required this.name,
    required this.gender,
    required this.probability,
    required this.count,
  });
  final String name; /// The name submitted through the query parameters
  final String gender; /// The gender prediction
  final double probability; /// The probability of the prediction validity
  final int count; /// The number of names in the database
  
  // 2
  factory Genderize.fromJson(Map<String, dynamic> data) {
    final name = data['name'] as String;
    final gender = data['gender'] as String;
    final probability = data['probability'] as double;
    final count = data['count'] as int;

    return Genderize(
      name: name,
      gender: gender,
      probability: probability,
      count: count,
    );
  }
}

// 3
class GenderizeAPI {
  GenderizeAPI([http.Client? client]) : client = client ?? http.Client();

  /// Http client dependency to send network requests.
  final http.Client client;
  
  // 4
  Future<Genderize> send(String name) async {
    final response = await client.get(Uri.parse('https://api.genderize.io?name=$name'));

    if (response.statusCode == 200) {
      // 5
      final json = jsonDecode(response.body) as Map<String, dynamic>;
      return Genderize.fromJson(json);
    } else {
      // 6
      throw Exception('Failed to load gender');
    }
  }
}

Here’s a code breakdown:

  1. You add a model class named Genderize for the API response.
  2. fromJson returns a Genderize model object.
  3. GenderizeAPI Class is a primary wrapper interface.
  4. A future send(String name) method to call an API which returns a Genderize object or throws an exception if the gender fails to load.
  5. You return the Genderize instance in case of success.
  6. Or throw an exception in case of error.

You fulfilled all your goals for TTD. Now, open the terminal and rerun the tests with the following command:

dart test

Press Enter, and run tests:

$ dart test 
00:01 +2: All tests passed!        

It works! Your Dart package is working well and has passed the tests.

Exporting Your Dart Package

After implementing your Dart project, you need to verify that your package is exportable and importable. lib/genderizeio.dart will be the main entry point to export the project.

Go to lib/genderizeio.dart, and check if your file looks like the code below:

library genderizeio;

export 'src/genderizeio_base.dart';

This file defines that all public variables from src/genderizeio_base.dart are visible to anyone who imports your package using import 'package:genderizeio/genderizeio.dart';.

Now it’s time to check the package you created. You’ll use the example app for this.

Go to example/genderizeio_example.dart, and replace its content with:

import 'package:genderizeio/genderizeio.dart';

void main() async {
  final genderize = GenderizeAPI();
  final result = await genderize.send('peter');
  print('${result.name}: ${result.gender}');
}

In the above code, you:

  • Create a GenderizeAPI instance object named genderize. Now the Genderize methods will be accessible.
  • Call the send function from GenderizeAPI, which takes a name parameter and returns a gender object.
  • Print in the console the genderize object name and gender.

If you’ve done the testing part, you’ll realize this is similar to the unit testing part.

Run the example app to make sure the above code is working.

In the terminal, run the following command to run the example app:

dart run example/genderizeio_example.dart

Press Enter, and check the output:

$ dart run example/genderizeio_example.dart
peter: male

You’ll get the above output. The example app is running and you get an output that tells Peter’s gender.