FlutterでiOSアプリ内にAppleの地図を表示する 2021.08.23
apple_maps_flutter
platform_maps_flutterは導入でコケたので、まずは簡単なapple_maps_flutterの方から。
プロジェクトを作成してSimulatorでアプリを起動。
% flutter create flutter_platform_maps_trial
% cd flutter_platform_maps_trial
% open -a Simulator
% flutter run
pubspec.yamlの編集
dependencies:
apple_maps_flutter: ^1.0.1
Info.plistの編集
To use this plugin on iOS you need to opt-in for the embedded views preview by adding a boolean property to the app’s Info.plist file, with the key io.flutter.embedded_views_preview and the value YES. You will also have to add the key Privacy - Location When In Use Usage Description with the value of your usage description.
以下のように追加した。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- ... -->
<key>io.flutter.embedded_views_preview</key>
<true/>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app requires user’s location for better user experience.</string>
</dict>
</plist>
io.flutter.embedded_views_preview
はWebViewを許可する項目のようだ。WebViewが必要なんだろうか?(後述)
NSLocationAlwaysAndWhenInUseUsageDescription
の中身は、位置情報を使いますよ、というユーザー向けの説明文でいいようだ。
main.dartの編集
apple_maps_flutterをインポートする部分。
import 'package:apple_maps_flutter/apple_maps_flutter.dart';
公式のサンプルは少し古いので修正。
class AppleMapsExample extends StatelessWidget {
+ AppleMapsExample({Key? key}) : super(key: key);
+
- AppleMapController mapController;
+ late AppleMapController mapController;
void _onMapCreated(AppleMapController controller) {
mapController = controller;
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: Container(
child: AppleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: const CameraPosition(
target: LatLng(0.0, 0.0),
),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Column(
children: <Widget>[
- FlatButton(
+ TextButton(
onPressed: () {
// ...
},
child: const Text('newCameraPosition'),
),
- FlatButton(
+ TextButton(
onPressed: () {
// ...
},
child: const Text('newLatLngZoom'),
),
],
),
Column(
children: <Widget>[
- FlatButton(
+ TextButton(
onPressed: () {
// ...
},
child: const Text('zoomIn'),
),
- FlatButton(
+ TextButton(
onPressed: () {
// ...
},
child: const Text('zoomOut'),
),
- FlatButton(
+ TextButton(
onPressed: () {
// ...
},
child: const Text('zoomTo'),
),
],
),
],
)
],
);
}
}
final
じゃないフィールドがある、というLintメッセージの意味はわかったが解決はできなかった。
This class (or a class that this class inherits from) is marked as ‘@immutable’, but one or more of its instance fields aren’t final: AppleMapsExample.mapController
apple_maps_flutterで問題なく地図を表示できた。
platform_maps_flutter
platform_maps_flutterはiOSにもAndroidにも対応できるのでマルチプラットフォームに便利そうだが、ビルドがなかなか通らなくて難儀した。
pubspec.yamlの編集
dependencies:
platform_maps_flutter: ^1.0.2
Info.plistは同じ。
main.dartの編集
import 'package:platform_maps_flutter/platform_maps_flutter.dart';
こちらのサンプルは最終的にそのままでも動いた。Lintに注意されたところだけ修正。
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: PlatformMap(
- initialCameraPosition: CameraPosition(
+ initialCameraPosition: const CameraPosition(
- target: const LatLng(47.6, 8.8796),
+ target: LatLng(47.6, 8.8796),
zoom: 16.0,
),
markers: // この部分は後述,
myLocationEnabled: true,
myLocationButtonEnabled: true,
onTap: (location) => print('onTap: $location'),
onCameraMove: (cameraUpdate) => print('onCameraMove: $cameraUpdate'),
compassEnabled: true,
onMapCreated: (controller) {
- Future.delayed(Duration(seconds: 2)).then(
+ Future.delayed(const Duration(seconds: 2)).then(
(_) {
controller.animateCamera(
CameraUpdate.newCameraPosition(
const CameraPosition(
// ...
),
),
);
},
);
},
),
);
}
}
しかし、buildに失敗。
% flutter run
Launching lib/main.dart on iPhone 12 in debug mode...
Running pod install... 2,806ms
Running Xcode build...
└─Compiling, linking and signing... 2,556ms
Xcode build done. 12.6s
Failed to build iOS app
Error output from Xcode build:
↳
** BUILD FAILED **
Xcode's output:
↳
ld: building for iOS Simulator, but linking in object file built for iOS, file
'/Users/.../ios/Pods/GoogleMaps/Base/Frameworks/GoogleMapsBase.framework/GoogleMa
psBase' for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
note: Using new build system
note: Building targets in parallel
note: Planning build
note: Constructing build description
Could not build the application for the simulator.
Error launching application on iPhone 12.
調べると、EXCLUDED_ARCHS
にarm64
を指定すればいいらしい。
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
+ installer.pods_project.build_configurations.each do |config|
+ config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64"
+ end
end
Podfileに追加した上のコードは、下の画像のようなXCodeの設定をプログラム的に行っているようだ。
- https://stackoverflow.com/questions/63607158/xcode-12-building-for-ios-simulator-but-linking-in-object-file-built-for-ios/63955114#63955114
- https://milanpanchal24.medium.com/xcode-12-building-for-ios-simulator-but-linking-in-object-file-built-for-ios-file-for-8c0cc28ec832
pod install
したら動いた。
% cd ios
% pod install
いろいろ弄っている場合、pod cache clean
が必要かもしれない。わからん。
% cd ios
% pod cache clean --all
% pod install
FlutterでAppleの地図を表示できた。
Use collection literals when possible.
サンプルの以下の部分。
Set<Marker>.of(
[
Marker(
// ...
),
],
)
Setリテラル{}
を使う。
<Marker>{
Marker(
// ...
),
},
io.flutter.embedded_views_previewは何を指定している?
io.flutter.embedded_views_previewフラグも不要に( ´・‿・`)
Info.plistからio.flutter.embedded_views_preview
の指定を外して、キャッシュを削除して問題なくビルドできるようだった。確信はない。
そして、io.flutter.embedded_views_preview
はWebViewではなくPlatformViewを使うためのフラグのようだ。以前は必要だったが、今は指定しなくても良くなったと考えられる。
PlatformView を使えばビューが Flutter のウイジェットツリーに組み込まれるので、表示の階層やレイアウトが Flutter 側で制御できるようになり、取り回しが大変良くなります。