Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

[iOS] SymbolLayer silently dropped from style when textFont is set

未关闭
#1,122 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

评估

难度
4/5
预计耗时
3-5 天
新手友好度
45/100
Issue 类型
缺陷
描述清晰度
基本清楚
活跃度
冷清
技术栈
dart, flutter, ios
领域
mobile

调研方向

从 _onStyleLoaded 复现和 map.style.addLayer 调用开始,然后将 iOS 对 SymbolLayer.textFont 的处理与所描述的 Android 行为进行比较。运行提供的 flutter run 命令,并验证在标签使用回退字体渲染时,通过立即和延迟的 getLayer 调用都仍能获取该图层。

由索引模型根据 Issue 内容生成。

描述

Summary

On iOS, setting SymbolLayer.textFont causes the entire layer to be silently dropped from the rendered style. The Dart-side addLayer call returns success, but the layer never persists in the style. Visual: the labels never render. Programmatic: subsequent calls to setStyleLayerProperty against that layer id throw PlatformException(0, "Layer ... is not in style").

Android renders the same SymbolLayer correctly with textFont set, on the same mapbox_maps_flutter versions. Android falls back to its default sans-serif when the requested font isn't bundled (I/Mbgl-FontUtils: Couldn't map font family for local ideograph, using sans-serif instead), but the layer renders.

This is the same failure pattern as the lineDasharray LineLayer bug (filed separately) and exhibits the same symptom mutation across versions:

Version Symptom
2.12.0 addLayer returns ok → style.getLayer(id) returns null immediately → labels do not render
2.21.1 addLayer returns ok → style.getLayer(id) returns the layer at the immediate audit → layer disappears from the style a few milliseconds later → labels do not render

The pattern of silent failure is identical for both bugs, suggesting a shared underlying defect in the iOS-side layer-add path that's specific to certain layout properties.

Affected versions

  • mapbox_maps_flutter 2.12.0 — confirmed broken (Sep 2024 era)
  • mapbox_maps_flutter 2.21.1 — confirmed broken with mutated symptom (current latest at time of filing)

Tested on:

  • iPhone 17, iOS 26.3.1, Xcode 26.3
  • Compared against: moto g play 2024, Android 14 (API 34) — works correctly on both versions (with sans-serif fallback)

Flutter:

  • Original: 3.24.5 / Dart 3.5.4
  • Retest: 3.41.6

Minimal reproduction

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart';

void main() {
  MapboxOptions.setAccessToken(const String.fromEnvironment('MAPBOX_TOKEN'));
  runApp(const MaterialApp(home: BugRepro()));
}

class BugRepro extends StatefulWidget {
  const BugRepro({super.key});
  @override
  State<BugRepro> createState() => _BugReproState();
}

class _BugReproState extends State<BugRepro> {
  static const _sourceId = 'repro-source';
  static const _layerId = 'repro-symbol-layer';

  @override
  Widget build(BuildContext context) => Scaffold(
        body: MapWidget(
          styleUri: MapboxStyles.SATELLITE_STREETS,
          cameraOptions: CameraOptions(
            center: Point(coordinates: Position(-122.4885, 37.6244)),
            zoom: 15,
          ),
          onStyleLoadedListener: (_) => _onStyleLoaded(),
          onMapCreated: (map) => _map = map,
        ),
      );

  MapboxMap? _map;

  Future<void> _onStyleLoaded() async {
    final map = _map;
    if (map == null) return;

    // A few Point features with a `label` property to render as text.
    final geojson = jsonEncode({
      'type': 'FeatureCollection',
      'features': [
        for (var i = 1; i <= 5; i++)
          {
            'type': 'Feature',
            'properties': {'label': '$i'},
            'geometry': {
              'type': 'Point',
              'coordinates': [-122.4885 + i * 0.0005, 37.6244],
            },
          },
      ],
    });

    await map.style.addSource(GeoJsonSource(id: _sourceId, data: geojson));

    // The bug: setting textFont causes the SymbolLayer to silently
    // disappear on iOS. Comment out the textFont line below and the
    // labels render correctly (in the Mapbox default font).
    await map.style.addLayer(SymbolLayer(
      id: _layerId,
      sourceId: _sourceId,
      textFieldExpression: ['get', 'label'],
      textSize: 14.0,
      textColor: 0xFFFFFFFF,
      textHaloColor: 0xFF000000,
      textHaloWidth: 1.5,
      textAllowOverlap: true,
      textFont: const ['DIN Pro Bold', 'Arial Unicode MS Bold'], // ← REMOVE → bug goes away
    ));

    // Diagnostic — same pattern as the lineDasharray bug:
    final immediate = await map.style.getLayer(_layerId);
    debugPrint('layer present immediately? ${immediate != null}');

    await Future<void>.delayed(const Duration(milliseconds: 100));
    final delayed = await map.style.getLayer(_layerId);
    debugPrint('layer present after 100ms delay? ${delayed != null}');
  }
}

Run with:

flutter run -d <ios-device> --dart-define=MAPBOX_TOKEN=pk.xxx

Expected behavior

The SymbolLayer is added to the style, the labels render on the map (in DIN Pro Bold if the glyph set is available, falling back to a default font if not), and subsequent getLayer(id) calls return the layer. This is what happens on Android with the exact same code.

We don't expect Mapbox to ship the DIN Pro Bold font itself — the textFont array is conventionally a fallback chain, and we'd expect either successful resolution to a fallback font OR a clear error at add time. What we get instead is the entire layer silently disappearing with no error and no callback.

Actual behavior

On iOS, with textFont set to ANY value (we tested ['DIN Pro Bold', 'Arial Unicode MS Bold'], ['Arial Unicode MS Bold'], and ['Open Sans Regular'] — all three reproduce):

  • The labels never render visually.
  • On 2.12.0: the layer present immediately? debug print outputs false.
  • On 2.21.1: the layer present immediately? print outputs true, but layer present after 100ms delay? prints false.

Workaround

Don't set textFont on SymbolLayer cross-platform. The labels will render in the Mapbox default font for the active style — for SATELLITE_STREETS this is a Mapbox-hosted sans-serif which is visually acceptable but does not match the iOS native app's typeface choice.

If matching a specific custom font is a hard requirement: bundle the font as a local glyph PBF set and use style.setStyleGlyphURL(url) instead of the per-layer textFont property. This appears to be the only currently-working path for custom fonts on iOS.

Why the symptom mutation matters

Same as for the related lineDasharray bug filed separately: the audit pattern (getLayer after addLayer) that catches this in user code on 2.12.0 silently passes on 2.21.1, even though the bug is still present. Whatever change was made in the intervening 9 months made the failure asynchronous. Code that defends against the original bug shape no longer detects the new one.

Filing both bugs together (cross-referenced) so a fix can address the underlying cause rather than chasing the surface symptoms one property at a time.

Related

Same set as for the lineDasharray issue:

  • #568 — Adding GeoJSON Layer in onMapCreated is unreliable
  • #527 — getLayer("puck") throws "Layer puck is not in style"

Spike report context

This issue was filed during a Flutter migration spike for an existing dual-native (Swift + Kotlin) app, alongside the related LineLayer.lineDasharray issue. Both bugs prevent 1:1 visual parity with the native iOS Mapbox SDK. Full report: https://github.com/oshelot/Caddie-AI/blob/kan-252-flutter-spike/flutter-spike/SPIKE_REPORT.md

Happy to provide additional logs, a smaller repro, or test on additional iOS versions if helpful.

主要语言
Dart
星标
380
派生
204
PR 合并指标
30 天内没有已合并 PR

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

mapbox/mapbox-maps-flutter 的其他 Issue

查看 mapbox/mapbox-maps-flutter 的全部 Issue

相似的 Issue

更多 Dart Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。