Fluttering Dart
Fluttering Dart: Libraries and Packages
How to use, build, reuse and share your code

If you’ll ever need to structure your project based on modularity, libraries will come to rescue.
They allow you to split some piece of code or module into multiple files and share with fellow developers.
Another benefit of libraries, next to code structuring, is that they allow for library encapsulation (determining what is visible or not to other libraries).
Dart libraries
To define a library, all that’s needed is to create a Dart file with some code inside of it.
To import it, we can add the import path_to_your_library; statement at the beginning of the file.
When importing, we can use the show and hide keywords for importing only some identifiers that we will use in our code, or respectively, excluding the ones we are sure we don’t want to import.
For example, to use only the Point class from the Dart math core library we’ll write:
import 'dart:math' show Point;Or to use all other classes except the Point class we’ll write:
import 'dart:math' hide Point;We also have to use the as keyword when we want to import libraries that define identical identifiers. This way we avoid potential conflicts.
import 'dart:math' as Math;That’s all!
Ways of importing libraries
To import libraries we have multiple options:
- relative path to file — used when the libraries are in the same folder
- absolute path to file — by adding
file://URI prefix we can reference a library file that is on the computer - a web URL — the same thing as in the case of the absolute path, but using the
http://protocol - a package — this is the most popular and the recommended option to import libraries; we specify the path from the
packageroot to the library file
Creating libraries
Dart libraries can be composed of a single file (most common and the recommended way) or multiple files.
We get, as expected, the optional library keyword to define a library. This is useful when creating libraries spread into multiple files or for creating documentation for libraries before publishing them.
Small libraries consist of a single file. The library definition can be split into multiple files but is not advised if not necessary. Splitting has implications on encapsulation and the way the library itself will be imported and used.
In Dart, everything is, by default, public. To mark something private we use the _ (underscore) character. We can also use the @protected annotation provided by the meta Dart package. When added to a class member, it indicates the member should be used only inside the class or its subtypes.
Both ways (either _ or @protected) set the marked member as private and being private prevents its outside access.
When defining single file libraries we just write all code inside one file and that’s all.
When defining multiple-files libraries, we can make use of the combined part, part of, and library statements:
part— allows a library to mention the library parts it is composed ofpart of— used in the library part to mention which library it’s part oflibrary— precedes thepartstatements, to link the parts to themainpart of a library
A better approach is to avoid altogether using the above statements for composition and just split the library into small individual libraries. After that use the export statement to create the composition inside the main of the library. This way the library users need to import a single file and all the smaller libraries exported inside it will also be available.
Dart packages
Every Dart project has a corresponding Dart package.
The main benefit of working with packages is that the code inside them can be reused and shared. Sending and pulling dependencies to the pub.dartlang.org website and repository is done using the pub tool.
Using a library package in our project makes that package and immediate dependency. This dependency may have its own dependencies and those are called transitive dependencies.
In Dart there are 2 types of packages:
- application packages
- library packages
Applications are themselves packages. These packages are not meant to be dependencies in other packages.
Library packages are the packages meant to be reused and shared. These can be used as dependencies.
The recommended structure of these types of packages is almost the same. Usage and purpose are what differ.

Let’s go through each of the above:











As we’ll see in the future, a Flutter project structure is similar to that of a Dart package.
The pubspec.yaml file
This is the core of a Dart package. Understanding this file structure is crucial for describing a package.
The file is using the YAML syntax. This format is usually used for configuration files and has a readable structure.
name: fluttering_dart
description: A starting point for Dart libraries or applications.
version: 1.0.0
homepage: https://www.constanting.com
author: Constantin Stan <[email protected]>environment:
sdk: '>=2.7.0 <3.0.0'dependencies:
path: ^1.6.0dev_dependencies:
pedantic: ^1.8.0
test: ^1.6.0This file provides the package metadata info, useful when we want to publish the package, defines third party dependencies and Dart SDK version.
Let’s examine the pubspec components:
name— the identifier of the package which is required and should contain only lowercase letters, digits, and the_char; it also should be a valid Dart identifier (doesn’t start with digits and is not a reserved word)description— an optional component (required only if publishing the package) used to describe the purpose of the packageversion— also optional if not publishing to thepubrepository and useful to track the current package versionhomepage— optional, but important, as it will be linked on package’s page once publishedauthor— also optional, but important for providing contact info; if there’s more than 1 author, then theauthorsshould be provided using the YAML list syntaxenvironment— specifies the main dependency of any Dart package, and that is the Dart SDK; using this we mention the target and the supported SDK versionsdependenciesanddev_dependencies— these define the required third-party packages for usage and development of the library
The above components are the most common found in a pubspec file. For a complete overview and other components, you can read further here.
Dependencies
The most important role of the pubspec.yaml file is that of adding dependencies.
After starting our project, using a generator tool like Stagehand or manually, running the pub get command is the first thing to do.
Let’s consider the following pubspec :
name: fluttering_dartRunning the pub get command against it will provide us with the following output:
Resolving dependencies...
Got dependencies!It will also generate 2 new files:
.packages— as mentioned previously, this maps the dependencies in the system’s pub cache; after packages are mapped, they can be imported and used in our Dart code; the file should be excluded from code management systems like Gitpubspec.lock— this file contains all dependency graphs of the package (all direct and transitive dependencies and their exact versions and extra metadata); this file should be included in the versioning systems, as it helps to replicate the exact environment
The above files are generated automatically by the pub tool and it is not advised to edit them manually.
In the pubspec.yaml example we’ve added the path package dependency.
We specify dependencies using the dependencies from the pubspec file. The syntax for doing that is:
<package>: <constraints>The dependency should be specified using the package name and constraints (version and source). If no constraints are provided, then any available version of the package will be used (the latest one) and the default source will be pub.dartlang.org (which redirects to pub.dev)
The version constraint has multiple ways in which it can be mentioned:
- any/empty —
path:orpath: any - concrete version —
path: 1.6.0 - minimal bound —
path: '>1.6.0'orpath: '>=1.6.0' - maximal bound —
path: '<1.6.0'orpath: '<=1.6.0' - range —
path: '>=1.6.0 <=1.6.4' - semantic range —
path: ^1.6.0which is similar topath: '>=1.6.0 <2.0.0'(minimal specified version to an no-longer compatible version)
The source constraint also has multiple ways to be specified:
- the hosted source:
dependencies:
path:
hosted:
name: path
url: https://private-pub-api-compliant-server.com- the path source (this will get a bit confusing because of our example package name):
dependencies:
path: # package is on this line
path: /some/path/to/path- the Git source (specify a package from a Git repository):
dependencies:
path:
git:
url: git://github.com/dart-lang/path.git
path: /
ref: master- the SDK source:
dependencies:
flutter_driver:
sdk: flutter
version: ^0.0.1The last method is only used for Flutter SDK dependencies.
Third-party dependencies are crucial when it comes to increasing productivity.
Flutter projects can use both platform-specific and cross-platform code. The latter is written in Dart, and, for building Flutter apps, some basic knowledge of Dart is required.
Fluttering Dart’s goal is to explore fundamental knowledge and unveil tips & tricks of the powerful programming language that brings Flutter to life.
In the previous parts of the series, we went through the Dart built-in data types, functions, operators, control flow statements, object-orientated programming (classes, objects and more), and asynchronous programming.
In this part, we took a deep dive into Dart libraries and packages.
In the next part of the Fluttering Dart series, we’ll delve into unit testing.
Tha(nk|t’)s all!
