I am trying to host a native MapView from the ArcGIS Maps SDK for Kotlin v200.1 inside a Flutter app.
This is my main.dart
file, which simply displays a custom MapView
widget inside a Scaffold:
// lib/main.dart
import 'package:flutter/material.dart';
import 'map_view.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('ArcGIS Map View'),
),
body: const Center(
child: MapView()
),
),
);
}
}
The MapView
widget looks like so:
// lib/map_view.dart
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class MapView extends StatelessWidget {
const MapView({super.key});
@override
Widget build(BuildContext context) {
if (defaultTargetPlatform == TargetPlatform.iOS) {
return const UiKitView(viewType: _viewType);
} else if (defaultTargetPlatform == TargetPlatform.android) {
return const AndroidView(viewType: _viewType);
} else {
throw UnsupportedError('Platform not supported');
}
}
static const _viewType = 'mapView';
}
On the Android side, I try to create the ArcGIS map view as part of a PlatformView
:
// android/app/src/main/kotlin/com/example/test/NativeView.kt
package com.example.test
import android.content.Context
import android.view.View
import io.flutter.plugin.platform.PlatformView
import com.arcgismaps.ApiKey
import com.arcgismaps.ArcGISEnvironment
import com.arcgismaps.mapping.ArcGISMap
import com.arcgismaps.mapping.BasemapStyle
import com.arcgismaps.mapping.Viewpoint
import com.arcgismaps.mapping.view.MapView
internal class NativeView(context: Context, id: Int, creationParams: Map<String?, Any?>?) : PlatformView {
private val mapView: MapView
init {
ArcGISEnvironment.apiKey = ApiKey.create("<hidden>")
mapView = MapView(context)
mapView.map = ArcGISMap(BasemapStyle.ArcGISTopographic)
mapView.setViewpoint(Viewpoint(34.0270, -118.8050, 72000.0))
}
override fun getView(): View {
return mapView
}
override fun dispose() {}
}
The NativeView
is instantiated by a NativeViewFactory
as described in the Flutter documentation (Hosting a native Android view).
When running the Flutter app in an Android emulator, it crashes on startup with an exception saying "lateinit property lifeCycleOwner has not been initialized":
A Dart VM Service on sdk gphone64 x86 64 is available at: http://127.0.0.1:65061/zcoQFFeeeZk=/
The Flutter DevTools debugger and profiler on sdk gphone64 x86 64 is available at:
http://127.0.0.1:9102?uri=http://127.0.0.1:65061/zcoQFFeeeZk=/
I/PlatformViewsController(10555): Hosting view in view hierarchy for platform view: 0
I/Choreographer(10555): Skipped 62 frames! The application may be doing too much work on its main thread.
E/FrameEvents(10555): updateAcquireFence: Did not find frame.
W/Parcel (10555): Expecting binder but got null!
I/TextureView(10555): onSurfaceTextureAvailable
D/AndroidRuntime(10555): Shutting down VM
E/FrameEvents(10555): updateAcquireFence: Did not find frame.
E/AndroidRuntime(10555): FATAL EXCEPTION: main
E/AndroidRuntime(10555): Process: com.example.test, PID: 10555
E/AndroidRuntime(10555): kotlin.UninitializedPropertyAccessException: lateinit property lifeCycleOwner has not been initialized
E/AndroidRuntime(10555): at com.arcgismaps.mapping.view.GeoView.getLifeCycleOwner$api_release(GeoView.kt:110)
E/AndroidRuntime(10555): at com.arcgismaps.mapping.view.GeoView$RenderingThread.onSurfaceTextureAvailable(GeoView.kt:1630)
E/AndroidRuntime(10555): at android.view.TextureView.getTextureLayer(TextureView.java:466)
E/AndroidRuntime(10555): at android.view.TextureView.draw(TextureView.java:415)
E/AndroidRuntime(10555): at android.view.View.updateDisplayListIfDirty(View.java:22061)
E/AndroidRuntime(10555): at android.view.View.draw(View.java:22925)
E/AndroidRuntime(10555): at android.view.ViewGroup.drawChild(ViewGroup.java:4529)
E/AndroidRuntime(10555): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4290)
E/AndroidRuntime(10555): at android.view.View.updateDisplayListIfDirty(View.java:22052)
E/AndroidRuntime(10555): at android.view.View.draw(View.java:22925)
E/AndroidRuntime(10555): at android.view.ViewGroup.drawChild(ViewGroup.java:4529)
E/AndroidRuntime(10555): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4290)
E/AndroidRuntime(10555): at android.view.View.draw(View.java:23197)
E/AndroidRuntime(10555): at io.flutter.plugin.platform.PlatformViewWrapper.draw(PlatformViewWrapper.java:305)
E/AndroidRuntime(10555): at android.view.View.updateDisplayListIfDirty(View.java:22061)
E/AndroidRuntime(10555): at android.view.View.draw(View.java:22925)
E/AndroidRuntime(10555): at android.view.ViewGroup.drawChild(ViewGroup.java:4529)
E/AndroidRuntime(10555): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4290)
E/AndroidRuntime(10555): at android.view.View.updateDisplayListIfDirty(View.java:22052)
E/AndroidRuntime(10555): at android.view.View.draw(View.java:22925)
E/AndroidRuntime(10555): at android.view.ViewGroup.drawChild(ViewGroup.java:4529)
E/AndroidRuntime(10555): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4290)
E/AndroidRuntime(10555): at android.view.View.updateDisplayListIfDirty(View.java:22052)
E/AndroidRuntime(10555): at android.view.View.draw(View.java:22925)
E/AndroidRuntime(10555): at android.view.ViewGroup.drawChild(ViewGroup.java:4529)
E/AndroidRuntime(10555): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4290)
E/AndroidRuntime(10555): at android.view.View.updateDisplayListIfDirty(View.java:22052)
E/AndroidRuntime(10555): at android.view.View.draw(View.java:22925)
E/AndroidRuntime(10555): at android.view.ViewGroup.drawChild(ViewGroup.java:4529)
E/AndroidRuntime(10555): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4290)
E/AndroidRuntime(10555): at android.view.View.draw(View.java:23197)
E/AndroidRuntime(10555): at com.android.internal.policy.DecorView.draw(DecorView.java:821)
E/AndroidRuntime(10555): at android.view.View.updateDisplayListIfDirty(View.java:22061)
E/AndroidRuntime(10555): at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:689)
E/AndroidRuntime(10555): at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:695)
E/AndroidRuntime(10555): at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:793)
E/AndroidRuntime(10555): at android.view.ViewRootImpl.draw(ViewRootImpl.java:4670)
E/AndroidRuntime(10555): at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:4381)
E/AndroidRuntime(10555): at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3600)
E/AndroidRuntime(10555): at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2328)
E/AndroidRuntime(10555): at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:9087)
E/AndroidRuntime(10555): at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1231)
E/AndroidRuntime(10555): at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1239)
E/AndroidRuntime(10555): at android.view.Choreographer.doCallbacks(Choreographer.java:899)
E/AndroidRuntime(10555): at android.view.Choreographer.doFrame(Choreographer.java:832)
E/AndroidRuntime(10555): at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1214)
E/AndroidRuntime(10555): at android.os.Handler.handleCallback(Handler.java:942)
E/AndroidRuntime(10555): at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime(10555): at android.os.Looper.loopOnce(Looper.java:201)
E/AndroidRuntime(10555): at android.os.Looper.loop(Looper.java:288)
E/AndroidRuntime(10555): at android.app.ActivityThread.main(ActivityThread.java:7872)
E/AndroidRuntime(10555): at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime(10555): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
E/AndroidRuntime(10555): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
D/TrafficStats(10555): tagSocket(123) with statsTag=0xffffffff, statsUid=-1
I/Process (10555): Sending signal. PID: 10555 SIG: 9
Lost connection to device.
When instead using the older ArcGIS Runtime SDK for Android v100.15.2, it will work as expected:
// android/app/src/main/kotlin/com/example/test/NativeView.kt
package com.example.test
import android.content.Context
import android.view.View
import io.flutter.plugin.platform.PlatformView
import com.esri.arcgisruntime.ArcGISRuntimeEnvironment
import com.esri.arcgisruntime.mapping.ArcGISMap
import com.esri.arcgisruntime.mapping.view.MapView
import com.esri.arcgisruntime.mapping.BasemapStyle
import com.esri.arcgisruntime.mapping.Viewpoint
internal class NativeView(context: Context, id: Int, creationParams: Map<String?, Any?>?) : PlatformView {
private val mapView: MapView
init {
ArcGISRuntimeEnvironment.setApiKey("<hidden>")
mapView = MapView(context)
mapView.map = ArcGISMap(BasemapStyle.ARCGIS_TOPOGRAPHIC)
mapView.setViewpoint(Viewpoint(34.0270, -118.8050, 72000.0))
}
override fun getView(): View {
return mapView
}
override fun dispose() {}
}
Upon launch, I can see the map view being displayed:
I am not an experienced Android developer. Does anyone know what's wrong with the v200.1 approach?
Version info:
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.10.6, on macOS 13.4.1 22F770820d darwin-x64, locale en-US)
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.1)
[✓] Xcode - develop for iOS and macOS (Xcode 14.3.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2021.3)
[✓] VS Code (version 1.80.1)
[✓] VS Code (version 1.81.0-insider)
[✓] Connected device (3 available)
[✓] Network resources
• No issues found!
EDIT: There's someone trying to do the exact same thing using React Native and getting the same runtime exception on startup (StackOverflow post). However, no one has replied to that post unfortunately.