Flutter - Developing Packages & Plugins (日本語訳)

[Developing Packages & Plugins](https://flutter.io/developing-packages/) の日本語訳

## Package introduction

最小のパッケージは以下を含む:

- pubspec.yaml
- lib ディレクトリ。ここにコードを置く。最低限 <package-name>.dart が必要。

### パッケージの種類
**Dart パッケージ**: Dart で書かれたパッケージ。いくつかのものは Flutter 固有の機能を持ち、したがって Flutter フレームワークに依存し Flutter のためにのみ使用できる。例 https://pub.dartlang.org/packages/fluro 

**プラグインパッケージ**: Dart で書かれた特別なパッケージで、Android (using Java or Kotlin) や iOS (using ObjC or Swift) などプラットフォーム固有の実装を含む。例 https://pub.dartlang.org/packages/battery 

## Developing Dart packages

### Step 1: Create the package

Dart パッケージを作るには `--template=package` フラグとともに `flutter create` コマンドを実行する:

    $ flutter create --template=package hello

以下を含む `hello/` ディレクトリが作られる。

- live/hello.dart: パッケージの Dart コード
- test/hello_test.dart: パッケージのユニットテスト

### Step 2: Implement the package

純粋な Dart パッケージは `lib/<package name>.dart` に機能を実装するか、`lib` ディレクトリ以下のいくつかのファイルに実装する。パッケージをテストするために `test` ディレクトリにユニットテストを追加する。パッケージの構造の詳細については次を参照 [Dart library package](https://www.dartlang.org/guides/libraries/create-library-packages)。

## Developing plugin packages

もしプラットフォーム固有の機能を呼び出す必要があるならプラグインパッケージを作る必要がある。プラグインパッケージは Dart パッケージ の特別なバージョンで、先の説明にもあったように Android (using Java or Kotlin) や iOS (using ObjC or Swift) などプラットフォーム固有の実装を含む。その API は `platform channels` を使ってプラットフォーム固有の実装と連携する。 https://flutter.io/platform-channels/

### Step 1: Create the package

プラグインパッケージを作るには `--template=plugin` フラグとともに `flutter create` コマンドを実行する。`--org` オプションを使ってあなたの組織を逆ドメイン名表記で指定する。この値は様々なパッケージや自動生成された Android と iOS のコードに含まれる bundle identifiers に使われる。

    $ flutter create --org com.example --template=plugin hello

以下のを含む `hello/` ディレクトリが作られる:

- live/hello.dart: プラグインの Dart API
- android/src/main/java/com/example/​hello/HelloPlugin.java: Android 用のプラグインAPIの実装
- ios/Classes/HelloPlugin.m: iOS 用のプラグインAPIの実装
- example/: プラグインに依存するサンプルアプリ

デフォルトではプラグインは Objective-C と Java をそれぞれ iOS、Android のために使用する。必要に応じて `-i` オプションと `-a` オプションで Swift や Kotlin を指定することができる:

    $ flutter create --template=plugin -i swift -a kotlin hello

### Step 2: Implement the package

プラグインパッケージは異なる言語で書かれたプラットフォーム固有のコードをもつが、使いやすくするためにいくつかの固有のステップが必要になる。

### Step 2a: プラグインパッケージ API (.dart) を定義

プラグインパッケージの API は Dart コードで定義される。`hello/` ディレクトリを開いて `lib/helllo.dart` を作る。

### Step 2b: Android プラットフォームコード (.java/.kt) を追加

Android プラットフォームコードを Android Studio で編集する前にコードを一度ビルドする。（IDE から実行するか、次のようにして `cd hello/example; flutter build apk`）

次に

- Android Studio を起動
- `Welcome to Android Studio` ダイアログで `import project` を選択、もしくはメニューから `File > New > Import Project…` を選択し、`hello/example/android/build.gradle` を選択する
- `Gradle Sync` ダイアログで `OK` を選択
- `Android Gradle Plugin Update` ダイアログで `Don’t remind me again for this project` を選択

Android プラットフォームコードは `hello/java/com.example.hello/​HelloPlugin` に置かれる。▶︎ボタンを押してアプリを実行することができる。

Step 2c: iOS プラットフォームコード (.h+.m/.swift) を追加

Xcode で iOS プラットフォームコードを編集する前にコードを一度ビルドする（IDE から実行するか、次のようにして `cd hello/example; flutter build ios --no-codesign`）

次に

- Xcode を起動
- `File > Open` を選択し、`hello/example/ios/Runner.xcworkspace` を選択する

iOS プラットフォームコードはプロジェクトナビゲーター上で `Pods/Development`、` Pods/hello/Classes/` に置かれる。▶︎ボタンを押してアプリを実行することができる。

### Step 2d: API とプラットフォームコードの接続

最終的に、Dart コードで書かれた API とプラットフォーム固有の実装を接続する必要がある。そのために platform channels を使う https://flutter.io/platform-channels/ 

## ドキュメントの追加

全てのパッケージは以下のドキュメントを持つことが望ましい:

- README.md - パッケージの説明
- CHANGELOG.md - それぞれのバージョン毎の変更
- LICENSE - パッケージのライセンス
- 全てのパブリックAPI についての API ドキュメント（詳細は以下を参照）

### API ドキュメント

プラグインパッケージを公開すると API ドキュメントが自動で生成され、 dartdocs.org に公開される。例 https://pub.dartlang.org/documentation/device_info/0.0.4/index.html 

API ドキュメントをローカルマシンで生成したい場合は以下のようにする:
1. パッケージディレクトリに移動 cd ~/dev/mypackage
2. ドキュメンテーションツールに Flutter SDK の場所を伝える `export Flutter_ROOT=~/dev/flutter` (macOS or Linux),  `set Flutter_ROOT=~/dev/flutter` (Windows)
3. dartdoc を実行（dartdoc は Flutter SDK に含まれる） `$FLUTTER_ROOT/bin/cache/dart-sdk/bin/dartdoc` (macOS or Linux) `%FLUTTER_ROOT%\bin\cache\dart-sdk\bin\dartdoc` (Windows)

API ドキュメントを書く為の Tips についてはこちらを参照 https://www.dartlang.org/guides/language/effective-dart/documentation 

## パッケージの公開

パッケージを実装したら、他の開発者が見つけやすいように [Pub](https://pub.dartlang.org/) に公開することができる。
公開する前に pubspec.yaml, README.md, CHANGELOG.md の内容が正しいかレビューする。

次に、dry-run コマンドを実行して全ての analysis をパスすることを確認する:

    $ flutter packages pub publish --dry-run

最後に、実際のパブリッシュコマンドを実行する:

    $ flutter packages pub publish

パッケージの公開について詳細はこちらを参照 https://www.dartlang.org/tools/pub/publishing 

## パッケージの依存関係の取り扱い

もし hello というパッケージを作っていて、それが他のパッケージの Dart API に依存している場合、 pubspec.yaml ファイルの dependencies セクションにそのパッケージを追加する必要がある。以下のコードは url_launcher プラグインの Dart API を hello で利用可能にする:

hello/pubspec.yaml

```yaml
dependencies:
  url_launcher: ^0.4.2
```

これで `import 'package:url_launcher/url_launcher.dart'` して `launch(someUrl)` を実行できるようになる。これは Flutter アプリや Dart プロジェクトでパッケージを含めるのとなんら変わらない。しかし、もし hello が url_launcher が公開しているプラットフォーム固有の処理にアクセスする場合、プラットフォーム固有のビルドファイルに適切な依存関係を追加する必要がある。

### Android

hello/android/build.gradle:

```
android {
    // lines skipped
    dependencies {
        provided rootProject.findProject(":url_launcher")
    }
}
```

これで `import io.flutter.plugins.urllauncher.UrlLauncherPlugin` して `UrlLauncherPlugin` に `hello/android/src` 以下のコードからアクセスできる。

### iOS

hello/ios/hello.podspec:

```
Pod::Spec.new do |s|
  # lines skipped
  s.dependency 'url_launcher'
```

これで `#import "UrlLauncherPlugin.h"` して `UrlLauncherPlugin` クラスに `hello/ios/Classes` 以下のクラスからアクセスできるようになる。

### コンフリクトの解消

次のようなケースを想像してください。あなたは `some_package` と `other_package` をあなたの hello パッケージで使いたい。そして両方とも url_launcher に依存しているが別のバージョンである。このときコンフリクトが起こりうる。最前の解決策として、依存関係を指定するときに特定のバージョンを指定するのではなく version ranges を指定する。 https://www.dartlang.org/tools/pub/dependencies#version-constraints 

```
dependencies:
  url_launcher: ^0.4.2    # Good, any 0.4.x with x >= 2 will do.
  image_picker: '0.1.1'   # Not so good, only 0.1.1 will do.
```

もし some_package が上のような依存バージョンを指定していて、 other_package が url_launcher の '0.4.5' や '^0.4.0' を指定していた場合、`pub` はその依存関係を解決することができる。Grade module や Cocoa pods においても似たような方法をとることができる。

たとえ some_package と other_package が url_launcher について非互換なバージョンを指定していたとしても、互換性のある方法で url_launcher を使うことができる。この場合、コンフリクトは hello の pubspec.yaml に dependency override 追加して特定のバージョンを強制することで対処される。

hello/pubspec.yaml で url_launcher  '0.4.3' を強制する:

```
dependencies:
  some_package:
  other_package:
dependency_overrides:
  url_launcher: '0.4.3'
```

もしコンフリクトがパッケージ自体によるものではなく、Android の特定のライブラリによるものであった場合、Gradle ビルドロジックに代わりに dependency override を追加する必要がある。

hello/android/build.gradle で guava '23.0' を強制する:

```
configurations.all {
    resolutionStrategy {
        force 'com.google.guava:guava:23.0-android'
    }
}
```

Cocoapods は現在 dependency override 機能を提供していない。