こんにちは、CX事業本部 IoT事業部の若槻です。
今回は、FlutterのCupertinoでナビゲーションバーを実装する際に少しハマったことについて共有します。
Cupertinoとは
Cupertinoを使用すると、FlutterアプリケーションにiOSスタイルのデザインのWidgetを導入することができます。
CupertinoスタイルのWidgetは様々なものが用意されており、既定のMaterial DesignスタイルのWidgetを置き換えて、iOSネイティブのようなデザインのUIを簡単に実装できます。
下記エントリでは、CupertinoとMaterial DesignのUIの実装を比較しているので合わせてご覧ください。
ハマったこと
さて、そんなCupertinoのCupertinoNavigationBar classを使用してナビゲーションバーを実装しようとしたところ、いくつかハマりどころがありました。
下記はドキュメントにあるサンプルコードです。このコードを例にハマった箇所について紹介します。
lib/main.dart
import 'package:flutter/cupertino.dart';
void main() => runApp(const NavBarApp());
class NavBarApp extends StatelessWidget {
const NavBarApp({super.key});
@override
Widget build(BuildContext context) {
return const CupertinoApp(
theme: CupertinoThemeData(brightness: Brightness.light),
home: NavBarExample(),
);
}
}
class NavBarExample extends StatefulWidget {
const NavBarExample({super.key});
@override
State<NavBarExample> createState() => _NavBarExampleState();
}
class _NavBarExampleState extends State<NavBarExample> {
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
backgroundColor: CupertinoColors.systemGrey.withOpacity(0.5),
middle: const Text('CupertinoNavigationBar Sample'),
),
child: Column(
children: <Widget>[
Container(height: 50, color: CupertinoColors.systemRed),
Container(height: 50, color: CupertinoColors.systemGreen),
Container(height: 50, color: CupertinoColors.systemBlue),
Container(height: 50, color: CupertinoColors.systemYellow),
],
),
);
}
}
アプリケーションをChromeで実行した様子。
デフォルトで背景色が透過となる
まず、CupertinoNavigationBarではデフォルトで背景色が透過され、オブジェクトがオーバーレイしてしまいます。
例えば次のようにbackgroundColor
を指定しない場合は既定の透過度0
となります。
lib/main.dart(backgroundColorがデフォルト値)
navigationBar: const CupertinoNavigationBar(
//backgroundColor: CupertinoColors.systemGrey.withOpacity(0.5),
middle: Text('CupertinoNavigationBar Sample'),
),
透過させないようにするためには、次のように透過度1.0
を明示的に指定するか、透過なしのColorオブジェクトを指定するようにします。
lib/main.dart(backgroundColorの透過度が1.0)
navigationBar: CupertinoNavigationBar(
backgroundColor: CupertinoColors.systemGrey.withOpacity(1.0),
middle: const Text('CupertinoNavigationBar Sample'),
),
lib/main.dart(backgroundColorの透過度が1.0)
navigationBar: CupertinoNavigationBar(
backgroundColor: CupertinoColors.systemGrey,
middle: const Text('CupertinoNavigationBar Sample'),
),
別Widget化する場合は ObstructingPreferredSizeWidget で implements する
クラスを再利用出来るように別Widget化したい場合があると思います。そこでCupertinoNavigationBar
を使用して次のようなCommonNavigationBar
Widgetを実装しました。
lib/main.dart(エラー)
class _NavBarExampleState extends State<NavBarExample> {
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CommonNavigationBar('CupertinoNavigationBar Sample'),
child: Column(
children: <Widget>[
Container(height: 50, color: CupertinoColors.systemRed),
Container(height: 50, color: CupertinoColors.systemGreen),
Container(height: 50, color: CupertinoColors.systemBlue),
Container(height: 50, color: CupertinoColors.systemYellow),
],
),
);
}
}
class CommonNavigationBar extends StatelessWidget {
final String middleText;
const CommonNavigationBar({Key? key, this.middleText = ''}) : super(key: key);
@override
Widget build(BuildContext context) {
return CupertinoNavigationBar(
backgroundColor: CupertinoColors.systemGrey,
middle: Text(middleText),
);
}
}
しかし上記実装は次のようなエラーとなってしまいます。
原因としては、エラーメッセージの通りなのですが、CupertinoPageScaffold.navigationBar
に指定可能なObstructingPreferredSizeWidget
以外のタイプが指定されたためです。
The argument type 'CommonNavigationBar' can't be assigned to the parameter type 'ObstructingPreferredSizeWidget?'
ObstructingPreferredSizeWidgetクラスでは、Widgetの優先サイズを取得するgetterであるPreferredSizeWidget.preferredSize
と、透過の有無を返すObstructingPreferredSizeWidget.shouldFullyObstruct
の実装が必要となります。
そこでCommonNavigationBar
を次のように修正し、ObstructingPreferredSizeWidget
を継承したWidgetを実装します。
lib/main.dart
class _NavBarExampleState extends State<NavBarExample> {
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CommonNavigationBar(
middleText: 'CupertinoNavigationBar Sample'),
child: Column(
children: <Widget>[
Container(height: 50, color: CupertinoColors.systemRed),
Container(height: 50, color: CupertinoColors.systemGreen),
Container(height: 50, color: CupertinoColors.systemBlue),
Container(height: 50, color: CupertinoColors.systemYellow),
],
),
);
}
}
class CommonNavigationBar extends StatelessWidget
implements ObstructingPreferredSizeWidget {
final String middleText;
const CommonNavigationBar({Key? key, this.middleText = ''}) : super(key: key);
@override
Widget build(BuildContext context) {
return CupertinoNavigationBar(
backgroundColor: CupertinoColors.systemGrey,
middle: Text(middleText),
);
}
@override
Size get preferredSize => const Size.fromHeight(40); // Widgetの高さを指定
@override
bool shouldFullyObstruct(BuildContext context) {
return true; // 透過させない場合はtrue
}
}
これで無事CupertinoNavigationBar
を別Widget化できました。
おわりに
FlutterのCupertinoでナビゲーションバーを実装する際に少しハマったことについて共有しました。
公式ドキュメントをよく読みましょうということに尽きると思います。
以上