こんにちは、教育系エンジニアのひらまつ(@hiramatsuu)です。
書籍「ゼロからわかる Linuxコマンド200本ノック(技術評論社)」の著者。Udemy受講者8万人。
プログラミング教育をメインに活動するエンジニアとして、動画教材の作成・技術書の執筆・学習アプリの開発などを行なっています(詳しくはこちら)。
本記事では、Flutter開発における用語、パッケージ・ライブラリ・プラグインについて解説します。
これらの用語は、違いがわかりづらく、混同しやすいので、本記事を読んで、正確に理解できるようになりましょう。
簡単なまとめ
- ライブラリ(library)は、Dartファイルのこと。基本的には、1つのDartファイルが、1つのライブラリになるが、2つ以上のDartファイルが1つのライブラリになることもある。
- パッケージ(package)は、ライブラリを他者に共有するために使われる配布形式。パッケージのlibディレクトリ内に、ライブラリが格納されている。
- プラグイン(plugin)は、パッケージの一種で、プラットフォーム固有の機能を使うことができるもの。プラグインの中には、KotlinやSwiftなど、プラットフォーム固有の機能にアクセスするための、コードが含まれる。
ライブラリ(library)とは
ライブラリ(library)は、Dartファイルのこと。基本的には、1つのDartファイルが、1つのライブラリになるが、2つ以上のDartファイルが1つのライブラリになることもある。
ライブラリのスコープ
ライブラリは、コンパイルの単位で、ライブラリごとにプライベートなスコープを持つ。そのため、importせずに、ライブラリの外から、機能にアクセスすることはできない。
例えば、lib1.dartとlib2.dartという、2つのライブラリがあるとする。以下のように、lib2.dartで、lib1.dartの機能を使おうとしても、エラーになる。
lib1.dart
void hoge() {
print("Hello!");
}
lib2.dart
void foo() {
hoge(); // lib1.dartの関数なのでエラーになる
}
別のライブラリの機能を使うには、import(もしくはexport)が必要。上記2つのファイルが、同じディレクトリにある場合、相対パスで次のようにimportする。
lib2.dart
import 'lib1.dart';
void foo() {
hoge(); // importしているので、lib1.dartの関数が使える
}
複数ファイルを1つのライブラリにする(非推奨)
基本的には、1つのDartファイルが、1つのライブラリになるが、「part」「part of」ディレクティブを使うことで、複数のDartファイルを1つのライブラリとして扱うこともできる。
例えば、以下のようにすると、lib1.dartとlib2.dartを1つのライブラリにすることができる。
lib1.dart
// lib2.dartがこのライブラリの一部であることを宣言
part 'lib2.dart';
void hoge() {
print("Hello!");
}
lib2.dart
// このファイルがlib1.dartの一部であることを宣言
part of 'lib1.dart';
void foo() {
hoge(); // 同一ライブラリなので、importせずにhoge()を使用可能
}
ただし、partとpart ofは、すでに非推奨になっていることに注意。複数ファイルを同一のライブラリにするのではなく、importやexportを使って、別のライブラリの機能にアクセスすることが推奨されている。
ライブラリの配布方法
ライブラリは、基本的にはパッケージという形式で配布されるが、Dart SDKに含まれているライブラリもある。Dart SDKの中に含まれるライブラリを、Dart’s core librariesといい、dart:coreやdart:asyncなどがある。Dart’s core librariesは、明示的にimportせずとも使用することができる。
例えば、print()は、dart:coreに含まれる関数。
lib1.dart
void hoge() {
print("Hello!"); // dart:coreライブラリの機能なので、import不要で使える
}
パッケージ(package)とは
パッケージ(package)とは、ライブラリなどを、他者に共有するための配布形式。1パッケージによって共有されるのは、ライブラリだけではなく、コマンドラインツールなども含まれる。
パッケージのディレクトリ構成
パッケージの実体は、1つのディレクトリであり、すべてのパッケージは、pubspec.yamlファイルをルートに持つ。pubspec.yamlについて、より詳しくはこちらの記事を参照。
パッケージのディレクトリ内には、pubspec.yamlの他に、ライブラリを格納するlibディレクトリや、公開するコマンドラインツールを格納するbinディレクトリなどがある。パッケージのディレクトリ構成について、より詳しくはこちらの記事を参照。
パッケージの、libディレクトリの直下に入っているのが、ユーザーにAPIが公開されるライブラリ。APIが公開されない、実装を担うライブラリは、lib/srcディレクトリに入れる慣例がある。
メインライブラリ(main library)とミニライブラリ(mini library)
パッケージのlibディレクトリ直下には、パッケージ名と同名のDartファイルがある(fooパッケージなら、lib/foo.dartファイルを持つ)。このライブラリは、メインライブラリ(main library)と呼ばれることもある。
多くのメインライブラリでは、lib/src内にある、実装を担うライブラリを、exportしている。exportディレクティブは、指定したライブラリのAPIを公開して、exportディレクティブが書かれたファイルをimportするだけで、exportしたライブラリのAPIすべてを使えるようにするための機能。
例えば、実在するshelfパッケージのメインライブラリ(lib/shelf.dart)は、次のような実装になっている。
lib/shelf.dart
export 'src/cascade.dart' show Cascade;
export 'src/handler.dart' show Handler;
export 'src/hijack_exception.dart' show HijackException;
export 'src/middleware.dart' show Middleware, createMiddleware;
export 'src/middleware/add_chunked_encoding.dart' show addChunkedEncoding;
export 'src/middleware/logger.dart' show logRequests;
export 'src/middleware_extensions.dart' show MiddlewareExtensions;
export 'src/pipeline.dart' show Pipeline;
export 'src/request.dart' show Request;
export 'src/response.dart' show Response;
export 'src/server.dart' show Server;
export 'src/server_handler.dart' show ServerHandler;
exportに加えて「show クラス名/関数名」とすることで、ライブラリに含まれるすべてのAPIを公開する代わりに、ここで指定したAPIだけを公開することができる。なので「export ‘src/cascade.dart’ show Cascade;」では、「cascade.dartのうち、CascadeクラスのみのAPIを公開する」という意味になる。
このようにすることで、shelfパッケージのユーザーは、
import 'package:shelf/shelf.dart';
というように、shelf.dartをimportするだけで、shalfパッケージのlib/srcディレクトリに入っている、ライブラリの機能もを使えるようになる。
このように、実装を担うライブラリを、メインライブラリ内でexportすることによって、ユーザーはメインライブラリだけをimportするだけで、パッケージの多くの機能を使えるようになる。
ライブラリは、それぞれを小さく作る方が、保守しやすい。そのため、1つの大きなライブラリを作るのではなく、exportやimportを使って、複数の小さなライブラリ(mini libraries)を作ることが、推奨されている。
プラグイン(plugin)とは
プラグイン(plugin)は、パッケージの一種で、プラットフォーム固有の機能を使うことができるもの。正式名称は「プラグインパッケージ」だが、「プラグイン」と基本的に呼ばれる。
プラグインの中には、KotlinやSwiftなど、プラットフォーム固有の機能にアクセスするための、コードが含まれる。例えば、デバイスのカメラを使うには、cameraプラグインが必要。
プラグインを新たに利用するためには、Hot ReloadやHot Restartではなく、Full Restartする必要がある。これは、Hot ReloadとHot Restartは、Dartコードのアップデートしかしないため。
補足
libraryディレクティブ
libraryディレクティブは、ライブラリレベルのdoc commentsやmetadata annotationを指定する用途で使う。2
例えば、asyncパッケージのlib/async.dartの上部には、次のようなdoc commentsの記述がある。
lib/async.dart
/// Utilities that expand on the asynchronous features of the `dart:async`
/// library.
///
/// {@youtube 560 315 https://www.youtube.com/watch?v=r0tHiCjW2w0}
library async;
また、libraryディレクティブの有無によらず、あらゆるDartファイルはライブラリであることに注意。ライブラリレベルのdoc commentsを記載しないなら、libraryディレクティブは不要。
アプリケーションパッケージ
パッケージの種類の一つとして、アプリケーションパッケージもある。
アプリケーションパッケージ(application package)とは、Flutterアプリのような、他のパッケージから依存されないパッケージのこと。アプリケーションパッケージは、エンドユーザーから直接利用される。
アプリケーションパッケージのlockfileは、バージョン管理が必要。より詳しくはこちらの記事を参照。
その他の情報
Flutter開発についての、他の記事はこちらを参照。