Maps, Location & Device Features

Retrieving Device Information with device_info_plus

15 min Lesson 12 of 12

Retrieving Device Information with device_info_plus

Many production Flutter apps need to know what device they are running on — to tailor the user experience, report crashes accurately, enforce platform-specific licensing, or log diagnostics. The device_info_plus package exposes a clean, typed API that surfaces platform-specific metadata such as the device model, OS version, system name, and unique identifiers on both Android and iOS (as well as Web, Windows, macOS, and Linux).

Note: device_info_plus is the community-maintained successor to the original device_info plugin. Always use device_info_plus in new projects — the older package is deprecated and unmaintained.

Adding the Dependency

Add device_info_plus to your pubspec.yaml and run flutter pub get:

pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
  device_info_plus: ^10.1.0   # use the latest stable version

No extra Android permissions or iOS Info.plist entries are required for the basic metadata fields covered in this lesson.

Core API — DeviceInfoPlugin

The package's entry point is DeviceInfoPlugin. You create a single instance and call the appropriate platform getter, which returns a typed info object:

  • AndroidandroidInfo returns an AndroidDeviceInfo object
  • iOSiosInfo returns an IosDeviceInfo object
  • WebwebBrowserInfo returns a WebBrowserInfo object
  • Windows / macOS / Linux — corresponding typed objects are available too

Because each getter is an async call to the platform channel, you must await the result inside an async method or initState override.

Fetching Device Info (cross-platform safe)

import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart' show defaultTargetPlatform, TargetPlatform;

Future<Map<String, String>> fetchDeviceInfo() async {
  final plugin = DeviceInfoPlugin();
  final info = <String, String>{};

  if (defaultTargetPlatform == TargetPlatform.android) {
    final android = await plugin.androidInfo;
    info['Platform']    = 'Android';
    info['Model']       = android.model;
    info['Brand']       = android.brand;
    info['SDK Version'] = android.version.sdkInt.toString();
    info['OS Release']  = android.version.release;
    info['Device ID']   = android.id;
    info['Is Physical'] = android.isPhysicalDevice.toString();
  } else if (defaultTargetPlatform == TargetPlatform.iOS) {
    final ios = await plugin.iosInfo;
    info['Platform']      = 'iOS';
    info['Model']         = ios.model;
    info['Device Name']   = ios.name;
    info['System Name']   = ios.systemName;
    info['System Version']= ios.systemVersion;
    info['Identifier']    = ios.identifierForVendor ?? 'unavailable';
    info['Is Physical']   = ios.isPhysicalDevice.toString();
  }

  return info;
}
Tip: Use defaultTargetPlatform from package:flutter/foundation.dart instead of Platform.isAndroid from dart:io. The former works on all platforms (including Web); the latter throws on Web.

Key Fields Reference

Understanding what each field means helps you choose the right one for your use case:

  • Android — model: the marketing model name (e.g. Pixel 8 Pro)
  • Android — brand: the consumer-facing brand (e.g. google)
  • Android — version.sdkInt: the Android SDK level (e.g. 34 for Android 14)
  • Android — version.release: the human-readable OS version string (e.g. 14)
  • Android — id: a build fingerprint string; not a stable unique device ID
  • iOS — identifierForVendor: a UUID unique per app-vendor per device; resets on reinstall if no other apps from the same vendor are installed
  • iOS — utsname.machine: the hardware machine identifier (e.g. iPhone16,2)
Warning: There is no truly stable, permanent unique device ID available without special permissions on modern Android or iOS. identifierForVendor can change; ANDROID_ID can change after a factory reset. For analytics, prefer a server-generated UUID stored in secure storage rather than relying on hardware identifiers.

Building a Settings-Style Display Screen

A common pattern is to show device metadata on a diagnostics or "About this device" settings screen. The widget loads data asynchronously in initState and stores it in a Map that drives a ListView of ListTile rows.

DeviceInfoScreen — Complete Example

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

class DeviceInfoScreen extends StatefulWidget {
  const DeviceInfoScreen({super.key});

  @override
  State<DeviceInfoScreen> createState() => _DeviceInfoScreenState();
}

class _DeviceInfoScreenState extends State<DeviceInfoScreen> {
  Map<String, String> _deviceData = {};
  bool _isLoading = true;

  @override
  void initState() {
    super.initState();
    _loadDeviceInfo();
  }

  Future<void> _loadDeviceInfo() async {
    final plugin = DeviceInfoPlugin();
    final data = <String, String>{};

    try {
      if (defaultTargetPlatform == TargetPlatform.android) {
        final android = await plugin.androidInfo;
        data['Model']        = android.model;
        data['Brand']        = android.brand;
        data['Manufacturer'] = android.manufacturer;
        data['Android SDK']  = android.version.sdkInt.toString();
        data['OS Version']   = android.version.release;
        data['Security Patch'] = android.version.securityPatch ?? 'N/A';
        data['Is Physical']  = android.isPhysicalDevice ? 'Yes' : 'No';
      } else if (defaultTargetPlatform == TargetPlatform.iOS) {
        final ios = await plugin.iosInfo;
        data['Device Name']    = ios.name;
        data['Model']          = ios.model;
        data['Machine']        = ios.utsname.machine;
        data['System Name']    = ios.systemName;
        data['System Version'] = ios.systemVersion;
        data['Vendor ID']      = ios.identifierForVendor ?? 'N/A';
        data['Is Physical']    = ios.isPhysicalDevice ? 'Yes' : 'No';
      }
    } catch (e) {
      data['Error'] = e.toString();
    }

    if (mounted) {
      setState(() {
        _deviceData = data;
        _isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Device Information')),
      body: _isLoading
          ? const Center(child: CircularProgressIndicator())
          : ListView(
              children: _deviceData.entries.map((entry) {
                return ListTile(
                  title: Text(
                    entry.key,
                    style: const TextStyle(
                      fontWeight: FontWeight.w600,
                      fontSize: 13,
                      color: Colors.grey,
                    ),
                  ),
                  subtitle: Text(
                    entry.value,
                    style: const TextStyle(fontSize: 16),
                  ),
                  dense: true,
                );
              }).toList(),
            ),
    );
  }
}

Best Practices

  • Call once, cache the result — platform channel calls have overhead; store the returned data in a variable or a state management provider rather than calling the plugin repeatedly.
  • Guard with mounted — always check if (mounted) before calling setState inside async methods to avoid setting state on a disposed widget.
  • Wrap in try/catch — on unsupported platforms or during testing, the plugin may throw; graceful error handling keeps your app stable.
  • Do not hard-code SDK levels — compare android.version.sdkInt against named constants (e.g. Build.VERSION_CODES.TIRAMISU = 33) or well-documented integer values rather than magic numbers.
Key Takeaway: device_info_plus provides a simple async API to read typed, platform-specific device metadata. Create one DeviceInfoPlugin instance, await the correct platform getter, and store the result. Guard async state updates with mounted, and prefer a server-generated UUID over hardware identifiers for tracking purposes.