diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index a8a23b432..bba79f17c 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -11,7 +11,7 @@ _allPages = [ MapUiPage(), @@ -48,7 +52,7 @@ final List _allPages = [ Sources() ]; -class MapsDemo extends StatelessWidget { +class MapsDemo extends StatefulWidget { // FIXME: You need to pass in your access token via the command line argument // --dart-define=ACCESS_TOKEN=ADD_YOUR_TOKEN_HERE // It is also possible to pass it in while running the app via an IDE by @@ -58,6 +62,31 @@ class MapsDemo extends StatelessWidget { // in the following line with your access token directly. static const String ACCESS_TOKEN = String.fromEnvironment("ACCESS_TOKEN"); + @override + State createState() => _MapsDemoState(); +} + +class _MapsDemoState extends State { + @override + void initState() { + initHybridComposition(); + super.initState(); + } + + /// Determine the android version of the phone and turn off HybridComposition + /// on older sdk versions to improve performance for these + Future initHybridComposition() async { + if (!kIsWeb && Platform.isAndroid) { + final androidInfo = await DeviceInfoPlugin().androidInfo; + final sdkVersion = androidInfo.version.sdkInt; + if (sdkVersion != null && sdkVersion >= 29) { + MapboxMap.useHybridComposition = true; + } else { + MapboxMap.useHybridComposition = false; + } + } + } + void _pushPage(BuildContext context, ExamplePage page) async { if (!kIsWeb) { final location = Location(); @@ -77,7 +106,8 @@ class MapsDemo extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('MapboxMaps examples')), - body: ACCESS_TOKEN.isEmpty || ACCESS_TOKEN.contains("YOUR_TOKEN") + body: MapsDemo.ACCESS_TOKEN.isEmpty || + MapsDemo.ACCESS_TOKEN.contains("YOUR_TOKEN") ? buildAccessTokenWarning() : ListView.separated( itemCount: _allPages.length, diff --git a/example/pubspec.yaml b/example/pubspec.yaml index f8ca7a68e..9731f59c0 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: path_provider: ^2.0.0 http: ^0.13.0 collection: ^1.0.0 + device_info_plus: ^3.2.2 dependency_overrides: mapbox_gl_platform_interface: diff --git a/lib/src/mapbox_map.dart b/lib/src/mapbox_map.dart index 3e2c92856..ffd86c02e 100644 --- a/lib/src/mapbox_map.dart +++ b/lib/src/mapbox_map.dart @@ -222,6 +222,14 @@ class MapboxMap extends StatefulWidget { /// * All fade/transition animations have completed final OnMapIdleCallback? onMapIdle; + /// Set `MapboxMap.useHybridComposition` to `false` in order use Virtual-Display + /// (better for Android 9 and below but may result in errors on Android 12) + /// or leave it `true` (default) to use Hybrid composition (Slower on Android 9 and below). + static bool get useHybridComposition => + MethodChannelMapboxGl.useHybridComposition; + static set useHybridComposition(bool useHybridComposition) => + MethodChannelMapboxGl.useHybridComposition = useHybridComposition; + @override State createState() => _MapboxMapState(); } diff --git a/mapbox_gl_platform_interface/lib/mapbox_gl_platform_interface.dart b/mapbox_gl_platform_interface/lib/mapbox_gl_platform_interface.dart index 3585cd037..bf59ae3bb 100644 --- a/mapbox_gl_platform_interface/lib/mapbox_gl_platform_interface.dart +++ b/mapbox_gl_platform_interface/lib/mapbox_gl_platform_interface.dart @@ -7,6 +7,7 @@ import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:meta/meta.dart' show visibleForTesting; diff --git a/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart b/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart index 0ef0ff788..d50dc323b 100644 --- a/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart +++ b/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart @@ -2,6 +2,7 @@ part of mapbox_gl_platform_interface; class MethodChannelMapboxGl extends MapboxGlPlatform { late MethodChannel _channel; + static bool useHybridComposition = true; Future _handleMethodCall(MethodCall call) async { switch (call.method) { @@ -138,13 +139,50 @@ class MethodChannelMapboxGl extends MapboxGlPlatform { OnPlatformViewCreatedCallback onPlatformViewCreated, Set>? gestureRecognizers) { if (defaultTargetPlatform == TargetPlatform.android) { - return AndroidView( - viewType: 'plugins.flutter.io/mapbox_gl', - onPlatformViewCreated: onPlatformViewCreated, - gestureRecognizers: gestureRecognizers, - creationParams: creationParams, - creationParamsCodec: const StandardMessageCodec(), - ); + if (useHybridComposition) { + return PlatformViewLink( + viewType: 'plugins.flutter.io/mapbox_gl', + surfaceFactory: ( + BuildContext context, + PlatformViewController controller, + ) { + return AndroidViewSurface( + controller: controller as AndroidViewController, + gestureRecognizers: gestureRecognizers ?? + const >{}, + hitTestBehavior: PlatformViewHitTestBehavior.opaque, + ); + }, + onCreatePlatformView: (PlatformViewCreationParams params) { + final SurfaceAndroidViewController controller = + PlatformViewsService.initSurfaceAndroidView( + id: params.id, + viewType: 'plugins.flutter.io/mapbox_gl', + layoutDirection: TextDirection.ltr, + creationParams: creationParams, + creationParamsCodec: const StandardMessageCodec(), + onFocus: () => params.onFocusChanged(true), + ); + controller.addOnPlatformViewCreatedListener( + params.onPlatformViewCreated, + ); + controller.addOnPlatformViewCreatedListener( + onPlatformViewCreated, + ); + + controller.create(); + return controller; + }, + ); + } else { + return AndroidView( + viewType: 'plugins.flutter.io/mapbox_gl', + onPlatformViewCreated: onPlatformViewCreated, + gestureRecognizers: gestureRecognizers, + creationParams: creationParams, + creationParamsCodec: const StandardMessageCodec(), + ); + } } else if (defaultTargetPlatform == TargetPlatform.iOS) { return UiKitView( viewType: 'plugins.flutter.io/mapbox_gl',