Flutter Community
Published in

Flutter Community

Deep Dive Into The Pubspec.yaml File

Photo by John Barkiple on Unsplash

Whether we are starting a new Flutter project for web, mobile or desktop, there are two thing that will be common in all projects: the pubspec.yaml and pubspec.lock files. In this article we will explore the pubspec.yaml file - what it is and what we can do with it.

The Pubspec.yaml File

When we create a new Flutter Project, we usually have the following file tree:

But what is exactly inside the pubspec.yaml file? Let's examine it:

As we can see, the pubspec.yaml file is divided into different sections, let's dive deep into each one.

General information — The Metadata

At the top of the file we see the name. This field dictates the package name, and also how we will import files inside this project or from this project. For example, if in one file we want to import the main.dart function, the import will be as follows:

However, if in the future we change the name field to to weather_app_prototype, we have to make sure to change all of our imports from fantastic_app to weather_app_prototype, which means that our main.dart import would look like the following:

The description field, as the name implies, is an optional field that let us add a small description to our project. If we are creating a library, this description is what will be visible to everyone if we decide to publish our package on pub.dev, for example for shared_preferences, we have the following description: "Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android." and, if we search for "shared_preferences" at pub.dev, we can see the description in the list of results:

Shared Preferences search result

The next parameter, version will allow us to add semantic versioning to the application or library. In the case of a mobile application, this is going to be the version that appears in the Google Play Store and iOS App Store. A Flutter app with version 1.2.3+4 means that it's the version 1.2.3 with build version 4.

On the other hand, if we are creating a library, the versioning will give any user the ability to specify which version of the library they want to use, letting them decide whether to use shared_preferences: 0.5.12+2 or shared_preferences: 0.1.0.

If we are creating a library project, there are additional optional fields that allows us to give more information about our package, specifically the author, homepage, issue_tracker and repository:

These fields will also be shown in pub.dev in the Metadata section:

Metadata for the bloc package

Environment

The next section will hold the environment fields. This field allows us to add constraints to both the Dart SDK and the Flutter Version, which means that when we use:

We are stipulating that this app or library will only run on Dart SDK versions higher or equal than 2.7.0 but lower than 3.0.0. We do not care what specific version it is currently using, only that it stays between these bounds.

Moreover, we can also stipulate what Flutter version we are going to use, using the flutter parameter:

In this case, we only allow our app to run if we are using a Flutter version of 1.22.0. If we try to get our packages, through flutter pub get using any other Flutter version we will see the following error:

By using these fields, we can even specify if we are using experimental features, such as sound null safety. In this case, we can set the Dart SDK constraints to the following:

Dependencies

The next two sections: dependencies and dev_dependencies contain all the packages that we are going to use in the application.

When declaring a dependency to a specific package, we must first know the different ways there are to do add them:

  • Dependency from a package hosted on pub.dev
  • Dependency from a path directory
  • Dependency from a git repository
  • Dependency from a custom pub server

The first one is the most commonly used. We go to pub.dev, select a package, such as bloc and go to the install section to see how we can add it to our project — by adding the dependency directly in the pubspec.yaml file in the dependencies section:

However, imagine the following scenario: we find a new issue with the bloc package and decide to clone the repository and fix it ourselves. We solve the issue locally, but then want to verify if the fix behaves correctly by testing it with our app.

In order to test it, we will need to compile the local bloc version with our app, and to do it we have to use the path reference. If for example our fix is situated in a directory called bloc_fixes_issue_110 in the same folder as the parent project, we can reference it by using ../bloc_fixes_issue_110

The bloc dependency will have to be changed to the following:

After testing it locally, we decide to contribute to the package by creating forking the project and creating a pull request with the branch fix/issue_110. While we wait for the fix to be merged, other people can use our fix by referencing the dependency by git reference as follows:

  • The url is the git reference for the repository
  • The ref represent a git reference, which can be a commit hash, tag or branch.
  • The path is to be used if the git repository has several packages. If we look at official github repository, we see that inside the packages are all the bloc-related projects, including bloc and flutter_bloc.

Finally, if we decide to have our own pub server (see the unpub repository), we have to let pubspec know that we are using a different pub server. This can easily be done by adding the hosted argument:

Now that we know how to add dependencies to our project, a question remains — what is the difference between dependencies and dev_dependencies?

The dependencies sections have all the dependencies needed to compile our application or library. So, if our app uses bloc, we need to add it to the dependencies section.

On the other hand the dev_dependencies are dependencies that our app require only on development time, such as test frameworks or libraries that generate code. This means that although we added tests to our app with the package, it is not needed to compile the app, since it is only needed when we run tests. Furthermore, if we are using the built_value package that uses generated code using the build_runner package, we are certain that we only generate code when developing, so our compiled application or library does not need a dependency from build_runner in order to work, which is why we leave it only on dev_dependencies.

Dependency Overrides

Imagine the following scenario: we are using the bloc package with version 6.0.0, but at the same time we have a dependency on another package that uses version 5.0.0. If we try to get the dependencies for the app, we get the following error:

If we cannot increase the version of the library, or just want to test the application, how can we do it? By overriding the dependency using dependency_overrides:

In summary: our package is using flutter_bloc version 5.0.0 and our app is using version 6.0.0. However, we need to run our app with version 6.0.6, so we override the dependency. By doing this, when pubspec compiles a list of all dependencies, it will use version 6.0.6, no matter what each app or library stipulates:

When getting the packages with this new field, we see a new warning message saying that we are using an overriden dependency:

However, a word of caution. The dependency_overrides will only be used in the application we are compiling. This means that if we have a dependency_overrides section on our package, and we compile the example app, the example app will ignore the dependency overrides of all other libraries.

Flutter section

At the bottom of our file, we see a section called flutter. When creating a new project we see that it already has a parameter: uses-material-design:

This is where we have all the Flutter-specific configurations, such as assets and fonts:

This section is also used to set each platform’s version of a plugin, when creating a plugin package. Take as an example the connectivity package which stipulates different platform-specific code for android, ios, macos and web:

Connectivity Plugin pubspec.yaml flutter section

Conclusion

The pubspec.yaml file is transversal to all apps and packages - it is where we add metadata to our project, stipulate the Dart and Flutter SDK constraints, manage the dependencies and also set Flutter-specific configurations. To see a list of all the fields we can use, we can go to the official documentation for pubspec - The pubspec file.

However, one thing that we did not see is what happens after we get packages (via flutter pub get for example). Specifically what happens with the pubspec.lock file. This is going to be the topic of a future article where, among other things, we will see what is the ^ symbol that we have when declaring dependencies from pub.dev and why we should or should not use it.

https://twitter.com/gonpalma

https://www.twitter.com/FlutterComm

--

--

--

Articles and Stories from the Flutter Community

Recommended from Medium

Boot camp day3

Quick Blog #6 27–02–2019

Process vs Thread — [Notes]

Minting Your Own ZRC-2 Tokens in Zilliqa

Every December I Become a Better Programmer and You Could Too

Interview with Tom Moor: Founder, Outline

Cover art for The Craft of Open Source

Mixing and Matching Guidelines

5 Things a Developer should always do

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Gonçalo Palma

Gonçalo Palma

Flutter Lead Developer @Pvotal Tech. Organizer @ Flutter Portugal and collaborator @FlutterExp. https://gpalma.pt/

More from Medium

Basic and advanced networking in Dart and Flutter — the Tide way. Part 4: HTTP client. Advanced.

Why is Flutter Fast? — Part 1: Sublinear Building

An Introduction to Flutter’s World

Velocity and Gravity in Flutter Games with Flame