├── android ├── settings.gradle ├── .gitignore ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── kotlin │ │ └── dev │ │ └── fluttercommunity │ │ └── android_id │ │ └── AndroidIdPlugin.kt └── build.gradle ├── analysis_options.yaml ├── example ├── analysis_options.yaml ├── android │ ├── gradle.properties │ ├── app │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── drawable │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── drawable-v21 │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── values │ │ │ │ │ │ └── styles.xml │ │ │ │ │ └── values-night │ │ │ │ │ │ └── styles.xml │ │ │ │ ├── kotlin │ │ │ │ │ └── dev │ │ │ │ │ │ └── fluttercommunity │ │ │ │ │ │ └── android_id_example │ │ │ │ │ │ └── MainActivity.kt │ │ │ │ └── AndroidManifest.xml │ │ │ ├── debug │ │ │ │ └── AndroidManifest.xml │ │ │ └── profile │ │ │ │ └── AndroidManifest.xml │ │ └── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── .gitignore │ ├── build.gradle │ └── settings.gradle ├── pubspec.yaml ├── README.md ├── .gitignore ├── .metadata ├── test │ └── android_id_example_test.dart ├── lib │ └── main.dart ├── integration_test │ └── android_id_test.dart └── pubspec.lock ├── .gitattributes ├── .github ├── workflows │ ├── pub_publish.yml │ ├── pub_publish_dry_run.yml │ ├── pub_score.yml │ ├── flutter_build_example.yml │ ├── integration_tests_android.yml │ └── flutter_checks.yml └── dependabot.yml ├── .gitignore ├── lib └── android_id.dart ├── pubspec.yaml ├── .metadata ├── LICENSE ├── test └── android_id_test.dart ├── CHANGELOG.md └── README.md /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'android_id' 2 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .cxx 10 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/android_id/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/android_id/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/android_id/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/android_id/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/android_id/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/dev/fluttercommunity/android_id_example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package dev.fluttercommunity.android_id_example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() 6 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip 6 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /.github/workflows/pub_publish.yml: -------------------------------------------------------------------------------- 1 | name: Pub publish 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | publish: 9 | name: Pub publish 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v6 13 | 14 | - name: Publish 15 | uses: k-paxian/dart-package-publisher@v1.6 16 | with: 17 | credentialJson: ${{ secrets.CREDENTIALS }} 18 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | rootProject.buildDir = "../build" 9 | subprojects { 10 | project.buildDir = "${rootProject.buildDir}/${project.name}" 11 | } 12 | subprojects { 13 | project.evaluationDependsOn(":app") 14 | } 15 | 16 | tasks.register("clean", Delete) { 17 | delete rootProject.buildDir 18 | } 19 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: android_id_example 2 | 3 | description: Demonstrates how to use the android_id plugin. 4 | 5 | version: 1.0.0+1 6 | 7 | publish_to: 'none' 8 | 9 | environment: 10 | sdk: '>=3.0.0 <4.0.0' 11 | 12 | dependencies: 13 | flutter: 14 | sdk: flutter 15 | android_id: 16 | path: ../ 17 | 18 | dev_dependencies: 19 | flutter_lints: ^6.0.0 20 | flutter_test: 21 | sdk: flutter 22 | integration_test: 23 | sdk: flutter 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | .vscode/ 19 | 20 | # Flutter/Dart/Pub related 21 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 22 | /pubspec.lock 23 | **/doc/api/ 24 | .dart_tool/ 25 | .packages 26 | build/ 27 | -------------------------------------------------------------------------------- /.github/workflows/pub_publish_dry_run.yml: -------------------------------------------------------------------------------- 1 | name: Pub publish dry run 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | publish-dry-run: 11 | name: Pub publish dry run 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v6 15 | 16 | - name: Publish dry run 17 | uses: k-paxian/dart-package-publisher@v1.6 18 | with: 19 | credentialJson: MockCredentialJson 20 | dryRunOnly: true 21 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # android_id_example 2 | 3 | Demonstrates how to use the android_id plugin. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) 13 | 14 | For help getting started with Flutter development, view the 15 | [online documentation](https://docs.flutter.dev/), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /lib/android_id.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/services.dart'; 3 | 4 | /// The plugin class for retrieving the Android ID. 5 | class AndroidId { 6 | const AndroidId(); 7 | 8 | /// The method channel used to interact with the native platform. 9 | static const _methodChannel = MethodChannel('android_id'); 10 | 11 | /// Calls the native method to retrieve the Android ID. 12 | Future getId() async { 13 | final isAndroid = 14 | !kIsWeb && defaultTargetPlatform == TargetPlatform.android; 15 | if (!isAndroid) return null; 16 | 17 | return _methodChannel.invokeMethod('getId'); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | .vscode/ 19 | 20 | # Flutter/Dart/Pub related 21 | **/doc/api/ 22 | **/ios/Flutter/.last_build_id 23 | .dart_tool/ 24 | .flutter-plugins 25 | .flutter-plugins-dependencies 26 | .packages 27 | .pub-cache/ 28 | .pub/ 29 | /build/ 30 | 31 | # Symbolication related 32 | app.*.symbols 33 | 34 | # Obfuscation related 35 | app.*.map.json 36 | 37 | # Android Studio will place build artifacts here 38 | /android/app/debug 39 | /android/app/profile 40 | /android/app/release 41 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: android_id 2 | 3 | description: A Flutter plugin for getting the Android ID. Only for Android. 4 | repository: https://github.com/fluttercommunity/android_id 5 | issue_tracker: https://github.com/fluttercommunity/android_id/issues 6 | 7 | maintainer: Joachim Nohl (@nohli) 8 | 9 | topics: 10 | - androidid 11 | - device 12 | - identifier 13 | - android 14 | - native 15 | 16 | version: 0.5.1 17 | 18 | environment: 19 | sdk: '>=3.0.0 <4.0.0' 20 | flutter: '>=3.10.0' 21 | 22 | dependencies: 23 | flutter: 24 | sdk: flutter 25 | 26 | dev_dependencies: 27 | flutter_lints: ^6.0.0 28 | flutter_test: 29 | sdk: flutter 30 | 31 | flutter: 32 | plugin: 33 | platforms: 34 | android: 35 | package: dev.fluttercommunity.android_id 36 | pluginClass: AndroidIdPlugin 37 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | }() 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 21 | id "com.android.application" version "8.7.2" apply false 22 | id "org.jetbrains.kotlin.android" version "2.1.0" apply false 23 | } 24 | 25 | include ":app" 26 | -------------------------------------------------------------------------------- /.github/workflows/pub_score.yml: -------------------------------------------------------------------------------- 1 | name: Check pub score 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | branches: 8 | - main 9 | 10 | jobs: 11 | check-score: 12 | name: Check pub score 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v6 16 | 17 | - uses: axel-op/dart-package-analyzer@v3 18 | id: workflow 19 | with: 20 | githubToken: ${{ secrets.GITHUB_TOKEN }} 21 | 22 | - name: Check score 23 | env: 24 | TOTAL: ${{ steps.workflow.outputs.total }} 25 | TOTAL_MAX: ${{ steps.workflow.outputs.total_max }} 26 | run: | 27 | MISSING_POINTS=$(( $TOTAL_MAX - $TOTAL )) 28 | if (( MISSING_POINTS > 0 )) 29 | then 30 | echo Score can be improved! 31 | echo Run pana to find out how. 32 | exit 1 33 | fi 34 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | 13 | - package-ecosystem: "pub" 14 | directory: "/" 15 | schedule: 16 | interval: "daily" 17 | ignore: 18 | - dependency-name: "*" 19 | update-types: ["version-update:semver-minor", "version-update:semver-patch"] 20 | 21 | - package-ecosystem: "pub" 22 | directory: "example" 23 | schedule: 24 | interval: "daily" 25 | ignore: 26 | - dependency-name: "*" 27 | update-types: ["version-update:semver-minor", "version-update:semver-patch"] 28 | -------------------------------------------------------------------------------- /.github/workflows/flutter_build_example.yml: -------------------------------------------------------------------------------- 1 | name: Flutter build example 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | branches: 8 | - main 9 | 10 | jobs: 11 | build_android: 12 | name: Build example for Android 13 | runs-on: ubuntu-latest 14 | timeout-minutes: 10 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@v6 18 | 19 | - name: Setup Java 20 | uses: actions/setup-java@v5 21 | with: 22 | distribution: temurin 23 | java-version: 17 24 | 25 | - name: Install Flutter 26 | uses: subosito/flutter-action@v2 27 | with: 28 | channel: stable 29 | cache: true 30 | 31 | - name: Disable analytics 32 | run: flutter config --no-analytics 33 | 34 | - name: Flutter pub get 35 | run: flutter pub get 36 | 37 | - name: Flutter build appbundle 38 | run: | 39 | cd example 40 | flutter build appbundle --release 41 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "a14f74ff3a1cbd521163c5f03d68113d50af93d3" 8 | channel: "stable" 9 | 10 | project_type: plugin 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 17 | base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 18 | - platform: android 19 | create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 20 | base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 21 | 22 | # User provided section 23 | 24 | # List of Local paths (relative to this file) that should be 25 | # ignored by the migrate tool. 26 | # 27 | # Files that are not part of the templates will be ignored by default. 28 | unmanaged_files: 29 | - 'lib/main.dart' 30 | - 'ios/Runner.xcodeproj/project.pbxproj' 31 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled. 5 | 6 | version: 7 | revision: 52b3dc25f6471c27b2144594abb11c741cb88f57 8 | channel: stable 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57 17 | base_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57 18 | - platform: android 19 | create_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57 20 | base_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57 21 | - platform: ios 22 | create_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57 23 | base_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57 24 | 25 | # User provided section 26 | 27 | # List of Local paths (relative to this file) that should be 28 | # ignored by the migrate tool. 29 | # 30 | # Files that are not part of the templates will be ignored by default. 31 | unmanaged_files: 32 | - 'lib/main.dart' 33 | - 'ios/Runner.xcodeproj/project.pbxproj' 34 | -------------------------------------------------------------------------------- /example/test/android_id_example_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility in the flutter_test package. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:android_id_example/main.dart'; 9 | import 'package:flutter/material.dart'; 10 | import 'package:flutter/services.dart'; 11 | import 'package:flutter_test/flutter_test.dart'; 12 | 13 | void main() { 14 | const channel = MethodChannel('android_id'); 15 | 16 | TestWidgetsFlutterBinding.ensureInitialized(); 17 | 18 | setUp(() { 19 | TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger 20 | .setMockMethodCallHandler( 21 | channel, (MethodCall methodCall) async => '42'); 22 | }); 23 | 24 | tearDown(() { 25 | TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger 26 | .setMockMethodCallHandler(channel, null); 27 | }); 28 | 29 | testWidgets('Gets and displays Android ID', (WidgetTester tester) async { 30 | // Build our app and trigger a frame. 31 | await tester.pumpWidget(const MyApp()); 32 | await tester.pumpAndSettle(); 33 | 34 | // Verify that an Android ID is retrieved. 35 | expect( 36 | find.byWidgetPredicate((Widget widget) { 37 | return widget is Text && widget.data!.startsWith('Android ID: 42'); 38 | }), 39 | findsOneWidget, 40 | ); 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, Joachim Nohl 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | group = "dev.fluttercommunity.android_id" 2 | version = "1.0-SNAPSHOT" 3 | 4 | buildscript { 5 | ext.kotlin_version = "2.1.0" 6 | repositories { 7 | google() 8 | mavenCentral() 9 | } 10 | 11 | dependencies { 12 | classpath("com.android.tools.build:gradle:8.7.2") 13 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version") 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | google() 20 | mavenCentral() 21 | } 22 | } 23 | 24 | apply plugin: "com.android.library" 25 | apply plugin: "kotlin-android" 26 | 27 | android { 28 | if (project.android.hasProperty("namespace")) { 29 | namespace = "dev.fluttercommunity.android_id" 30 | } 31 | 32 | compileSdk = flutter.compileSdkVersion 33 | 34 | compileOptions { 35 | sourceCompatibility = JavaVersion.VERSION_17 36 | targetCompatibility = JavaVersion.VERSION_17 37 | } 38 | 39 | kotlinOptions { 40 | jvmTarget = "17" 41 | } 42 | 43 | sourceSets { 44 | main.java.srcDirs += "src/main/kotlin" 45 | test.java.srcDirs += "src/test/kotlin" 46 | } 47 | 48 | defaultConfig { 49 | minSdk = flutter.minSdkVersion 50 | } 51 | 52 | dependencies { 53 | testImplementation("org.jetbrains.kotlin:kotlin-test") 54 | testImplementation("org.mockito:mockito-core:5.20.0") 55 | } 56 | 57 | testOptions { 58 | unitTests.all { 59 | useJUnitPlatform() 60 | 61 | testLogging { 62 | events "passed", "skipped", "failed", "standardOut", "standardError" 63 | outputs.upToDateWhen {false} 64 | showStandardStreams = true 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:android_id/android_id.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter/services.dart'; 6 | 7 | void main() => runApp(const MyApp()); 8 | 9 | class MyApp extends StatefulWidget { 10 | const MyApp({super.key}); 11 | 12 | @override 13 | State createState() => _MyAppState(); 14 | } 15 | 16 | class _MyAppState extends State { 17 | static const _androidIdPlugin = AndroidId(); 18 | 19 | var _androidId = 'Unknown'; 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | _initAndroidId(); 25 | } 26 | 27 | // Platform messages are asynchronous, so we initialize in an async method. 28 | Future _initAndroidId() async { 29 | String androidId; 30 | // Platform messages may fail, so we use a try/catch PlatformException. 31 | // We also handle the message potentially returning null. 32 | try { 33 | androidId = await _androidIdPlugin.getId() ?? 'Unknown ID'; 34 | } on PlatformException { 35 | androidId = 'Failed to get Android ID.'; 36 | } 37 | 38 | // If the widget was removed from the tree while the asynchronous platform 39 | // message was in flight, we want to discard the reply rather than calling 40 | // setState to update our non-existent appearance. 41 | if (!mounted) return; 42 | 43 | setState(() => _androidId = androidId); 44 | } 45 | 46 | @override 47 | Widget build(BuildContext context) { 48 | return MaterialApp( 49 | home: Scaffold( 50 | appBar: AppBar( 51 | title: const Text('Plugin example app'), 52 | ), 53 | body: Center( 54 | child: Text('Android ID: $_androidId'), 55 | ), 56 | ), 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/android_id_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:android_id/android_id.dart'; 2 | import 'package:flutter/foundation.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter_test/flutter_test.dart'; 5 | 6 | void main() { 7 | const plugin = AndroidId(); 8 | const channel = MethodChannel('android_id'); 9 | const mockAndroidId = '42'; 10 | 11 | TestWidgetsFlutterBinding.ensureInitialized(); 12 | debugDefaultTargetPlatformOverride = TargetPlatform.android; 13 | 14 | setUp(() { 15 | TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger 16 | .setMockMethodCallHandler(channel, (methodCall) async => mockAndroidId); 17 | }); 18 | 19 | tearDown(() { 20 | TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger 21 | .setMockMethodCallHandler(channel, null); 22 | }); 23 | 24 | test('getAndroidId', () async { 25 | expect(await plugin.getId(), mockAndroidId); 26 | }); 27 | 28 | test('returns null on non-Android platforms', () async { 29 | addTearDown(() => debugDefaultTargetPlatformOverride = null); 30 | 31 | debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia; 32 | 33 | expect(await plugin.getId(), isNull); 34 | 35 | debugDefaultTargetPlatformOverride = TargetPlatform.iOS; 36 | 37 | expect(await plugin.getId(), isNull); 38 | 39 | debugDefaultTargetPlatformOverride = TargetPlatform.linux; 40 | 41 | expect(await plugin.getId(), isNull); 42 | 43 | debugDefaultTargetPlatformOverride = TargetPlatform.macOS; 44 | 45 | expect(await plugin.getId(), isNull); 46 | 47 | debugDefaultTargetPlatformOverride = TargetPlatform.windows; 48 | 49 | expect(await plugin.getId(), isNull); 50 | 51 | debugDefaultTargetPlatformOverride = TargetPlatform.android; 52 | 53 | expect(await plugin.getId(), mockAndroidId); 54 | }); 55 | } 56 | -------------------------------------------------------------------------------- /.github/workflows/integration_tests_android.yml: -------------------------------------------------------------------------------- 1 | name: Integration Tests (Android) 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | branches: 8 | - main 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | integration_test: 16 | name: Android API ${{ matrix.api-level }} - ${{ matrix.profile }} 17 | runs-on: ubuntu-latest 18 | timeout-minutes: 20 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | api-level: [24, 30, 35] 23 | profile: [medium_phone] 24 | 25 | steps: 26 | - name: Checkout repository 27 | uses: actions/checkout@v6 28 | 29 | - name: Setup Java 30 | uses: actions/setup-java@v5 31 | with: 32 | distribution: temurin 33 | java-version: 17 34 | 35 | - name: Install Flutter 36 | uses: subosito/flutter-action@v2 37 | with: 38 | channel: stable 39 | cache: true 40 | 41 | - name: Disable analytics 42 | run: flutter config --no-analytics 43 | 44 | - name: Flutter pub get 45 | run: | 46 | flutter pub get 47 | cd example 48 | flutter pub get 49 | 50 | - name: Enable emulator hardware acceleration (KVM) 51 | run: | 52 | echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules 53 | sudo udevadm control --reload-rules 54 | sudo udevadm trigger --name-match=kvm 55 | 56 | - name: Boot emulator and run integration tests 57 | uses: reactivecircus/android-emulator-runner@v2 58 | with: 59 | api-level: ${{ matrix.api-level }} 60 | profile: ${{ matrix.profile }} 61 | arch: x86_64 62 | script: | 63 | cd example && flutter test integration_test 64 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/fluttercommunity/android_id/AndroidIdPlugin.kt: -------------------------------------------------------------------------------- 1 | package dev.fluttercommunity.android_id 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.ContentResolver 5 | import android.provider.Settings 6 | import androidx.annotation.NonNull 7 | 8 | import io.flutter.embedding.engine.plugins.FlutterPlugin 9 | import io.flutter.plugin.common.MethodCall 10 | import io.flutter.plugin.common.MethodChannel 11 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler 12 | import io.flutter.plugin.common.MethodChannel.Result 13 | 14 | /** AndroidIdPlugin */ 15 | class AndroidIdPlugin: FlutterPlugin, MethodCallHandler { 16 | /// The MethodChannel that will the communication between Flutter and native Android 17 | /// 18 | /// This local reference serves to register the plugin with the Flutter Engine and unregister it 19 | /// when the Flutter Engine is detached from the Activity 20 | private lateinit var channel: MethodChannel 21 | 22 | private lateinit var contentResolver: ContentResolver 23 | 24 | override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { 25 | contentResolver = flutterPluginBinding.applicationContext.contentResolver 26 | channel = MethodChannel(flutterPluginBinding.binaryMessenger, "android_id") 27 | channel.setMethodCallHandler(this) 28 | } 29 | 30 | override fun onMethodCall(call: MethodCall, result: Result) { 31 | when (call.method) { 32 | "getId" -> { 33 | try { 34 | result.success(getAndroidId()) 35 | } catch (e: Exception) { 36 | result.error("ERROR_GETTING_ID", "Failed to get Android ID", e.localizedMessage) 37 | } 38 | } 39 | else -> result.notImplemented() 40 | } 41 | } 42 | 43 | override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { 44 | channel.setMethodCallHandler(null) 45 | } 46 | 47 | // Fetch the Android ID while suppressing lint warning about hardware IDs 48 | @SuppressLint("HardwareIds") 49 | private fun getAndroidId(): String? { 50 | return Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. 5 | id "dev.flutter.flutter-gradle-plugin" 6 | } 7 | 8 | def localProperties = new Properties() 9 | def localPropertiesFile = rootProject.file("local.properties") 10 | if (localPropertiesFile.exists()) { 11 | localPropertiesFile.withReader("UTF-8") { reader -> 12 | localProperties.load(reader) 13 | } 14 | } 15 | 16 | def flutterVersionCode = localProperties.getProperty("flutter.versionCode") 17 | if (flutterVersionCode == null) { 18 | flutterVersionCode = "1" 19 | } 20 | 21 | def flutterVersionName = localProperties.getProperty("flutter.versionName") 22 | if (flutterVersionName == null) { 23 | flutterVersionName = "1.0" 24 | } 25 | 26 | android { 27 | namespace = "dev.fluttercommunity.android_id_example" 28 | compileSdk = flutter.compileSdkVersion 29 | ndkVersion = flutter.ndkVersion 30 | 31 | compileOptions { 32 | sourceCompatibility = JavaVersion.VERSION_17 33 | targetCompatibility = JavaVersion.VERSION_17 34 | } 35 | 36 | kotlinOptions { 37 | jvmTarget = "17" 38 | } 39 | 40 | defaultConfig { 41 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 42 | applicationId = "dev.fluttercommunity.android_id_example" 43 | // You can update the following values to match your application needs. 44 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. 45 | minSdk = flutter.minSdkVersion 46 | targetSdk = flutter.targetSdkVersion 47 | versionCode = flutterVersionCode.toInteger() 48 | versionName = flutterVersionName 49 | } 50 | 51 | buildTypes { 52 | release { 53 | // TODO: Add your own signing config for the release build. 54 | // Signing with the debug keys for now, so `flutter run --release` works. 55 | signingConfig = signingConfigs.debug 56 | } 57 | } 58 | } 59 | 60 | flutter { 61 | source = "../.." 62 | } 63 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 33 | 34 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.5.1 2 | 3 | * Add integration tests to verify plugin functionality across Android API levels 4 | * Add unit coverage to ensure the plugin returns `null` on non-Android platforms 5 | * Expand README with comprehensive troubleshooting guidance and defensive error-handling examples 6 | 7 | ## 0.5.0 8 | 9 | * Breaking: Flutter SDK constraint is now >=3.10.0 (Dart 3-only). Apps on older Flutter versions can’t upgrade. 10 | * Update Android tooling 11 | 12 | ## 0.4.1 13 | 14 | * Maintenance release 15 | 16 | ## 0.4.0 17 | 18 | * Upgrade Android dependencies (Gradle, AGP, Java, Kotlin) 19 | * Widen `flutter_lints` constraints 20 | 21 | ## 0.3.6 22 | 23 | * Return `null` on platforms other than Android, instead of throwing an exception 24 | 25 | ## 0.3.5 26 | 27 | * Maintenance release 28 | * Update `kotlin` and `gradle` 29 | 30 | ## 0.3.4 31 | 32 | * Refactor native code 33 | 34 | ## 0.3.3 35 | 36 | * Add topics to pubspec.yaml 37 | 38 | ## 0.3.2 39 | 40 | * Improve `namespace` fix for `Android Gradle Plugin` versions < 7 41 | 42 | ## 0.3.1 43 | 44 | * Fix `namespace` for `Android Gradle Plugin` versions < 7 45 | 46 | ## 0.3.0 47 | 48 | * Add `namespace` for compatibility with `Android Gradle Plugin 8` 49 | * Update `Gradle` version 50 | 51 | ## 0.2.0 52 | 53 | * Update `Gradle` version 54 | * Update `Android Gradle plugin` version 55 | * Update `Kotlin` version 56 | * Add Gradle wrapper files 57 | * Update `pubspec.lock` of example (`url: "https://pub.dev"`) 58 | 59 | ## 0.1.3+1 60 | 61 | * Improve readme: Add explanation for connection of `Android ID` and signing key 62 | 63 | ## 0.1.3 64 | 65 | * Add building of example app to GitHub workflows 66 | * Remove unneeded code 67 | 68 | ## 0.1.2 69 | 70 | * Add `Flutter test` for example app to workflows 71 | 72 | ## 0.1.1+1 73 | 74 | * Update readme 75 | 76 | ## 0.1.1 77 | 78 | * Update example app dependencies 79 | 80 | ## 0.1.0 81 | 82 | * Rename Android `applicationId` 83 | * Recreate `android` folder from template 84 | 85 | ## 0.0.8 86 | 87 | * Add more GitHub Actions for checking code quality 88 | * Update kotlin_version from 1.6.10 to 1.7.20 89 | * Update gradle from 7.1.2 to 7.3.1 90 | * Add dependabot 91 | 92 | ## 0.0.7 93 | 94 | * Add Google Play information to README. 95 | 96 | ## 0.0.6 97 | 98 | * Lower minimal sdk version to `2.12.0`. 99 | 100 | ## 0.0.5 101 | 102 | * Documentation typo fix. 103 | 104 | ## 0.0.4 105 | 106 | * Small improvements. 107 | 108 | ## 0.0.3 109 | 110 | * Improve example. 111 | 112 | ## 0.0.2 113 | 114 | * Enhance description to get full pub.dev score. 115 | 116 | ## 0.0.1 117 | 118 | * Initial release. 119 | -------------------------------------------------------------------------------- /example/integration_test/android_id_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:android_id/android_id.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:integration_test/integration_test.dart'; 4 | 5 | void main() { 6 | IntegrationTestWidgetsFlutterBinding.ensureInitialized(); 7 | 8 | group('AndroidId Plugin Integration Tests', () { 9 | const androidIdPlugin = AndroidId(); 10 | 11 | testWidgets( 12 | 'getId should not throw MissingPluginException on Android', 13 | (WidgetTester tester) async { 14 | // This test verifies that the plugin is properly registered 15 | // and does not throw MissingPluginException when called on Android 16 | 17 | String? androidId; 18 | 19 | // Should not throw MissingPluginException 20 | androidId = await androidIdPlugin.getId(); 21 | 22 | // On Android, we should get a non-null ID 23 | // On other platforms, we should get null 24 | expect( 25 | androidId, 26 | isNotNull, 27 | reason: 'Android ID should be available on Android devices/emulators', 28 | ); 29 | 30 | // Android ID should be a hex string 31 | expect( 32 | androidId, 33 | matches(RegExp(r'^[0-9a-f]+$')), 34 | reason: 'Android ID should be a hexadecimal string', 35 | ); 36 | 37 | // Android ID should be 16 characters (64-bit hex) 38 | expect( 39 | androidId?.length, 40 | equals(16), 41 | reason: 'Android ID should be 16 hex characters (64-bit)', 42 | ); 43 | }, 44 | ); 45 | 46 | testWidgets( 47 | 'getId should return consistent value', 48 | (WidgetTester tester) async { 49 | // Call getId multiple times and verify it returns the same value 50 | final id1 = await androidIdPlugin.getId(); 51 | final id2 = await androidIdPlugin.getId(); 52 | 53 | expect( 54 | id1, 55 | equals(id2), 56 | reason: 'Android ID should be consistent across multiple calls', 57 | ); 58 | }, 59 | ); 60 | 61 | testWidgets( 62 | 'getId should work after hot restart', 63 | (WidgetTester tester) async { 64 | // This test verifies that the plugin remains registered and functional 65 | // after pumping frames. Note: Hot restart cannot be simulated in integration tests. 66 | 67 | final id = await androidIdPlugin.getId(); 68 | expect(id, isNotNull); 69 | 70 | // Pump frames to simulate app lifecycle 71 | await tester.pumpAndSettle(); 72 | 73 | // Should still work 74 | final idAfter = await androidIdPlugin.getId(); 75 | expect(idAfter, equals(id)); 76 | }, 77 | ); 78 | }); 79 | } 80 | -------------------------------------------------------------------------------- /.github/workflows/flutter_checks.yml: -------------------------------------------------------------------------------- 1 | name: Flutter checks 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | branches: 8 | - main 9 | 10 | jobs: 11 | check_pub_dependencies: 12 | name: Check dependencies 13 | timeout-minutes: 10 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@v6 18 | 19 | - name: Install Flutter 20 | uses: subosito/flutter-action@v2 21 | with: 22 | channel: stable 23 | cache: true 24 | 25 | - name: Disable analytics 26 | run: flutter config --no-analytics 27 | 28 | - name: Flutter pub get 29 | run: flutter pub get 30 | 31 | check_formatting: 32 | name: Check formatting 33 | runs-on: ubuntu-latest 34 | timeout-minutes: 10 35 | steps: 36 | - name: Checkout repository 37 | uses: actions/checkout@v6 38 | 39 | - name: Install Flutter 40 | uses: subosito/flutter-action@v2 41 | with: 42 | channel: stable 43 | cache: true 44 | 45 | - name: Disable analytics 46 | run: flutter config --no-analytics 47 | 48 | - name: Flutter pub get 49 | run: flutter pub get 50 | 51 | - name: Check Dart formatting 52 | run: dart format --line-length 80 --set-exit-if-changed . 53 | 54 | analyze: 55 | name: Dart analyze 56 | timeout-minutes: 10 57 | runs-on: ubuntu-latest 58 | steps: 59 | - name: Checkout repository 60 | uses: actions/checkout@v6 61 | 62 | - name: Install Flutter 63 | uses: subosito/flutter-action@v2 64 | with: 65 | channel: stable 66 | cache: true 67 | 68 | - name: Disable analytics 69 | run: flutter config --no-analytics 70 | 71 | - name: Flutter pub get 72 | run: flutter pub get 73 | 74 | - name: Run Dart analyze 75 | uses: invertase/github-action-dart-analyzer@v3.0.0 76 | 77 | test: 78 | name: Flutter test 79 | runs-on: ubuntu-latest 80 | timeout-minutes: 10 81 | steps: 82 | - name: Checkout repository 83 | uses: actions/checkout@v6 84 | 85 | - name: Install Flutter 86 | uses: subosito/flutter-action@v2 87 | with: 88 | channel: stable 89 | cache: true 90 | 91 | - name: Disable analytics 92 | run: flutter config --no-analytics 93 | 94 | - name: Flutter pub get 95 | run: flutter pub get 96 | 97 | - name: Flutter test 98 | run: flutter test 99 | 100 | test_example: 101 | name: Flutter test example 102 | runs-on: ubuntu-latest 103 | timeout-minutes: 10 104 | steps: 105 | - name: Checkout repository 106 | uses: actions/checkout@v6 107 | 108 | - name: Install Flutter 109 | uses: subosito/flutter-action@v2 110 | with: 111 | channel: stable 112 | cache: true 113 | 114 | - name: Disable analytics 115 | run: flutter config --no-analytics 116 | 117 | - name: Flutter pub get 118 | run: | 119 | cd example 120 | flutter pub get 121 | 122 | - name: Flutter test example 123 | run: | 124 | cd example 125 | flutter test 126 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # android_id 2 | 3 | [![Flutter Community: android_id](https://fluttercommunity.dev/_github/header/android_id)](https://github.com/fluttercommunity/community) 4 | 5 | ![Pub Version](https://img.shields.io/pub/v/android_id) 6 | 7 | A Flutter plugin for retrieving the Android ID. 8 | 9 | ## Getting started 10 | 11 | * Add plugin to pubspec.yaml 12 | * Use it in your code (see examples of all methods below) 13 | 14 | ## Usage 15 | 16 | ```dart 17 | const _androidIdPlugin = AndroidId(); 18 | 19 | final String? androidId = await _androidIdPlugin.getId(); 20 | ``` 21 | 22 | ### Optional defensive handling 23 | 24 | Treat registration/runtime failures as `null` by wrapping the call in a `try`/`catch`: 25 | 26 | ```dart 27 | const _androidIdPlugin = AndroidId(); 28 | 29 | String? androidId; 30 | try { 31 | androidId = await _androidIdPlugin.getId(); 32 | } on MissingPluginException { 33 | print('Failed to get Android ID: MissingPluginException'); 34 | androidId = null; 35 | } on PlatformException catch (e) { 36 | print('Failed to get Android ID: ${e.message}'); 37 | androidId = null; 38 | } 39 | ``` 40 | 41 | **Note:** `getId()` returns `null` on non-Android platforms (iOS, Web, etc.). On Android, it throws `MissingPluginException` if the plugin is not properly registered (see more [below](https://pub.dev/packages/android_id#troubleshooting)). 42 | 43 | ## Important 44 | 45 | Please note that on `Android 8` and above, the `Android ID` is not unique per device, but also per signing key the app was built with: 46 | 47 |
48 | On Android 8.0 (API level 26) and higher versions of the platform, a 64-bit number (expressed as a hexadecimal string), unique to each combination of app-signing key, user, and device. 49 | 50 | The value may change if a factory reset is performed on the device or if an APK signing key changes. 51 |
52 | 53 | [Android API reference](https://developer.android.com/reference/android/provider/Settings.Secure#ANDROID_ID) 54 | 55 | [Stack Overflow explanation](https://stackoverflow.com/a/43393373) 56 | 57 | ## Google Play 58 | 59 | Before using this plugin in your app, make sure to follow Google Play guidelines. 60 | For example [here](https://support.google.com/googleplay/android-developer/answer/6048248#zippy=%2Cpersistent-identifiers-including-android-id): 61 | 62 |
63 | Persistent identifiers, including Android ID 64 | 65 | Use for non-advertising purposes
66 | You can use persistent identifiers as long as you have a [privacy policy](https://support.google.com/googleplay/android-developer/answer/9859455) and handle the data in accordance with the [Developer Distribution Agreement](https://play.google.com/about/developer-distribution-agreement.html#use) and all applicable privacy laws in the areas where you make your app available. 67 |
68 | 69 | ## Troubleshooting 70 | 71 | If you're experiencing `MissingPluginException`, try these steps in order: 72 | 73 | 1. **Full Rebuild**: Stop the app completely and rebuild from scratch 74 | ```bash 75 | flutter clean 76 | flutter pub get 77 | flutter run 78 | ``` 79 | 80 | 2. **Check Flutter Version**: Ensure you're using Flutter 3.10.0 or higher 81 | ```bash 82 | flutter --version 83 | ``` 84 | 85 | 3. **Verify Android Embedding**: Check `android/app/src/main/.../MainActivity.kt` (or `.java`) 86 | - Should import: `io.flutter.embedding.android.FlutterActivity` 87 | - Should NOT import: `io.flutter.app.FlutterActivity` (legacy v1 embedding) 88 | 89 | If using v1 embedding, migrate to v2 following [this guide](https://github.com/flutter/flutter/blob/main/docs/platforms/android/Upgrading-pre-1.12-Android-projects.md) 90 | 91 | 4. **Check Hot Reload**: Plugin registration happens during cold start. If using hot reload/restart: 92 | - Stop the app completely 93 | - Run `flutter run` again (not hot restart) 94 | 95 | 5. **Invalidate Caches**: For Android Studio/IntelliJ: 96 | - File → Invalidate Caches → Invalidate and Restart 97 | 98 | 6. **Test on Real Device**: If on emulator, try on a physical Android device 99 | 100 | If none of these work: 101 | - Search existing GitHub issues for a similar report and add your details there, or open a new issue if you can't find one. 102 | - Include the following information: 103 | - Flutter version (`flutter --version`) 104 | - Output of `flutter doctor -v` 105 | - Your `pubspec.yaml` dependencies 106 | - Full error stack trace 107 | - Whether it happens in a fresh project (`flutter create test_app`) 108 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | android_id: 5 | dependency: "direct main" 6 | description: 7 | path: ".." 8 | relative: true 9 | source: path 10 | version: "0.5.1" 11 | async: 12 | dependency: transitive 13 | description: 14 | name: async 15 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 16 | url: "https://pub.dev" 17 | source: hosted 18 | version: "2.11.0" 19 | boolean_selector: 20 | dependency: transitive 21 | description: 22 | name: boolean_selector 23 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 24 | url: "https://pub.dev" 25 | source: hosted 26 | version: "2.1.1" 27 | characters: 28 | dependency: transitive 29 | description: 30 | name: characters 31 | sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 32 | url: "https://pub.dev" 33 | source: hosted 34 | version: "1.4.0" 35 | clock: 36 | dependency: transitive 37 | description: 38 | name: clock 39 | sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b 40 | url: "https://pub.dev" 41 | source: hosted 42 | version: "1.1.2" 43 | collection: 44 | dependency: transitive 45 | description: 46 | name: collection 47 | sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" 48 | url: "https://pub.dev" 49 | source: hosted 50 | version: "1.19.1" 51 | fake_async: 52 | dependency: transitive 53 | description: 54 | name: fake_async 55 | sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" 56 | url: "https://pub.dev" 57 | source: hosted 58 | version: "1.3.3" 59 | file: 60 | dependency: transitive 61 | description: 62 | name: file 63 | sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 64 | url: "https://pub.dev" 65 | source: hosted 66 | version: "7.0.1" 67 | flutter: 68 | dependency: "direct main" 69 | description: flutter 70 | source: sdk 71 | version: "0.0.0" 72 | flutter_driver: 73 | dependency: transitive 74 | description: flutter 75 | source: sdk 76 | version: "0.0.0" 77 | flutter_lints: 78 | dependency: "direct dev" 79 | description: 80 | name: flutter_lints 81 | sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" 82 | url: "https://pub.dev" 83 | source: hosted 84 | version: "6.0.0" 85 | flutter_test: 86 | dependency: "direct dev" 87 | description: flutter 88 | source: sdk 89 | version: "0.0.0" 90 | fuchsia_remote_debug_protocol: 91 | dependency: transitive 92 | description: flutter 93 | source: sdk 94 | version: "0.0.0" 95 | integration_test: 96 | dependency: "direct dev" 97 | description: flutter 98 | source: sdk 99 | version: "0.0.0" 100 | leak_tracker: 101 | dependency: transitive 102 | description: 103 | name: leak_tracker 104 | sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" 105 | url: "https://pub.dev" 106 | source: hosted 107 | version: "11.0.2" 108 | leak_tracker_flutter_testing: 109 | dependency: transitive 110 | description: 111 | name: leak_tracker_flutter_testing 112 | sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" 113 | url: "https://pub.dev" 114 | source: hosted 115 | version: "3.0.10" 116 | leak_tracker_testing: 117 | dependency: transitive 118 | description: 119 | name: leak_tracker_testing 120 | sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" 121 | url: "https://pub.dev" 122 | source: hosted 123 | version: "3.0.2" 124 | lints: 125 | dependency: transitive 126 | description: 127 | name: lints 128 | sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0 129 | url: "https://pub.dev" 130 | source: hosted 131 | version: "6.0.0" 132 | matcher: 133 | dependency: transitive 134 | description: 135 | name: matcher 136 | sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 137 | url: "https://pub.dev" 138 | source: hosted 139 | version: "0.12.17" 140 | material_color_utilities: 141 | dependency: transitive 142 | description: 143 | name: material_color_utilities 144 | sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec 145 | url: "https://pub.dev" 146 | source: hosted 147 | version: "0.11.1" 148 | meta: 149 | dependency: transitive 150 | description: 151 | name: meta 152 | sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c 153 | url: "https://pub.dev" 154 | source: hosted 155 | version: "1.16.0" 156 | path: 157 | dependency: transitive 158 | description: 159 | name: path 160 | sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" 161 | url: "https://pub.dev" 162 | source: hosted 163 | version: "1.9.1" 164 | platform: 165 | dependency: transitive 166 | description: 167 | name: platform 168 | sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" 169 | url: "https://pub.dev" 170 | source: hosted 171 | version: "3.1.6" 172 | process: 173 | dependency: transitive 174 | description: 175 | name: process 176 | sha256: c6248e4526673988586e8c00bb22a49210c258dc91df5227d5da9748ecf79744 177 | url: "https://pub.dev" 178 | source: hosted 179 | version: "5.0.5" 180 | sky_engine: 181 | dependency: transitive 182 | description: flutter 183 | source: sdk 184 | version: "0.0.0" 185 | source_span: 186 | dependency: transitive 187 | description: 188 | name: source_span 189 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 190 | url: "https://pub.dev" 191 | source: hosted 192 | version: "1.10.0" 193 | stack_trace: 194 | dependency: transitive 195 | description: 196 | name: stack_trace 197 | sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" 198 | url: "https://pub.dev" 199 | source: hosted 200 | version: "1.12.1" 201 | stream_channel: 202 | dependency: transitive 203 | description: 204 | name: stream_channel 205 | sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" 206 | url: "https://pub.dev" 207 | source: hosted 208 | version: "2.1.4" 209 | string_scanner: 210 | dependency: transitive 211 | description: 212 | name: string_scanner 213 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 214 | url: "https://pub.dev" 215 | source: hosted 216 | version: "1.2.0" 217 | sync_http: 218 | dependency: transitive 219 | description: 220 | name: sync_http 221 | sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" 222 | url: "https://pub.dev" 223 | source: hosted 224 | version: "0.3.1" 225 | term_glyph: 226 | dependency: transitive 227 | description: 228 | name: term_glyph 229 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 230 | url: "https://pub.dev" 231 | source: hosted 232 | version: "1.2.1" 233 | test_api: 234 | dependency: transitive 235 | description: 236 | name: test_api 237 | sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" 238 | url: "https://pub.dev" 239 | source: hosted 240 | version: "0.7.6" 241 | vector_math: 242 | dependency: transitive 243 | description: 244 | name: vector_math 245 | sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b 246 | url: "https://pub.dev" 247 | source: hosted 248 | version: "2.2.0" 249 | vm_service: 250 | dependency: transitive 251 | description: 252 | name: vm_service 253 | sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" 254 | url: "https://pub.dev" 255 | source: hosted 256 | version: "14.2.1" 257 | webdriver: 258 | dependency: transitive 259 | description: 260 | name: webdriver 261 | sha256: "2f3a14ca026957870cfd9c635b83507e0e51d8091568e90129fbf805aba7cade" 262 | url: "https://pub.dev" 263 | source: hosted 264 | version: "3.1.0" 265 | sdks: 266 | dart: ">=3.8.0 <4.0.0" 267 | flutter: ">=3.18.0-18.0.pre.54" 268 | --------------------------------------------------------------------------------