2022/5/23/Flutter/Adaptive Apps in Flutterの訳パート1



Adaptive Apps in Flutter

1. Introduction

In this codelab, you’ll learn how to build a Flutter app that adapts to the platform that it is running on, be that Android, iOS, the web, Windows, macOS, or Linux.


What you’ll learn

  • How to grow a Flutter app designed for mobile to work on all six platforms supported by Flutter.



  • The different Flutter APIs for detecting the platform and when to use each API.

プラットフォームを検出するためのFlutter APIの種類と、各APIを使用するタイミングについて説明します。


  • Adapting to the restrictions and expectations of running an app on the web.



  • How to use different packages alongside each other to support the full range of Flutter’s platforms.



What you’ll build



In this codelab, you’ll initially build a Flutter app for Android and iOS that explores Flutter’s YouTube playlists.



You will then adapt this application to work on the three desktop platforms (Windows, macOS, and Linux) by modifying how information is displayed given the size of the application window.



Then you will adapt the application for the web by making text displayed in the app selectable, as web users expect.



Finally, you will add authentication to the app so you can explore your own Playlists, as opposed to the ones created by the Flutter team, which requires different approaches to authentication for Android, iOS, and the web, versus the three desktop platforms Windows, macOS, and Linux.


Here is a screenshot of the Flutter app on Android and iOS:


This codelab focuses on transforming a mobile Flutter app into an adaptive app that works across all six Flutter platforms. Non-relevant concepts and code blocks are glossed over, and are provided for you to simply copy and paste.


2. Set up your Flutter environment

This codelab covers developing Flutter apps across multiple operating systems. While one can reach four of the six target platforms from a macOS laptop or desktop (Android, iOS, web, and macOS), to reach all six will require multiple systems. Developers on all development platforms should be able to develop for Android, the web, and the desktop operating system you are developing on. This is enough to cover all of the lessons of this codelab.



You must install software to compile for various targets. For example, you need access to Xcode tooling for iOS and macOS development, Android Studio to install Android tooling, Visual Studio for Win32 compilation tooling, and so forth. For details, please refer to Flutter’s getting started documentation, then follow that with flutter.dev/desktop.

様々なターゲットにコンパイルするためのソフトウェアをインストールする必要があります。例えば、iOSやmacOSの開発にはXcodeのツール、AndroidのツールをインストールするにはAndroid Studio、Win32のコンパイルツールにはVisual Studio、といった具合にアクセスする必要があります。詳しくはFlutterのgeting startedドキュメントを参照し、それに沿ってflutter.dev/desktopをインストールしてください。

3. Get started



Creating a Flutter project

An easy way to get started writing Flutter for desktop apps is to use the Flutter command-line tool to create a Flutter project. Alternatively, your IDE may provide a workflow for creating a Flutter project through its UI.



To make sure everything is working, run the boilerplate Flutter application as a mobile app as shown below. Alternatively, open this project in your IDE, and use its tooling to run the application. Thanks to the previous step, running as a desktop application should be the only available option.



You should now see the app running. Modify the content in lib/main.dart as follows, and perform a hot reload to update the content. How to perform a hot reload changes depending on whether you are running the app via the command line (type ‘r’ into the console window) or via an editor (in which case, saving the file is probably enough to trigger hot reload).

これで、アプリが動作しているのが確認できるはずです。lib/main.dart の内容を以下のように変更し、ホットリロードを実行して内容を更新します。ホットリロードの実行方法は、アプリをコマンドライン経由で実行しているか(コンソールウィンドウに ‘r’ と入力)、エディタ経由で実行しているか(その場合、ファイルを保存するだけでホットリロードが開始されます)によって異なります。

import 'dart:io' show Platform;
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      home: const ResizeablePage(),

class ResizeablePage extends StatelessWidget {
  const ResizeablePage({Key? key}) : super(key: key);

  Widget build(BuildContext context) {
    final mediaQuery = MediaQuery.of(context);
    final themePlatform = Theme.of(context).platform;

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
              'Window properties',
              style: Theme.of(context).textTheme.headline5,
            const SizedBox(height: 8),
              width: 350,
              child: Table(
                textBaseline: TextBaseline.alphabetic,
                children: <TableRow>[
                    context: context,
                    property: 'Window Size',
                    value: '${mediaQuery.size.width.toStringAsFixed(1)} x '
                    context: context,
                    property: 'Device Pixel Ratio',
                    value: mediaQuery.devicePixelRatio.toStringAsFixed(2),
                    context: context,
                    property: 'Platform.isXXX',
                    value: platformDescription(),
                    context: context,
                    property: 'Theme.of(ctx).platform',
                    value: themePlatform.toString(),

  TableRow _fillTableRow(
      {required BuildContext context,
      required String property,
      required String value}) {
    return TableRow(
      children: [
          verticalAlignment: TableCellVerticalAlignment.baseline,
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Text(property),
          verticalAlignment: TableCellVerticalAlignment.baseline,
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Text(value),

  String platformDescription() {
    if (kIsWeb) {
      return 'Web';
    } else if (Platform.isAndroid) {
      return 'Android';
    } else if (Platform.isIOS) {
      return 'iOS';
    } else if (Platform.isWindows) {
      return 'Windows';
    } else if (Platform.isMacOS) {
      return 'macOS';
    } else if (Platform.isLinux) {
      return 'Linux';
    } else if (Platform.isFuchsia) {
      return 'Fuchsia';
    } else {
      return 'Unknown';

The above app is designed to give you a feeling for how different platforms can be detected and adapted to. Here is the app running natively on Android and iOS:


And here is the same code running natively on macOS and inside of Chrome, again running on macOS.


The important point to note here is that, at first glance, Flutter is doing what it can to adapt the content to the display it is running on.



The laptop on which these screenshots were taken has a high resolution Mac display, which is why both the macOS and web versions of the app are rendered at Device Pixel Ratio of 2. Meanwhile, on the iPhone 12, you see a ratio of 3, and 2.63 on the Pixel 2. In all cases the displayed text is roughly similar, making our job as developers a lot easier.

これらのスクリーンショットを撮影したノートパソコンには高解像度のMacディスプレイが搭載されており、そのためmacOS版とウェブ版のアプリはどちらもデバイスピクセル比2.00でレンダリングされています。一方、iPhone 12では3.00、Pixel 2では2.63の比率で表示されています。いずれの場合も、表示されるテキストはほぼ同じで、開発者としての仕事がとてもやりやすくなっています。


The second point to note is that the two options for checking out which platform the code is running on results in different values.



The first option inspects the Platform object imported from dart:io, while the second option (available only inside the Widget’s build method), retrieves the Theme object from the BuildContext argument.



The reason that these two methods return different results is that their intent is different.



The Platform object imported from dart:io is meant to be used for making decisions that are independent of rendering choices.



A prime example of this is deciding which plugins to use, which may or may not have matching native implementations for a specific physical platform.



Extracting the Theme from the BuildContext is intended for implementation decisions that are Theme centric.



A prime example of this is deciding whether to use the Material slider, or the Cupertino slider, as discussed in Slider.adaptive.


Best Practices

  • For decisions that are not dependent on platform specific capabilities, use Theme.of(context).platform. A primary example of this is adapting the ordering of buttons to conform to operating system idioms. Most of your adaptive code should use this approach to adapt to different platforms.



  • For decisions that depend on platform specific capabilities, use the kIsWeb constant along with isAndroid, isIOS and friends from dart:io‘s Platform class. A capability difference example is the browser requirement for CORS headers on cross domain requests.



In the next section you’ll build a basic YouTube playlist explorer app that is optimized purely for Android and iOS. In the following sections you will add various adaptations to make the app work better on desktop and the web.

次のセクションでは、Android と iOS 向けに純粋に最適化された基本的な YouTube プレイリスト エクスプローラー アプリを作成します。その次のセクションでは、このアプリをデスクトップやウェブでよりよく動作させるために、さまざまな改良を加えます。

4. Build a mobile app

Add packages

このアプリでは、YouTube Data APIへのアクセス、ステート管理、そしてテーマ設定のタッチを得るために、さまざまなFlutterパッケージを使用します。

$ flutter pub add googleapis
Resolving dependencies...
+ _discoveryapis_commons 1.0.2
+ googleapis 7.0.0
+ http 0.13.4
+ http_parser 4.0.0
  test_api 0.4.3 (0.4.8 available)
Changed 4 dependencies!


The first package, googleapis is a generated Dart library for accessing Google APIs.


$ flutter pub add http
Resolving dependencies...
  test_api 0.4.3 (0.4.8 available)
Got dependencies!

The http package will be instrumental for building out the ability to access the YouTube Data API using API keys.

httpパッケージはAPIキーを利用してYouTube Data APIにアクセスする機能を構築するために必要なものです。

$ flutter pub add provider


$ flutter pub add url_launcher

Use url_launcher as a way of jumping into a video from a playlist. As you can see from the resolved dependencies, url_launcher has implementations for Windows, macOS, Linux, and the web, in addition to the default Android and iOS. This is one capability you won’t need to create platform specific code for.

プレイリストから動画にジャンプする方法として、url_launcherを使用します。解決された依存関係からわかるように、url_launcher には、デフォルトの Android と iOS に加えて、Windows、macOS、Linux、Web のための実装があります。これは、プラットフォーム固有のコードを作成する必要がない機能の 1 つです。

$ flutter pub add flex_color_scheme

This last package is purely about giving the app a nice default color scheme. See the flex_color_schemedocumentation to get an understanding of the full range of capabilities.

この最後のパッケージは純粋にアプリに素敵なデフォルトのColorSchemeを与えるためのものです。flex_color_scheme のドキュメントを参照し、すべての機能を理解してください。

Configuring the mobile apps for url_launcher

The url_launcher plugin requires configuration of the Android and iOS runner applications. In the iOS Flutter runner, add the following lines to the plist dictionary.

url_launcher プラグインは、Android と iOS のrunnerアプリケーションの設定が必要です。iOSのFlutter runnerで、plist dictionaryに以下の行を追加します。



In the Android Flutter runner, add the following lines to the Manifest.xml. Add this queries node as a direct child of the manifest node and a peer of the application node.

Android Flutter runnerで、Manifest.xml に次の行を追加します。このクエリノードをマニフェストノードの直接の子として、またアプリケーションノードのピアとして追加します。


        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="https" />
        <action android:name="android.intent.action.DIAL" />
        <data android:scheme="tel" />
        <action android:name="android.intent.action.SEND" />
        <data android:mimeType="*/*" />

For more details about these required configuration changes, please see the url_launcher documentation.






パート2へ >>