Flutter debugDefaultTargetPlatformOverrideとは何か？を理解するとデスクトップ対応の状況が見えてくる

# はじめに

`package:flutter/foundation.dart`で提供される[`debugDefaultTargetPlatformOverride`](https://api.flutter.dev/flutter/foundation/debugDefaultTargetPlatformOverride.html)について解説します。

# 2020/03/10 追記

[Add Linux and Windows target platforms](https://github.com/flutter/flutter/pull/51519)のissueで正式対応されたため、本記事の`debugDefaultTargetPlatformOverride`の対応が不要になります！まだ`master`チャンネルのみですが。

# 用途

用途は主に以下の2つです。

* 今時点でFlutterのデスクトップ向けのコードを書く場合 (以下のサンプルコード参照)
* 例えばAndroid端末でiOS仕様の挙動の確認をしたいなど (実機と異なるプラットフォームの動作確認) の場合

```Dart:
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app/my_app.dart';

void _setOverrideForDesktop() {
  if (kIsWeb) return;

  if (Platform.isMacOS) {
    debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
  } else if (Platform.isLinux || Platform.isWindows) {
    debugDefaultTargetPlatformOverride = TargetPlatform.android; // ←　Linux/Windowsデスクトップ向けはこれが必要！
  } else if (Platform.isFuchsia) {
    debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
  }
}

void main() {
  _setOverrideForDesktop();
  runApp(MyMaterialApp());
}
```


# debugDefaultTargetPlatformOverrideとは何？

`debugDefaultTargetPlatformOverride`は本来ターゲットとするプラットフォームから強制的に別のプラットフォーム向けの挙動に変えるためのデバッグ専用のプロパティです。

`debugDefaultTargetPlatformOverride`はデフォルトnullで、この場合はアプリがターゲットのプラットフォーム向けの挙動になります。一方、上記のように何らかの値が設定されている場合には、強制的に指定されたプラットフォームの挙動をするようになります。

# デスクトップ for Linux/Windows向けの現状

[foundation/_platform_io.dartのソースコード](https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/foundation/_platform_io.dart)を見てみると、WindowsやLinuxの正式対応がまだされておらず、`debugDefaultTargetPlatformOverride`を使って何かのプラットフォームを指定しないとダメなことがわかります。

```Dart:flutter/packages/flutter/lib/src/foundation/_platform_io.dart
import 'dart:io';
import 'assertions.dart';
import 'platform.dart' as platform;

/// The dart:io implementation of [platform.defaultTargetPlatform].
platform.TargetPlatform get defaultTargetPlatform {
  platform.TargetPlatform result;
  if (Platform.isIOS) {
    result = platform.TargetPlatform.iOS;
  } else if (Platform.isMacOS) {
    result = platform.TargetPlatform.macOS;
  } else if (Platform.isAndroid) {
    result = platform.TargetPlatform.android;
  } else if (Platform.isFuchsia) {
    result = platform.TargetPlatform.fuchsia;
  }
  assert(() {
    if (Platform.environment.containsKey('FLUTTER_TEST'))
      result = platform.TargetPlatform.android;
    return true;
  }());
  if (platform.debugDefaultTargetPlatformOverride != null)
    result = platform.debugDefaultTargetPlatformOverride;
  if (result == null) {
    throw FlutterError(
      'Unknown platform.\n'
      '${Platform.operatingSystem} was not recognized as a target platform. '
      'Consider updating the list of TargetPlatforms to include this platform.'
    );
  }
  return result;
}
```

## プラットフォーム毎に何が違う？

では、プラットフォーム毎に何が違うのでしょうか？
それを解決するためには、`defaultTargetPlatform`をFlutter Frameworkのソースコードで grep してみると分かります。

一番分かりやすいのは、`CupertinoSwitch`です。
ソースコードの[flutter/packages/flutter/lib/src/cupertino/switch.dart](https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/cupertino/switch.dart)を見てみます。

iOSだけ`HapticFeedback.lightImpact()`をコールしています。

```Dart:flutter/packages/flutter/lib/src/cupertino/switch.dart
  void _emitVibration() {
    switch (defaultTargetPlatform) {
      case TargetPlatform.iOS:
        HapticFeedback.lightImpact();
        break;
      case TargetPlatform.android:
      case TargetPlatform.fuchsia:
      case TargetPlatform.macOS:
        break;
    }
  }
```

`HapticFeedback.lightImpact()`を追いかけてみると、SystemChannels APIを利用してプラットフォーム側のバイブレーション機能を呼び出しています。

結局この場合はハードウェアの実物がないと特に差分無しという感じになりますね。

なお、SystemCHannels APIんついては、[Flutter プラットフォーム固有機能を利用するためのSystemChannels APIについて](https://qiita.com/kurun_pan/items/c0c881f7a3f95ecb3f9d)にまとめていますので参考にして下さい。

[flutter/packages/flutter/lib/src/services/haptic_feedback.dar](https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/services/haptic_feedback.dart)

```Dart:flutter/packages/flutter/lib/src/services/haptic_feedback.dar
  /// Provides a haptic feedback corresponding a collision impact with a light mass.
  ///
  /// On iOS versions 10 and above, this uses a `UIImpactFeedbackGenerator` with
  /// `UIImpactFeedbackStyleLight`. This call has no effects on iOS versions
  /// below 10.
  ///
  /// On Android, this uses `HapticFeedbackConstants.VIRTUAL_KEY`.
  static Future<void> lightImpact() async {
    await SystemChannels.platform.invokeMethod<void>(
      'HapticFeedback.vibrate',
      'HapticFeedbackType.lightImpact',
    );
  }
```