Flutterで折りたたみ式デバイスに対応する場合、AndroidではMediaQuery.displayFeaturesなどを利用してディスプレイの折り目やヒンジを扱えます。
一方、iOSではFlutterが折りたたみデバイスのヒンジ情報をそのまま提供していないため、iPhone Duoのような折りたたみ式iPhoneでは、ネイティブ側の情報をFlutterへ橋渡しする必要があります。
「foldable」は、iPhone Duoのヒンジ角度や折りたたみ状態、ディスプレイ上の折り目やカメラ領域などをFlutterから取得できるパッケージです。
foldableとは
foldableは、Appleの折りたたみ式iPhone「iPhone Duo」に対応するためのFlutterパッケージです。
iPhone Duoの状態をFlutterから取得し、以下のような情報をアプリで利用できます。
- ヒンジの角度
- 折りたたみ状態
- 折り目の領域
- カメラによる占有領域
- iOSのSize Class
- 折りたたみ状態に応じたレイアウト変更
パッケージの最新バージョンは1.0.4で、iOS向けに提供されています。
主な特徴
| 機能 | 内容 |
|---|---|
| ヒンジ角度 | 0〜180度のヒンジ角度を取得 |
| 折りたたみ状態 | closed、partiallyOpen、fullyOpenを取得 |
| リアルタイム監視 | ヒンジ角度や状態の変化をStreamで監視 |
| FoldAwareBuilder | 折りたたみ状態に応じてWidgetを再構築 |
| DuoMediaQuery | Widgetツリーから折りたたみ情報を取得 |
| Size Class | iOSの画面サイズクラスを取得 |
| 折り目領域 | ディスプレイのヒンジ部分を取得 |
| カメラ領域 | カメラによる占有領域を取得 |
| MediaQuery連携 | displayFeaturesへの情報提供をオプションで有効化 |
| 非対応端末への対応 | 折りたたみ機構がない端末では安全に無効化 |
インストール
pubspec.yamlのdependenciesに追加します。
YAML
dependencies:
foldable: ^1.0.4
その後、Dartコードからインポートします。
Dart
import 'package:foldable/foldable.dart';
iPhone Duoが折りたたみ式か確認する
まず、実行中の端末が折りたたみ式デバイスかどうかを確認できます。
Dart
final isFoldable = await Foldable.isFoldable;
if (isFoldable) {
print('Foldable device');
}
iPhone Duo以外の端末ではfalseが返されます。
そのため、通常のiPhoneを含むアプリでも条件分岐を入れて利用できます。
ヒンジ角度を取得する
hingeAngleStreamを利用すると、ヒンジの角度をリアルタイムで監視できます。
Dart
Foldable.hingeAngleStream.listen((double degrees) {
print('Hinge angle: $degrees°');
});
角度は度数で取得されます。
- 0度:折りたたんだ状態
- 180度:完全に開いた状態
例えば、ヒンジの開き具合に応じてアニメーションやUIエフェクトを変更するといった使い方ができます。
折りたたみ状態を取得する
ヒンジの角度だけではなく、現在の折りたたみ状態も取得できます。
Dart
final status = await Foldable.hingeStatus;
print(status);
状態として以下が用意されています。
closedpartiallyOpenfullyOpen
状態の変化を監視する場合はhingeStatusStreamを利用します。
Dart
Foldable.hingeStatusStream.listen((status) {
print('Hinge status: $status');
});
ヒンジ角度のように連続的に変化する値ではなく、折りたたみ状態が変化したタイミングで通知を受け取れます。
FoldableDataでまとめて状態を取得する
Foldableには、現在の折りたたみ状態をまとめて取得するためのAPIも用意されています。
Dart
final data = await Foldable.snapshot;
print(data);
また、状態変化をFoldableDataとして受け取ることもできます。
Dart
Foldable.changes.listen((data) {
print(data);
});
ヒンジ角度や折りたたみ状態など、複数の情報をまとめて扱いたい場合に利用できます。
Widgetツリーで利用する
Widgetツリーの中で折りたたみ状態を利用する場合は、FoldableProviderを使用できます。
Dart
FoldableProvider(
child: MaterialApp(
home: HomePage(),
),
)
FoldableProviderでアプリをラップすることで、その配下のWidgetから折りたたみ情報を取得できるようになります。
FoldAwareBuilderを使う
折りたたみ状態に応じてレイアウトを変更する場合は、FoldAwareBuilderが利用できます。
Dart
FoldAwareBuilder(
builder: (context, constraints, fold) {
if (fold.isPartiallyOpen) {
return const TwoPaneLayout();
}
return const SinglePaneLayout();
},
)
例えば、端末が開いている場合は2ペイン、閉じている場合は1ペインというようにレイアウトを切り替えられます。
折り目を考慮したレイアウト
iPhone Duoの内側ディスプレイでは、中央に折り目となる領域があります。
FoldAwareBuilderから取得できるFoldInfoには、折り目に関する情報も含まれています。
例えば、折り目をまたがないように左右2つの領域へUIを配置できます。
Dart
FoldAwareBuilder(
builder: (context, constraints, fold) {
if (!fold.spansDivision) {
return const SinglePane();
}
return Row(
children: const [
Expanded(
child: Leading(),
),
Expanded(
child: Trailing(),
),
],
);
},
)
これにより、折り目を境界として2ペインのUIを構築できます。
DuoMediaQuery
Widgetツリー内から折りたたみ情報を取得する場合は、DuoMediaQueryも利用できます。
例えば、現在の折りたたみ状態を取得できます。
Dart
final status = DuoMediaQuery.statusOf(context);
ヒンジ角度を取得することもできます。
Dart
final angle = DuoMediaQuery.angleOf(context);
さらに、現在の状態をまとめて取得することもできます。
Dart
final data = DuoMediaQuery.of(context);
DuoMediaQueryはInheritedModelとして実装されているため、必要な情報に依存するWidgetだけを再構築できるようになっています。
Size Classにも対応
foldableでは、iOSのSize Classも取得できます。
例えば、横方向のSize Classを取得します。
Dart
final widthClass =
DuoMediaQuery.horizontalSizeClassOf(context);
if (widthClass == SizeClass.regular) {
// 広いレイアウト
}
Size Classには、画面の物理的な幅だけでは判断できないiOSのレイアウト状態が反映されます。
そのため、単純に画面幅だけを基準にするのではなく、iOSが提供するレイアウト情報を使ってUIを切り替えられます。
MediaQuery.displayFeaturesとの連携
foldableには、折り目やカメラ領域をFlutterのMediaQuery.displayFeaturesへ橋渡しする機能もあります。
ただし、この機能はデフォルトでは無効になっています。
これは、パッケージを導入しただけでFlutterの既存のレイアウト動作が変わらないようにするためです。
必要に応じてDisplayFeatureBridgeModeを指定できます。
Dart
FoldableProvider(
bridgeMode: DisplayFeatureBridgeMode.cutoutsOnly,
child: MaterialApp(
home: HomePage(),
),
)
利用できるモードには以下があります。
| モード | 動作 |
|---|---|
none | displayFeaturesへ情報を提供しない |
cutoutsOnly | カメラなどのカットアウト領域だけを提供 |
full | 折り目とカットアウト領域を提供 |
デフォルトはnoneです。
非折りたたみ端末でも利用できる
foldableは、iPhone Duo以外の端末で利用した場合にも安全に動作するよう設計されています。
折りたたみ機構を持たない端末では、isFoldableがfalseになります。
また、ヒンジ関連のStreamではイベントが発生しません。
そのため、通常のiPhoneを対象としたアプリに導入しても、折りたたみ端末だけを特別に処理できます。
iOS 27.1 SDKとの関係
iPhone DuoのヒンジAPIはiOS 27.1 SDKで提供されています。
一方、foldableは古いSDKでもビルドできるよう、実行時にネイティブAPIを解決する仕組みを採用しています。
そのため、パッケージを利用するだけで特定の新しいXcode環境を必須にする構成にはなっていません。
また、ネイティブAPIを利用できない場合は、その機能を無効化する仕組みが用意されています。
ネイティブAPIの情報を確認する
実行中の環境で利用できるネイティブAPIの情報を確認するためのdebugDumpNativeApi()も用意されています。
Dart
final dump = await Foldable.debugDumpNativeApi();
print(dump);
また、Foldable.capabilitiesから現在利用可能な機能や、その取得方法に関する情報を確認できます。
Dart
final capabilities = await Foldable.capabilities;
print(capabilities);
どのようなアプリに向いている?
foldableは、特にiPhone Duoのような折りたたみ式iPhoneを想定したUIを構築する場合に利用できます。
2ペインUI
折りたたみ状態に応じて、1画面表示から2ペイン表示へ切り替えるUIを構築できます。
折り目を考慮したUI
中央のヒンジ部分を避けて、左右にコンテンツを配置できます。
ヒンジ角度を利用したUI
ヒンジ角度をリアルタイムで取得できるため、端末の開閉に合わせたアニメーションなどに利用できます。
iOSのレスポンシブレイアウト
Size Classを利用して、画面サイズやレイアウト状態に応じてUIを変更できます。
まとめ
foldableは、FlutterアプリからiPhone Duoの折りたたみ機能を扱うためのiOS向けパッケージです。
主に以下の情報をFlutterから取得できます。
- ヒンジ角度
- 折りたたみ状態
- 折り目の領域
- カメラ領域
- iOS Size Class
- 折りたたみ状態の変化
また、FoldableProvider、FoldAwareBuilder、DuoMediaQueryなどを利用することで、折りたたみ状態に応じたレイアウトを構築できます。
iPhone Duo向けにFlutterアプリのUIを最適化したい場合や、折りたたみ状態を利用したレスポンシブなUIを実装したい場合に利用できるパッケージです。
