Skip to content

Crash when exiting a map view #462

@ynibling

Description

@ynibling

Platforms

Android

Version of flutter-maplibre

0.3.3+2

Bug Description

I have a list of different routes. When I click on one of them, I create a new widget containing Maplibre and display the route based on a GeoJson.

With go_router (16.3.0), I have a return route to go back to the list of routes. When I click on the return button, the application crashes.

I am using Android Studio to emulate an Android phone.
I tried with an without the filter option and the application crashes in both cases.
I tried with maplibre 0.3.2 and the back button works fine without crashing.

Steps to Reproduce

  1. Create a list with ListenableBuilder widget
  2. Add a route to each item
  3. Click on one of them
  4. Display a route and a back button
  5. Click on the back button

Expected Results

After clicking the back button, the list of routes is displayed again.

Actual Results

The application crashes with this error:

`F/libc    ( 6324): Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 in tid 6541 (TextureViewRend), pid 6324 (_test_graphique)
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'google/sdk_gphone64_x86_64/emu64xa:16/BE2A.250530.026.D1/13818094:user/release-keys'
Revision: '0'
ABI: 'x86_64'
Timestamp: 2026-01-25 14:26:04.165806900+0000
Process uptime: 262s
Cmdline: com.example.flutter_test_graphique
pid: 6324, tid: 6541, name: TextureViewRend  >>> com.example.flutter_test_graphique <<<
uid: 10222
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0000000000000000
Cause: null pointer dereference
    rax 0000000000000000  rbx 00007fe6d7af77d0  rcx 0000000000000001  rdx 0000000000000002
    r8  00007fe5591293d8  r9  0000000000000001  r10 00000000fffff800  r11 00007fe7fb8f0948
    r12 00007fe559129680  r13 00007fe707be3c48  r14 00007fe6d7af7920  r15 0000000000000000
    rdi 0000000000000000  rsi 000000005c000000
    rbp 00007fe559129470  rsp 00007fe559129410  rip 00007fe559fd1fe7
30 total frames
backtrace:
      #00 pc 000000000052cfe7  /data/app/~~1C8kZuMdaSXL_xFhqh1cfA==/com.example.flutter_test_graphique-b4eGIQLcC1zxK10JCoaZrQ==/base.apk!libmaplibre.so (offset 0x3f9c000) (mbgl::android::MapRenderer::render(_JNIEnv&)+151) (BuildId: 742e9668ab0dc19724583c9f39b0aa23006b0ff2)
      #01 pc 00000000005300a3  /data/app/~~1C8kZuMdaSXL_xFhqh1cfA==/com.example.flutter_test_graphique-b4eGIQLcC1zxK10JCoaZrQ==/base.apk!libmaplibre.so (offset 0x3f9c000) (auto auto jni::MakeNativeMethod<auto jni::NativeMethodMaker<void (auto jni::NativePeerMemberFunctionMethod<void (mbgl::android::MapRenderer::*)(_JNIEnv&), &mbgl::android::MapRenderer::render(_JNIEnv&)>::operator()<mbgl::android::MapRenderer, mbgl::android::MapRenderer, void>(jni::Field<mbgl::android::MapRenderer, long> const&)::'lambda'(_JNIEnv&, jni::Object<mbgl::android::MapRenderer>&)::*)(_JNIEnv&, jni::Object<mbgl::android::MapRenderer>&) const>::operator()<auto jni::NativePeerMemberFunctionMethod<void (mbgl::android::MapRenderer::*)(_JNIEnv&), &mbgl::android::MapRenderer::render(_JNIEnv&)>::operator()<mbgl::android::MapRenderer, mbgl::android::MapRenderer, void>(jni::Field<mbgl::android::MapRenderer, long> const&)::'lambda'(_JNIEnv&, jni::Object<mbgl::android::MapRenderer>&)>(char const*, auto jni::NativePeerMemberFunctionMethod<void (mbgl::android::MapRenderer::*)(_JNIEnv&), &mbgl::android::MapRenderer::render(_JNIEnv&)>::operator()<mbgl::android::MapRenderer, mbgl::android::MapRenderer, void>(jni::Field<mbgl::android::MapRenderer, long> const&)::'lambda'(_JNIEnv&, jni::Object<mbgl::android::MapRenderer>&) const&)::'lambda'(_JNIEnv*, jni::jobject*)>(char const*, char const*, auto jni::NativePeerMemberFunctionMethod<void (mbgl::android::MapRenderer::*)(_JNIEnv&), &mbgl::android::MapRenderer::render(_JNIEnv&)>::operator()<mbgl::android::MapRenderer, mbgl::android::MapRenderer, void>(jni::Field<mbgl::android::MapRenderer, long> const&)::'lambda'(_JNIEnv&, jni::Object<mbgl::android::MapRenderer>&) const&, std::__ndk1::enable_if<std::is_class<auto jni::NativePeerMemberFunctionMethod<void (mbgl::android::MapRenderer::*)(_JNIEnv&), &mbgl::android::MapRenderer::render(_JNIEnv&)>::operator()<mbgl::android::MapRenderer, mbgl::android::MapRenderer, void>(jni::Field<mbgl::android::MapRenderer, long> const&)::'lambda'(_JNIEnv&, jni::Object<mbgl::android::MapRenderer>&)>::value, void>::type*)::'lambda'(_JNIEnv*, auto...)::__invoke<jni::jobject*>(_JNIEnv*, auto...)+35) (BuildId: 742e9668ab0dc19724583c9f39b0aa23006b0ff2)
      #02 pc 000000000022c8cb  /apex/com.android.art/lib64/libart.so (art_quick_generic_jni_trampoline+219) (BuildId: 108a321dc93c20580e74b855c407e975)
      #03 pc 0000000000211e94  /apex/com.android.art/lib64/libart.so (art_quick_invoke_stub+756) (BuildId: 108a321dc93c20580e74b855c407e975)
      #04 pc 00000000004621c5  /apex/com.android.art/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+181) (BuildId: 108a321dc93c20580e74b855c407e975)
      #05 pc 00000000005fa142  /apex/com.android.art/lib64/libart.so (bool art::interpreter::DoCall<false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, bool, art::JValue*)+2402) (BuildId: 108a321dc93c20580e74b855c407e975)
      #06 pc 0000000000233611  /apex/com.android.art/lib64/libart.so (void art::interpreter::ExecuteSwitchImplCpp<false>(art::interpreter::SwitchImplContext*)+10625) (BuildId: 108a321dc93c20580e74b855c407e975)
      #07 pc 0000000000210915  /apex/com.android.art/lib64/libart.so (ExecuteSwitchImplAsm+5) (BuildId: 108a321dc93c20580e74b855c407e975)
      #08 pc 00000000001bf494  <anonymous:7fe4e1cf9000> (org.maplibre.android.maps.renderer.MapRenderer.onDrawFrame+0)
      #09 pc 00000000005f2082  /apex/com.android.art/lib64/libart.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool, bool) (.__uniq.112435418011751916792819755956732575238)+466) (BuildId: 108a321dc93c20580e74b855c407e975)
      #10 pc 00000000005f9396  /apex/com.android.art/lib64/libart.so (art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+102) (BuildId: 108a321dc93c20580e74b855c407e975)
      #11 pc 00000000005fa128  /apex/com.android.art/lib64/libart.so (bool art::interpreter::DoCall<false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, bool, art::JValue*)+2376) (BuildId: 108a321dc93c20580e74b855c407e975)
      #12 pc 0000000000233611  /apex/com.android.art/lib64/libart.so (void art::interpreter::ExecuteSwitchImplCpp<false>(art::interpreter::SwitchImplContext*)+10625) (BuildId: 108a321dc93c20580e74b855c407e975)
      #13 pc 0000000000210915  /apex/com.android.art/lib64/libart.so (ExecuteSwitchImplAsm+5) (BuildId: 108a321dc93c20580e74b855c407e975)
      #14 pc 00000000001c3348  <anonymous:7fe4e1cf9000> (org.maplibre.android.maps.renderer.textureview.TextureViewMapRenderer.onDrawFrame+0)
      #15 pc 00000000005f2082  /apex/com.android.art/lib64/libart.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool, bool) (.__uniq.112435418011751916792819755956732575238)+466) (BuildId: 108a321dc93c20580e74b855c407e975)
      #16 pc 00000000005f9396  /apex/com.android.art/lib64/libart.so (art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+102) (BuildId: 108a321dc93c20580e74b855c407e975)
      #17 pc 00000000005fa128  /apex/com.android.art/lib64/libart.so (bool art::interpreter::DoCall<false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, bool, art::JValue*)+2376) (BuildId: 108a321dc93c20580e74b855c407e975)
      #18 pc 0000000000233611  /apex/com.android.art/lib64/libart.so (void art::interpreter::ExecuteSwitchImplCpp<false>(art::interpreter::SwitchImplContext*)+10625) (BuildId: 108a321dc93c20580e74b855c407e975)
      #19 pc 0000000000210915  /apex/com.android.art/lib64/libart.so (ExecuteSwitchImplAsm+5) (BuildId: 108a321dc93c20580e74b855c407e975)
      #20 pc 00000000001c2ef4  <anonymous:7fe4e1cf9000> (org.maplibre.android.maps.renderer.textureview.GLTextureViewRenderThread.run+0)
      #21 pc 00000000005f2082  /apex/com.android.art/lib64/libart.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool, bool) (.__uniq.112435418011751916792819755956732575238)+466) (BuildId: 108a321dc93c20580e74b855c407e975)
      #22 pc 00000000009a861b  /apex/com.android.art/lib64/libart.so (artQuickToInterpreterBridge+907) (BuildId: 108a321dc93c20580e74b855c407e975)
      #23 pc 000000000022ca5c  /apex/com.android.art/lib64/libart.so (art_quick_to_interpreter_bridge+140) (BuildId: 108a321dc93c20580e74b855c407e975)
      #24 pc 0000000000211e94  /apex/com.android.art/lib64/libart.so (art_quick_invoke_stub+756) (BuildId: 108a321dc93c20580e74b855c407e975)
      #25 pc 00000000004621c5  /apex/com.android.art/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+181) (BuildId: 108a321dc93c20580e74b855c407e975)
      #26 pc 00000000008f42ec  /apex/com.android.art/lib64/libart.so (art::Thread::CreateCallback(void*)+1388) (BuildId: 108a321dc93c20580e74b855c407e975)
      #27 pc 00000000008f3d78  /apex/com.android.art/lib64/libart.so (art::Thread::CreateCallbackWithUffdGc(void*)+8) (BuildId: 108a321dc93c20580e74b855c407e975)
      #28 pc 000000000007ac5f  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+63) (BuildId: fcb82240218d1473de1e3d2137c0be35)
      #29 pc 000000000006d94d  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+61) (BuildId: fcb82240218d1473de1e3d2137c0be35)
Lost connection to device.`

Code Sample

The widget I use to contain maplibre

import 'package:flutter/material.dart';
import 'package:flutter_test_graphique/graphic/ui/profil/itineraire_enregistre_map_viewmodel.dart';
import 'package:flutter_test_graphique/tools/line_painter.dart';
import 'package:flutter_test_graphique/tools/mpa_libre/dynamic_map_libre.dart';
import 'package:go_router/go_router.dart';

class ItineraireEnregistreMap extends StatefulWidget {
  const ItineraireEnregistreMap({super.key, required this.viewModel});

  final ItineraireEnregistreMapViewmodel viewModel;

  @override
  State<ItineraireEnregistreMap> createState() => _ItineraireEnregistreMapState();
}

class _ItineraireEnregistreMapState extends State<ItineraireEnregistreMap> {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Padding(
          padding: EdgeInsets.zero,
          child: SizedBox(
            height: 50,
            child: Row(
              children: [
                GestureDetector(
                onTap: () {
                  context.go('/profil/itineraires_enregistres');
                },
                child: Icon(Icons.arrow_back_ios),
                ),
                Text('Mon itinéraire'),
              ],
            ),
          ),
        ),
        CustomPaint(
          painter: LinePainter(startPoint: Offset(0, 0), endPoint: Offset(500, 0), tailleTrait: 0.2),
        ), 
        ListenableBuilder(
          listenable: widget.viewModel.loadGeoJSON,
          builder: (context, _) {
            if (widget.viewModel.loadGeoJSON.running == true) {
              return const Center(child: CircularProgressIndicator());
            } else {
              return Expanded(
                child: DynamicMap(viewModel: widget.viewModel),
              );
            }
          },
        ),
      ],
    );
  }
}

The maplibre widget:

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_test_graphique/graphic/ui/profil/itineraire_enregistre_map_viewmodel.dart';
import 'package:maplibre/maplibre.dart';

class DynamicMap extends StatefulWidget {
  const DynamicMap({super.key, required this.viewModel});

  final ItineraireEnregistreMapViewmodel viewModel;

  @override
  State createState() => DynamicMapState();
}

class DynamicMapState extends State<DynamicMap> {
  late final MapController _mapController;
  static const String _geojsonSourceId = 'geojson-source';

  Future<void> addGeoJsonSourceAndLayers(StyleController style) async {

    final String geoJsonData = widget.viewModel.geoJSON!;

    final geoJson = GeoJsonSource(
          id: _geojsonSourceId,
          data: geoJsonData,
    );

    await style.addSource(geoJson);

    const lineLayerOutside = LineStyleLayer(
      id: 'itinerary_border',
      sourceId: _geojsonSourceId,
      paint: {
        'line-color': '#ffffff',
        'line-width': [
          'interpolate',
          [
            'exponential',
            0.5
          ],
          [
            'zoom'
          ],
          10,
          12,
          18,
          6
        ],
      },
      layout: {
        'line-join': 'round',
        'line-cap': 'round',
      },
       filter: [
         '==',
         [
           'get',
           'type'
         ],
         'stage'
       ],
    );
    
    const lineLayerInside = LineStyleLayer(
        id: 'line_border', 
        sourceId: _geojsonSourceId,
         filter: [
           '==',
           [
             'get',
             'type'
           ],
           'stage'
         ],
        paint: {
          'line-color': '#ff0000',
          'line-width':  [
            'interpolate',
            [
              'exponential',
              0.5
            ],
            [
              'zoom'
            ],
            10,
            8,
            18,
            4
          ],
        },
        layout: {
          'line-join': 'round',
          'line-cap': 'round',
        }
      );

    const symbolLayerWaypoint = SymbolStyleLayer(
      id: 'itinerary_waypoint_label',
      sourceId: _geojsonSourceId,
       filter: [
         'all',
         [
           '==',
           [
             'get',
             'type'
           ],
           'waypoint'
         ],
         [
           '!=',
           [
             'get',
             'role'
           ],
           'via'
         ]      
       ],
      paint: {
        'text-color': '#ffffff',
      },
      layout: {
        'text-field': [
          'to-string',
          [
            'get',
            'number'
          ]
        ],
        'text-font':  [
          'Noto Sans Bold'
        ],
        'text-size': 12,
        'text-anchor': 'center',
        'text-allow-overlap': true,
      }
    );

    const circleLayerWaypoint = CircleStyleLayer(
      id: 'itinerary_waypoint_circle', 
      sourceId: _geojsonSourceId,
       filter: [
           '==',
           [
             'get',
             'type'
           ],
           'waypoint'
       ],
      paint: {
        'circle-radius': 10,
        'circle-color': [
          'case',
          [
            '==',
            [
              'get',
              'role'
            ],
            'start'
          ],
          '#038003',
          [
            '==',
            [
              'get',
              'role'
            ],
            'end'
          ],
          '#a10202',
          '#1c1c1e'
        ],
        'circle-stroke-color': '#FFF',
        'circle-stroke-width': [
          'case',
          [
            '!=',
            [
              'get',
              'role'
            ],
            'via'
          ],
          3,
          0
        ],
      },
    );

    await style.addLayer(lineLayerOutside);
    await style.addLayer(lineLayerInside);
    await style.addLayer(circleLayerWaypoint);
    await style.addLayer(symbolLayerWaypoint, aboveLayerId: 'itinerary_waypoint_circle');
  }

  Future<void> setUserCurrentLocation () async {

    await _mapController.enableLocation();
    _mapController.trackLocation(trackLocation: true, trackBearing: BearingTrackMode.gps);


    final itinineraireProprietesData = widget.viewModel.itinineraireProprietes!;
    final itinineraireProprietesJsonResult = jsonDecode(itinineraireProprietesData);

    final List<dynamic> bbox = itinineraireProprietesJsonResult['bbox'];
    final LngLatBounds bounds = LngLatBounds(longitudeWest: bbox[0], longitudeEast: bbox[2], latitudeSouth: bbox[1], latitudeNorth: bbox[3]);

    _mapController.fitBounds(bounds: bounds, padding: EdgeInsets.all(200));

  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: MapLibreMap(
        options: MapOptions(
          initZoom: 5,
          initStyle: 'https://api.maptiler.com/maps/XXXXX,
        ),
        onMapCreated: (controller) {
          _mapController = controller;
        },
        onStyleLoaded: (style) async {

          debugPrint('Map loaded 😎');
          await addGeoJsonSourceAndLayers(style);
          await setUserCurrentLocation();

        },
      ),
    );
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    androidAndroid specific issuebugSomething isn't working

    Projects

    Status

    Done

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions