Skip to content
Blog

Platform-Native Loading Experience with CircularProgressIndicator.adaptive in Flutter

Learn how to use CircularProgressIndicator.adaptive in Flutter to render platform-specific spinners automatically across iOS, Android, macOS, and Web.

Published on • November 9, 2026

AI Assistant

Building high-quality cross-platform applications requires respecting target platform design conventions. While standard Material design loading indicators look clean on Android, iOS users expect the native Apple-style Cupertino activity indicator tick-spinner.

Flutter simplifies platform-conforming UI elements by providing constructor variants such as CircularProgressIndicator.adaptive().

What is CircularProgressIndicator.adaptive?

CircularProgressIndicator.adaptive() is an adaptive constructor that automatically inspects the target runtime platform (Theme.of(context).platform or TargetPlatform):

  • iOS & macOS: Automatically instantiates a CupertinoActivityIndicator.
  • Android, Web, Windows & Linux: Instantiates a standard Material CircularProgressIndicator.
import 'package:flutter/material.dart';

class AdaptiveLoadingScreen extends StatelessWidget {
  const AdaptiveLoadingScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Adaptive Loading')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // Automatically switches indicator based on operating system
            const CircularProgressIndicator.adaptive(),
            const SizedBox(height: 16),
            Text(
              'Loading data for ${Theme.of(context).platform.name}...',
              style: TextStyle(color: Colors.grey.shade700),
            ),
          ],
        ),
      ),
    );
  }
}

Customizing Adaptive Colors and Sizes

CircularProgressIndicator.adaptive() accepts standard customization properties while maintaining native platform fidelity:

  • value: Optional progress ratio (0.0 to 1.0). If null, it renders an indeterminate spinning animation.
  • backgroundColor: On iOS/Cupertino, this maps to the track tint where applicable.
  • color & valueColor: Specifies the indicator tint color across both Material and Cupertino variants.
CircularProgressIndicator.adaptive(
  valueColor: AlwaysStoppedAnimation<Color>(Colors.indigo),
  strokeWidth: 3.0,
);

Building a Custom Adaptive Switcher Component

If you need even tighter control—such as passing Cupertino-specific radius parameters vs Material strokeWidth parameters—you can build a custom platform-switching helper function:

import 'package:flutter/foundation.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

Widget buildPlatformSpinner(BuildContext context, {Color? color}) {
  final platform = Theme.of(context).platform;

  if (platform == TargetPlatform.iOS || platform == TargetPlatform.macOS) {
    return CupertinoActivityIndicator(
      color: color,
      radius: 14.0,
    );
  }

  return CircularProgressIndicator(
    valueColor: color != null ? AlwaysStoppedAnimation<Color>(color) : null,
    strokeWidth: 3.0,
  );
}

Other Adaptive Flutter Widgets

CircularProgressIndicator.adaptive belongs to Flutter’s family of platform-adaptive widgets:

  • Switch.adaptive: Displays a native iOS toggle switch on Apple devices and a Material switch on Android.
  • Slider.adaptive: Renders native slider tracks tailored to platform design guidelines.
  • Checkbox.adaptive: Renders platform-matching checkbox indicators.
  • Radio.adaptive: Renders platform-conforming radio buttons.

Takeaway: Using .adaptive() constructors ensures your app respects platform design patterns seamlessly without duplicating widget tree logic.