【翻訳記事】Flutterでのアプリ開発を楽しくさせる4つの方法

# はじめに
当記事は「[4 ways Flutter makes mobile app development delightful](https://opensource.com/article/18/6/flutter)」の翻訳記事です。

原著者はGoogleのDart,Flutter開発チームのエンジニアですので、公式による「こういったアプリを作るのがいいよ！」といった開発の指針となる記事かと思います。

Flutterに関しては、公式ドキュメントの充実ぶりも言うに及ばず、
Googleの中の人が積極的に作り方のレシピを公開されているのでありがたいですね。

翻訳の間違い等あれば、ご指摘など頂けると幸いです。

# Flutterでのアプリ開発を楽しくさせる4つの方法
あなたにわたしの秘密をお教えします：何年も前から私はモバイル開発が嫌いでした。私はモバイル開発を好きになりたかったのです。わたしにとって - モバイルとは未来でした！クールでした！低電力でした！これは従来のデスクトップPCとは異なる、デバイスとユーザーとがつながる最初の方法でした。しかし、その開発進捗はは遅く、イライラさせられました。代わりに私はウェブ開発の問題がない領域(entirely problem-free area of web development)に自分自身を隔離し、HTMLタグの消滅を悲しんだものです（笑）。

次に、開発者が同じコードを使ってiOSとAndroid用のモバイルアプリを作成できるようにする、Googleによって開発されたオープンソースのモバイルアプリSDKであるFlutterを発見しました。

Flutterであれば、モバイル開発が楽しくなるかもしれないと思いました。

はい。楽しいです。

どうするの？とあなたは尋ねるでしょう。私はその言葉の意味を示すため、スタックオーバーフローをクエリする非常に単純なFlutterアプリケーションを作成します。誇りあるオープンソースの開発者として、あなたはきっと、あなたの開発したソフトウェアに対する人々の質問をリストの一番上に表示し続けたいことと確信しています。このアプリでは、特定のトピックに関する質問を検索できます。

## Lightning-fast development cycle(イナズマ的速度の開発サイクル)
従来のコンパイラは問題です。そしてコンパイラはこのようです：
あなたは「コンパイル」をヒットします。そして、あなたは、子猫の写真の中には10タブの階層があると知り--やがてランチタイムへと至ります。さいわい、私のウェブ開発中では、インタプリタとジャストインタイム（JIT）コンパイラが私の子猫の傾向から私を救い、その名前を「編集、保存、更新」しました。

Flutterはこの迅速な開発アイデアをさらに進化させ、「編集、保存」します。 Web技術ではありませんが、Flutterのホットリロードのおかげで、モバイルデバイスの画面上で変更を1秒以内で確認できます。

通常、派手で動的に型付けされたスクリプト言語を使用することで、この高速な開発サイクルを得ることができます(コンパイル時にエラーを事前にキャッチするのではなく、実行時にエラーをプッシュするという欠点がありますが)。 2番目の共通の欠点は、コンパイルされた言語ほどのパフォーマンスが得られないことです。
Dartをプログラミング言語として使用することで、Flutterでは上記の問題を回避します。 Dartには強固で健全な型システムがあるので、テストカバレッジでコードベースのその部分をデモを実行する前にそれらの問題を捕捉できます。

次に、Dartには2つのモードがあります。

- Dart仮想マシン上で「インタプリタ」モードで実行されています。これにより、楽しいリロード体験が可能になります。
- コンパイルモードでは、アプリをリリースする準備ができたら、アプリをネイティブマシンコードにコンパイルします。

これらの機能を利用することで、Dartは開発者にFlutterの優れた開発とリリースの経験を提供できます。

最後に、Dartは簡単に学習できるように設計されているので、Java、C ++、JavaScriptなどのCスタイルの言語で作業していれば、使い慣れていると感じるでしょう。

## Cool features, such as Streams and Futures
さあコーディングしましょう！私たちのアプリはStack Overflow APIを使用してFlutterについての質問を探していますので、オープンソースのプロジェクトオーナーが、あなたのコミュニティに情報を提供して助けることができます。 Dartでその情報を取得する最も簡単な方法は、非同期要求です。

```dart:
final url = 'https://api.stackexchange.com/2.2/questions?
order=desc&sort=activity&tagged=flutter&site=stackoverflow';
var result = await http.get(url);
print(result.body);
```

その結果、JSONで以下のように表示されます。

```json
{
  "items": [
    {
      "tags": [
        "android",
        "ios",
        "flutter"
      ],
      "owner": {
        "reputation": 1,
        ...
      },
      is_answered: false,
      "view_count": 1337,
      "title": "How to make a pretty Flutter app?"
...
}
```

このスニペットでは、http.getがFuture-Request-を返しています。これは、結果が将来のtype（Http）Requestとして利用できることを意味します。サーバーの往復を行っていても、コールバックを渡す必要はありません。私たちはawaitキーワードを使って応答を待つだけです。 Flutterには、FutureBuilderウィジェットとStreamBuilderウィジェットがありますが、それはFutureまたはStreamの結果をもとに、対応するUIコンポーネントを作成するものです。

StreamはFutureと似ていますが、一度ではなく複数回非同期で結果を提供できる点が異なります。私たちのアプリでは、興味のあるStack Overflowの質問の更新を聞くことができるStreamを作成します。スタックオーバーフローAPIはボックスからのプッシュ通知を提供しないため、StreamControllerを使用して独自のStreamを構築し、取得したときに更新されたStack Overflow情報を追加します。

```dart
final controller = StreamController<List<String>>();

void refreshQuestions() async {
  var result = await http.get(url);
  Map decoded = json.decode(result.body);
  List items = decoded['items'];
  controller.add(items
      .where((item) => !item['is_answered'])
      .map<String>((item) => item['title'])
      .toList());
}
```
refreshQuestions()では、Stack Overflow APIを新たに呼び出し、結果をフィルタリングして、回答されていない質問だけを探します。その結果から、私たちはアプリの中に表示するための質問タイトルを引き出します。

Flutterは、StreamBuilderウィジェットを提供します。このウィジェットは、Streamのコンテンツに基づいてユーザーがアプリケーション内で見えるものを自動的に更新することができます。この場合、入力Streamソース（controller.stream）を提供し、データの受信に成功したかどうかに応じて異なる結果を表示します（この場合、非常にエキサイティングなテキストウィジェットを構築します）。 StreamBuilderはStreamからの自身のunsbscribeへの注意と、その後にクリーンアップするのに便利です。

```dart
StreamBuilder(
   stream: controller.stream,
   builder: (BuildContext context, AsyncSnapshot<List<String>> snapshot) {
     if (snapshot.hasError)
       return Text('Error ${snapshot.error}');
     else if (snapshot.connectionState == ConnectionState.waiting) {
       return Text('Receiving questions...');
     }
     return Expanded(
         child: ListView(
             children: snapshot.data
                 .map<Widget>((info) => Text(info))
                 .toList()));
   });
```
![flutter1.png](https://qiita-image-store.s3.amazonaws.com/0/190712/44696a90-857b-2fce-0c8a-49d036d20b9c.png)

上記のコードの結果です。

# One technology for both iOS and Android
"Do not Repeat Yourself"はソフトウェアエンジニアリングでの格言ですが、モバイル開発の世界は否定的なようです。あまりに多くの場合、企業は別々のiOSとAndroidアプリのチームをスピンアップするので、それぞれのチームは同じ問題を2回解決する必要があります。 Flutterを使用すると、Dartで記述し、iOSとAndroidの両方にネイティブに展開できます。スクロール動作、システムフォント、その他の基本的な対話コンポーネントは、使用しているプラ​​ットフォームに自動的にデフォルト設定されます。より高いレベルでは、FlutterはCupertinoとMaterial Designのウィジェットライブラリを提供し、ユーザーが自分の選択したプラットフォームで期待する見た目と動作を実現します。

Stack Overflowアプリでは、「Get New Results」ボタンを用意して、私たちの注意を必要とする新しい質問があるかどうかを確認します。 PlatformAdaptiveButtonを記述します。その動作は、実行しているプラ​​ットフォームによって異なります。

```dart
class PlatformAdaptiveButton extends StatelessWidget {
  final Widget child;
  final Widget icon;
  final VoidCallback onPressed;
  PlatformAdaptiveButton({Key key, this.child, this.icon, this.onPressed})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    if (Theme.of(context).platform == TargetPlatform.iOS) {
      return CupertinoButton(
        child: child,
        onPressed: onPressed,
      );
    } else {
      return FloatingActionButton(
        icon: icon,
        onPressed: onPressed,
      );
    }
  }
}
```
Flutterアプリケーションでは、次のように簡単に構築できます。

```dart
return PlatformAdaptiveButton(
   child: const Text('Refresh'),
   icon: const Icon(Icons.refresh),
   onPressed: refreshQuestions);
```
これを押すと、StackOverFlowAPIからの更新が要求されます。 Flutterのロードマップでは、コードにプラットフォーム適応のコンポーネントを組み込むための組み込みの方法が必要なので、調整してください。

他にもいくつかのアプリ開発システムが、クロスプラットフォーム機能を提供しています。ネイティブ、Xamarin、およびIonicです。 React NativeとIonicを使用すると、型の安全性が低下する可能性があるJavaScriptが開発され、実行時に不必要なバグの発生可能性があり、コードが解釈またはJITされます。 Xamarinを使用すると、C＃で強力な型安全性が保証され、ターゲットプラットフォームによっては、コードがネイティブ、JITed、または仮想マシン上でコンパイルされます。 Flutterは、iOSとAndroidの両方でネイティブマシンコードにコンパイルされ、予測可能で高速なパフォーマンスを提供します。

# Customization
"しかしエミリー"と言います。 「代理店で仕事をしていて、作成したすべてのアプリを同じように見せることはできないものです。私は彼らに独特の表情を見せて、スタイリッシュなタッチを加える必要があります。私の代理店の友人を恐れることはありません。Flutterは本当にこの世界で輝いています。

Flutterはすべてのピクセルを画面に描画するので、すべてがカスタマイズ可能です。組み込みのCupertinoButtonがどのように動作しているのか気に入らないのですか？サブクラスを作り、それを自分でデザインします。ソリッドカラーのアプリバーは大変だと思いますか？あなた自身のウィジェットを書いてください。私たちのStack Overflowアプリでは、他の退屈なアプリケーションバーと区別するためにカスタムフォントとグラデーションカラースキームを持つカスタムアプリケーションバーを作成しました。

```dart
@override
Widget build(BuildContext context) {
  final double statusBarHeight = MediaQuery.of(context).padding.top;
  return Container(
   padding: EdgeInsets.only(top: statusBarHeight),
   height: statusBarHeight * 4,
   child: Center(
     child: Text(
       title,
       style: const TextStyle(
           color: Colors.white, fontFamily: 'Kranky', fontSize: 36.0),
     ),
   ),
   decoration: BoxDecoration(
     gradient: LinearGradient(
       colors: [
         Colors.deepOrange,
         Colors.orangeAccent,
       ],
     ),
   ),
  );
}
```
以下が最終結果です。
![flutter3a.png](https://qiita-image-store.s3.amazonaws.com/0/190712/8a9cc3bc-d2ff-ea85-8164-8a02820266f1.png)

この記事で書いたすべてのコードは[GitHub上](https://github.com/efortuna/stack_overflow_viewer)にあります。