![](/style/images/good.png)
![](/style/images/bad.png)
Dart Package Tutorial – Getting Started [FREE]
source link: https://www.raywenderlich.com/34692047-dart-package-tutorial-getting-started
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
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.
Version
![PublishingDartPackages-feature.png](https://koenig-media.raywenderlich.com/uploads/2022/08/PublishingDartPackages-feature.png)
Often, there are features you want to include in your app, but writing the code can be tedious or difficult. So, you hop on to pub.dev and get the package that can help you add that feature, drastically saving your time and energy.
Packages help you extract the code you need so that it can be reused in the same or multiple apps. Packages are also a way you can contribute to the open source community.
Wouldn’t it be great to create your very own package and share it with the community? With this tutorial, you can! Along the way, you’ll learn how to:
- Create your first Dart package.
- Publish your package to pub.dev.
- Import your package into your Flutter app.
Getting Started
Start by clicking the Download Materials button at the top or bottom of the page to download the starter project.
In this tutorial, you’ll use Visual Studio Code, but you can also continue with Android Studio or IntelliJ IDEA. You’ll focus more on the Dart package development and end with integrating your package into your Flutter app (Android/iOS).
In the example, you’ll use Genderize.io, an Open API for predicting gender based on names.
Open your project in Visual Studio Code or Android Studio, and install your dependencies. In the terminal, type:
cd flutter flutter pub get
Press Enter, and check the output:
$ cd flutter $ flutter pub get Running "flutter pub get" in flutter... 2,860ms
Build and run your project.
The app has an input field to enter a name and a button to genderize it. Enter the name Peter and tap Genderize.
As you see, you don’t get a result. This functionality is what you’ll implement later using your newly published Dart package.
Understanding Dart Packages
Dart packages are reusable code published on the Dart package registry. They function as libraries for Dart software development. But there are nuances between Dart packages, specifically those of Flutter as plugins.
Dart Package vs. Flutter Plugin
While both are technically Dart packages, a little nuance differentiates them.
As the name implies, Dart packages are made in pure Dart. You can use Dart packages for both Flutter and server-side Dart. Developing a Dart package is easier than a Flutter plugin because you don’t need to test any platform-specific code. Everything is in Dart!
On the other hand, Flutter plugins are pieces of code that function primarily as part of the mobile app. Flutter plugins usually wrap native Android/iOS code in a Dart package to reuse it within the Flutter app. In this tutorial, you’ll make a Dart package, so your package will be reusable within Flutter or simple Dart scripts.
Knowing When to Create a Dart Package
Flutter has a ton of packages for even the slightest of problems.
For example, if you want to create a native splash, Flutter has a native splash page package ready for you to use. Or, if you want to create launcher images, Flutter has a separate package for that too.
However, when you don’t find a suitable package for your needs, it’s usually because:
- You’re using a brand-new programming language with little community support.
- The problem is too technically expensive to implement — say, creating a new machine learning library.
- It’s a common problem that no one has created a plug-and-play solution for yet.
If you’re experiencing the last issue, you’ve found an excellent opportunity to create a new package and provide a solution to the wider community.
Writing Your First Dart Package
Before writing your Dart package, you must understand the API you’ll use. You’ll write a Dart API wrapper for Genderize.io, which predicts gender based on names. Users can submit names and get an approximate probability of that name’s gender.
The API accepts many parameters, but you’ll only use the name parameter.
Use this API directly to see how it works. Tap the link https://api.genderize.io/?name=peter:
{ "name": "peter", "gender": "male", "probability": 0.99, "count": 165452 }
You see the results of calling this API. Now, you have to create a wrapper around it in Dart.
Creating a Dart Package
It’s finally time to create your first package. Open the terminal in the root of the starter project, and type:
dart create -t package genderizeio
Press Enter, and check the result:
$ dart create -t package genderizeio Creating genderizeio using template package... .gitignore analysis_options.yaml CHANGELOG.md pubspec.yaml README.md example/genderizeio_example.dart lib/genderizeio.dart lib/src/genderizeio_base.dart test/genderizeio_test.dart Running pub get... 1.9s Resolving dependencies... Changed 46 dependencies! Created project genderizeio in genderizeio! To get started, run the following commands: cd genderizeio dart run example/genderizeio_example.dart
You just created the package! This command uses the Dart template and prepares base package files for you. You must fill them with business logic.
The previous command’s output asks you to run a few more commands, so you’ll do that next. In the terminal, type the following commands:
cd genderizeio dart run example/genderizeio_example.dart
Here is what’s going on in the commands above:
- Changed the working directory to your newly created package.
- Run the example project.
Press Enter to execute the commands.
$ dart run example/genderizeio_example.dart awesome: true
You’ve just executed an example project. It has no special code, so you see the simple message awesome: true
. You’ll update this file later to run the Genderizeio package.
Understanding Dart Package Project Structure
The package’s core consists of the following files:
- lib/genderizeio.dart: Main interface file.
- lib/src/genderizeio_base.dart: Core business logic file. Everything under the lib/src folder is your private implementation and shouldn’t be imported by consumers directly. You have to export all public classes in the lib/genderizeio.dart file.
- pubspec.yaml: Dependencies and package metadata file.
- README.md, CHANGELOG.md: Supporting files and documentation.
- example/genderizeio_example.dart: The example that imports the library as if it were a package and tests whether the app is running.
- test/genderizeio_test.dart: For testing the core business logic.
Test-Driven Development of the Dart Package
You’ll use the test-driven development (TTD) process to implement your business logic. It means you must first write your tests. After that, you must write your code so that all the tests pass.
Since the package is an API wrapper, you’ll only do unit tests.
In testing, you will:
- Create a public interface,
GenderizeAPI
, to use the package. - Add the method
Future GenderizeAPI.send(String name) async
to call Genderize.io. - Return the object
Genderize
with the propertygender
in case of success or throw an exception in case of error.
Replace test/genderizeio_test.dart with the following code:
import 'package:genderizeio/genderizeio.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; import 'package:http/http.dart' as http; import 'genderizeio_test.mocks.dart'; @GenerateMocks([http.Client]) void main() { group('Genderize.io', () { // 1 final client = MockClient(); // 2 final genderize = GenderizeAPI(client); test('Peter is male', () async { // 3 when( client.get(Uri.parse('https://api.genderize.io?name=peter')), ).thenAnswer( (_) async => http.Response( '{"name":"peter","gender":"male","probability":0.99,"count":165452}', 200, ), ); // 4 final result = await genderize.send('peter'); // 5 expect(result.gender, 'male'); }); // 6 test('API exception', () async { when( client.get(Uri.parse('https://api.genderize.io?name=')), ).thenAnswer( (_) async => http.Response( '{"error":"Missing \'name\' parameter"}', 500, ), ); final result = genderize.send(''); await expectLater( result, throwsException, ); }); }); }
You’ll see several code issues in the editor. That’s because you haven’t added the dependencies you’ve used. You’ll fix the errors soon by adding the dependencies and generating the mock data.
In the above code, you:
- Create an instance of mock
http.Client
. This class has mocked HTTP functions likeget
andpost
generated bybuild_runner
. - Create an instance of an API wrapper based on a mocked http client.
- Intercepte the network requests to return the mock data in tests.
- Call an API Wrapper with “Peter” as the name parameter.
- Test if Peter is male, with a result of “male”.
- Test to check whether the wrapper returns an exception in case of error.
Next, you’ll start fixing the errors in the above code.
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.
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
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:
- You add a model class named
Genderize
for the API response. -
fromJson
returns a Genderize model object. -
GenderizeAPI Class
is a primary wrapper interface. - 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. - You return the Genderize instance in case of success.
- 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.
Publishing Your Dart Package
Your package is almost ready. It only needs a few finishing touches before it goes live. First, you need to create basic documentation to tell developers what your package does and how they can use it.
For simplicity’s sake, change the package’s metadata name to my_genderizeio
in pubspec.yaml
.
Your pubspec.yaml should look like the screenshot below:
Now you’ll see that your example app and genderizeio_test.dart are throwing errors. This is because you changed the package name and it can’t find the GenderizeAPI
class.
To fix this issue, change the import as follow in both files:
import 'package:my_genderizeio/genderizeio.dart';
Creating the Basic Documentation README.md
The documentation for the package shouldn’t be too long and can be straightforward since the package is basically a REST API wrapper. The Dart template fills in the README.md with some usual sections like Getting started and Usage. Go through the file and fill in the sections with information.
Fill in the Usage section with instructions on how developers can use your package:
# Usage To use the `Genderize.io` package, you can simply import the `GenderizeAPI` class and call the `send` function: ``` final genderize = GenderizeAPI(); final result = await genderize.send('peter'); print('${result.name}: ${result.gender}'); ```
README.md instructions are a very important part of the documentation. They tell developers how a package can help them and how to use it.
Next, edit your pubspec.yaml and add a new repository
section. You can’t publish the library if it isn’t hosted in a public repository, like GitHub:
repository: https://github.com/your/repository
Your pubspec.yaml should look like this:
LICENSE
To successfully publish your package, you must include a license for your library. The license grants permission to other users and states how they can use the package and under whose name the package is registered. If you aren’t familiar with licenses, copy the MIT license and paste it into the LICENSE file.
Generating Package Documentation
Dart has a very helpful tool for generating documentation using forward slashs. You use three forwards slashes before the function ///
and add information to your code. This information will be displayed when the user hovers their cursor over the function.
Comment your lib/src/genderizeio_base.dart to improve the quality of the code. Here’s an example of what it may look like:
Open Terminal and type the following command:
dart doc
Press Enter, and check the output:
$ dart doc Documenting my_genderizeio... Initialized dartdoc with 196 libraries Generating docs for library genderizeio from package:my_genderizeio/genderizeio.dart... no issues found Documented 1 public library in 8.8 seconds Success! Docs generated into genderizeio/doc/api
Check the project folder; it has a new directory named doc.
Open the index file doc/api/index.html in a browser, and navigate to the send
method documentation.
You’ll now see the documentation for the send
method with all comments you added to the code.
Importing Your Package Locally
In general, you add the package into your pubspec.yaml and run flutter pub get
. This command fetches the package from the online repo and adds it to your project.
You may have noticed that you haven’t deployed your package anywhere online. For the project to fetch the package, you must give the package’s path, or where the package is located locally in your machine.
Go to flutter/pubspec.yaml, and import the package locally by adding the path of the adjacent genderizeio folder:
my_genderizeio: path: ../genderizeio
Open Terminal, change the directory to your Flutter project, and run flutter pub get
to verify the installation is correct. Then, check the output:
$ flutter pub get Running "flutter pub get" in flutter... 225ms
Go to flutter/main.dart and replace the _predictGender
method with the following code:
void _predictGender() async { setState(() { _gender = _genderLoading; }); final genderize = GenderizeAPI(); final result = await genderize.send('peter'); setState(() { _gender = 'is probably ${result.gender}'; }); }
Import the genderizeio
package when IDE shows you an error and suggests importing the library.
Build and run the project.
You just used your package in your app!
Publishing a Package Dry Run
Now that you’ve tested your package, it’s ready to go public.
Before you publish your Dart package, do a dry run to test whether everything goes according to plan. Open Terminal, go to the genderizeio folder, and type the following command:
dart pub publish --dry-run
Press Enter, and check the output. Your package should return a message:
$ dart pub publish --dry-run Publishing my_genderizeio 1.0.0 to https://pub.dartlang.org: |-- CHANGELOG.md |-- LICENSE |-- README.md |-- analysis_options.yaml |-- doc | -- api | |-- __404error.html | |-- categories.json | |-- genderizeio | | |-- Genderize | | | |-- Genderize.fromJson.html | | | |-- Genderize.html | | | |-- count.html | | | |-- gender.html | | | |-- name.html | | | |-- probability.html | | |-- Genderize-class.html | | |-- GenderizeAPI | | | |-- GenderizeAPI.html | | | -- send.html | | |-- GenderizeAPI-class.html | | -- genderizeio-library.html | |-- index.html | |-- index.json | |-- static-assets | |-- favicon.png | |-- github.css | |-- highlight.pack.js | |-- play_button.svg | |-- readme.md | |-- script.js | |-- styles.css |-- example | -- genderizeio_example.dart |-- lib | |-- genderizeio.dart | -- src | -- genderizeio_base.dart |-- pubspec.yaml |-- test -- genderizeio_test.dart NullSafetyCompliance.compliant Package has 0 warnings. The server may enforce additional checks.
The response looks good, meaning the package is ready to be published.
If your package is missing some files, like the license, it’ll throw an error. So, it’s important that the above command passes without any errors.
Publishing Dart Packages
Now, it’s time to try publishing it for real! Open Terminal, and type the following command:
dart pub publish
Press Enter, and check the output. If everything goes well, you’ll receive:
Publishing my_genderizeio 1.0.0 to https://pub.dartlang.org: |-- CHANGELOG.md |-- LICENSE |-- README.md |-- analysis_options.yaml |-- example | -- genderizeio_example.dart |-- doc ... |-- lib | |-- genderizeio.dart | |-- src | -- genderizeio_base.dart |-- pubspec.yaml |-- test |-- genderizeio_test.dart NullSafetyCompliance.compliant Package has 0 warnings. The server may enforce additional checks.
Great, you published your first package!
Importing Your Dart Package as Dependencies
To import your package inside your Flutter app, open flutter/pubspec.yaml again and change the dependency to an external one:
my_genderizeio: 1.0.0
Run flutter pub get
, and you’ll be good to go!
Build and run your Flutter project.
Where to Go From Here?
You can download the learning materials using the Download Materials button on the top or bottom of the page to compare your results.
Creating a Dart package from scratch is a fairly basic process, and it’s a good learning step to creating and learning Dart overall.
If you’re interested in exploring Dart some more, check out:
I hope you enjoyed making your first Dart package and found this tutorial helpful. Please join the forum discussion below if you have any questions or comments.
raywenderlich.com Weekly
The raywenderlich.com newsletter is the easiest way to stay up-to-date on everything you need to know as a mobile developer.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK