Select to view content in your preferred language

background maps not showing on older android systems.

1180
8
Jump to solution
01-15-2026 06:15 AM
Labels (1)
RasmusLind
Emerging Contributor

as the subject suggests i have a problem on the app i have made with the backgroundmaps not showing on older models. The background maps comes from a group on an agol organisation https://danmarksdata.maps.arcgis.com/home/group.html?sortField=modified&sortOrder=desc&id=1a2896d741.... i use http calls to the rest api to find the items in the group and then uses: 

Basemap.withItem(basemapitem.results[0]);
to make the Basemap object.

the newest version of android where is fails has been android 13 api 33-ext5.

has any one else experienced something like this and has a solution to this. 

0 Kudos
1 Solution

Accepted Solutions
HarishK
Esri Contributor

Short answer
This is a TLS trust‑store issue on older Android builds. Your basemaps ultimately hit https://services.datafordeler.dk, whose current certificate chain (Sectigo “Public Server Authentication” hierarchy) isn’t in the system trust store on some older devices, so Android rejects the handshake. On newer OS versions the chain is trusted, so everything works. [sectigo.com], [developer....ndroid.com]

The ArcGIS Maps SDK for Flutter surfaces this as a NetworkAuthenticationChallenge. Handle the ServerTrustAuthenticationChallenge and explicitly trust that host at runtime.


Why it happens on older Android only

  • Android’s certificate trust store is part of the OS and evolves with releases. If a root/intermediate CA isn’t present (or has changed due to industry migrations), TLS handshakes fail even when the server is correctly configured. [developer....ndroid.com]
  • Sectigo has been migrating issuance to newer single‑purpose roots (e.g., R46/E46). Devices that haven’t received updated root stores may not recognize these chains until updated or handled in-app. [sectigo.com]

App‑level fix: trust the host when challenged

The SDK lets you register a NetworkAuthenticationChallengeHandler. When the device doesn’t trust a server cert, you’ll receive a ServerTrustAuthenticationChallenge; respond with a ServerTrustNetworkCredential to trust just that host and retry the request.

Scope & safety: This trusts only the host you specify and only inside your app (not system‑wide). It’s a standard, supported workflow in the ArcGIS Maps SDK. [developers...arcgis.com]

Code (Flutter / Dart):

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:arcgis_maps/arcgis_maps.dart';

// 1) (Optional) Persist network credentials across launches.
Future<void> initAuthStores() async {
  await ArcGISEnvironment.authenticationManager.setNetworkCredentialStore(
    await NetworkCredentialStore.initPersistentStore(),
  ); // persists in Android Keystore / iOS Keychain
}

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await initAuthStores();
  // Set your API key here...
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) => const MaterialApp(home: MapPage());
}

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

class _MapPageState extends State<MapPage>
    implements NetworkAuthenticationChallengeHandler {

  @override
  void initState() {
    super.initState();
    // 2) Register this instance as the global network challenge handler.
    ArcGISEnvironment.authenticationManager.networkAuthenticationChallengeHandler = this;
  }

  @override
  FutureOr<void> handleNetworkAuthenticationChallenge(
      NetworkAuthenticationChallenge challenge) {

    // 3) Trust services.datafordeler.dk only when the OS doesn't trust its cert.
    if (challenge is ServerTrustAuthenticationChallenge &&
        challenge.host == 'services.datafordeler.dk') {
      challenge.continueWithCredential(
        ServerTrustNetworkCredential.forChallenge(challenge),
      );
      return;
    }

    // For other challenges (Basic/Digest/NTLM/Client cert), handle as needed
    // or fail so the error surfaces:
    challenge.continueAndFail();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ArcGISMapView(
        controllerProvider: () => ArcGISMapView.createController()
          ..arcGISMap = ArcGISMap.withBasemapStyle(BasemapStyle.arcGISTopographic),
      ),
    );
  }
}
  • ServerTrustAuthenticationChallenge is raised when the server cert is untrusted but the app can choose to proceed. [pub.dev]
  • ServerTrustNetworkCredential tells the SDK to trust that host and retry; the credential is stored so subsequent requests to that host succeed. If you initialized a persistent NetworkCredentialStore, it’s kept across app restarts.

If you use a toolkit-based authenticator instead of handling the API directly, the toolkit also includes UI and handling for server‑trust prompts. [developers...arcgis.com]

View solution in original post

8 Replies
HarishK
Esri Contributor

This kind of “basemap shows on some devices but not others” issue with Flutter + ArcGIS usually boils down to one of a few culprits:

  • cleartext/HTTP being blocked on newer Android
  • item accessibility/auth
  • load error coming from a specific basemap/layer (e.g., vector tile style referencing blocked resources)

Quick things to verify first

1. Are you using http anywhere? 

On Android 9+ (API 28+), cleartext (http) is blocked by default. If you call the REST API over http or if the basemap’s service URL is `http`, Android 13 will refuse to load it.

Fix: Use https everywhere for:

  • REST item searches
  • Service URLs inside the Portal Items (item data can reference layers that are http even if the item page is https).
  • Temporary debug workaround (not recommended for prod):
<!-- AndroidManifest.xml -->
        <application
            android:usesCleartextTraffic="true"
            android:networkSecurityConfig="@xml/network_security_config" .../>

 

<!-- res/xml/network_security_config.xml -->
        <network-security-config>
          <base-config cleartextTrafficPermitted="true" />
        </network-security-config>
  • If this makes basemaps appear, you’ve confirmed a cleartext/HTTP issue. Switch to HTTPS URLs in items and remove this config for production.

2. Check the actual load error:

Wrap loads in try/catch and print loadError.message and loadError.additionalInfo. With the ArcGIS Flutter plugin (which wraps the native SDK), you can do:

final item = PortalItem.withPortalAndItemId(portal: dataMarksPortal , itemId: dataMarksBasemapId);
    await item.load();
    if (item.loadStatus != LoadStatus.loaded) {
      print('PortalItem failed: ${item.loadError?.message}');
    }

    final basemap = Basemap.withItem(item);
    await basemap.load();
    if (basemap.loadStatus != LoadStatus.loaded) {
      print('Basemap failed: ${basemap.loadError?.message}');
    }

 

You’ll often see something explicit like “CLEARTEXT not permitted”, “401/403”, or a TLS/certificate error.


0 Kudos
RasmusLind
Emerging Contributor

hello Harish.

 

i allready use Https everywhere as it is an AGOL organisation. 

i have tried the suggested error handling to check for load error, and i get no load error. 

i have included a screenshot to show the problem as you can see the basemaps are loaded in the app they just show up as blanks with used in the app. 

It work in newer versions. 

if i need to add more information please just say what you need 🙂 

best regards Rasmus 

 

0 Kudos
HarishK
Esri Contributor

Thanks for the extra details and the screenshot, Rasmus – that’s helpful.

To dig deeper we’ll need a small reproducible example and some device specifics, so we can try to reproduce this on our side. Could you share:

1. Minimal code sample
- A pared-down widget or sample app that:
- Creates the Basemap from your AGOL group (including the Basemap.withItem(basemapItem.results[0]) part).
- Sets up the ArcGISMap / ArcGISMapView exactly as you do in your app.
- If possible, please strip it down to the smallest example that still shows the “blank basemap on older devices” behavior.

2. Device and OS details
For at least one device where it fails and one where it works:
- Device model and manufacturer (e.g. Samsung A52, Pixel 4a, etc.).
- Android version (e.g. Android 13, API 33-ext5) and whether it’s physical or emulator.
- CPU architecture (ARM64 vs x86, if you know it).
- App build type (debug/release) and whether you see the issue in both.

3. SDK / tooling versions
- ArcGIS Maps SDK for Flutter version.
- Flutter version (flutter --version).
- Your app’s minSdkVersion, targetSdkVersion, and compileSdkVersion.

Once we have a minimal repro and those details, we can try to reproduce this under the same conditions and get back to you with further updates.

0 Kudos
RasmusLind
Emerging Contributor

1. i have attached a file with code

2. Samsung s20 Ultra, Android version 13

i run a virtuel device on android 13-0 pixel 7 where the error also happens .

 it work on a device that runs android 16 

i see the issue in both the debug version and the released version 

3. flutter sdk version arcgis_maps: ^200.8.0+4672 

flutter version 3.38.5

minsdk 28,

  targetSDKversion = targetSdk = flutter.targetSdkVersion i think it is  version 34,

compiledSdk is 36  

0 Kudos
HarishK
Esri Contributor

Hi — thanks for the detailed report and for sharing the AGOL group you’re using.

I investigated this on my side and was able to reproduce the issue on older Android devices. The basemaps fail to draw, and the runtime repeatedly reports 401 authentication errors when it tries to access layers hosted on:

services.datafordeler.dk

On newer Android versions the same WebMaps load without errors, which initially seems odd — but there is a likely explanation:

Why it works on newer devices but not older ones

Older Android builds often ship with outdated or missing CA root certificates.
If the certificate chain for services.datafordeler.dk isn’t trusted on that OS version, the TLS handshake can fail or degrade in a way that results in authentication failures (401) instead of a clean SSL error. Newer devices include updated CA bundles, so the same service appears to work even without explicitly handling authentication.

Since I don’t have access to your organization’s Datafordeler certificates or authentication setup, I can’t reproduce a successful connection here — but the behavior I’m seeing is consistent with a CA trust issue combined with the service requiring credentials.

What I saw on my devices

I tested on:

  • Samsung S24 Android 16
  • Samsung Tab Android 9

I’m attaching the Logcat output from both runs. The Android 9 device shows repeated authentication errors, while the newer device does not.

What would help next

To confirm that the issue on your device matches what I’m seeing, could you please share:

A Logcat capture from one of the older devices where the basemap fails to load
(from app start → first map draw)

Your Logcat will tell us whether:

  • your device is also hitting 401s,
  • or whether it’s a certificate‑trust problem unique to your environment,
  • or something else entirely.

Once we have that, we can determine whether the fix is:

  • installing the missing CA root(s),
  • configuring the Datafordeler service credentials inside the app,
  • or handling the authentication challenge explicitly through ArcGIS Runtime.

Happy to continue helping — just attach the Logcat and we’ll go from there.

RasmusLind
Emerging Contributor

Hi Agian 

sorry for the long silence. 

i have the log here, looks like a auth error as you talk about 

0 Kudos
HarishK
Esri Contributor

Short answer
This is a TLS trust‑store issue on older Android builds. Your basemaps ultimately hit https://services.datafordeler.dk, whose current certificate chain (Sectigo “Public Server Authentication” hierarchy) isn’t in the system trust store on some older devices, so Android rejects the handshake. On newer OS versions the chain is trusted, so everything works. [sectigo.com], [developer....ndroid.com]

The ArcGIS Maps SDK for Flutter surfaces this as a NetworkAuthenticationChallenge. Handle the ServerTrustAuthenticationChallenge and explicitly trust that host at runtime.


Why it happens on older Android only

  • Android’s certificate trust store is part of the OS and evolves with releases. If a root/intermediate CA isn’t present (or has changed due to industry migrations), TLS handshakes fail even when the server is correctly configured. [developer....ndroid.com]
  • Sectigo has been migrating issuance to newer single‑purpose roots (e.g., R46/E46). Devices that haven’t received updated root stores may not recognize these chains until updated or handled in-app. [sectigo.com]

App‑level fix: trust the host when challenged

The SDK lets you register a NetworkAuthenticationChallengeHandler. When the device doesn’t trust a server cert, you’ll receive a ServerTrustAuthenticationChallenge; respond with a ServerTrustNetworkCredential to trust just that host and retry the request.

Scope & safety: This trusts only the host you specify and only inside your app (not system‑wide). It’s a standard, supported workflow in the ArcGIS Maps SDK. [developers...arcgis.com]

Code (Flutter / Dart):

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:arcgis_maps/arcgis_maps.dart';

// 1) (Optional) Persist network credentials across launches.
Future<void> initAuthStores() async {
  await ArcGISEnvironment.authenticationManager.setNetworkCredentialStore(
    await NetworkCredentialStore.initPersistentStore(),
  ); // persists in Android Keystore / iOS Keychain
}

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await initAuthStores();
  // Set your API key here...
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) => const MaterialApp(home: MapPage());
}

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

class _MapPageState extends State<MapPage>
    implements NetworkAuthenticationChallengeHandler {

  @override
  void initState() {
    super.initState();
    // 2) Register this instance as the global network challenge handler.
    ArcGISEnvironment.authenticationManager.networkAuthenticationChallengeHandler = this;
  }

  @override
  FutureOr<void> handleNetworkAuthenticationChallenge(
      NetworkAuthenticationChallenge challenge) {

    // 3) Trust services.datafordeler.dk only when the OS doesn't trust its cert.
    if (challenge is ServerTrustAuthenticationChallenge &&
        challenge.host == 'services.datafordeler.dk') {
      challenge.continueWithCredential(
        ServerTrustNetworkCredential.forChallenge(challenge),
      );
      return;
    }

    // For other challenges (Basic/Digest/NTLM/Client cert), handle as needed
    // or fail so the error surfaces:
    challenge.continueAndFail();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ArcGISMapView(
        controllerProvider: () => ArcGISMapView.createController()
          ..arcGISMap = ArcGISMap.withBasemapStyle(BasemapStyle.arcGISTopographic),
      ),
    );
  }
}
  • ServerTrustAuthenticationChallenge is raised when the server cert is untrusted but the app can choose to proceed. [pub.dev]
  • ServerTrustNetworkCredential tells the SDK to trust that host and retry; the credential is stored so subsequent requests to that host succeed. If you initialized a persistent NetworkCredentialStore, it’s kept across app restarts.

If you use a toolkit-based authenticator instead of handling the API directly, the toolkit also includes UI and handling for server‑trust prompts. [developers...arcgis.com]

RasmusLind
Emerging Contributor

thanks for the help 

0 Kudos