├── 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 | [](https://github.com/fluttercommunity/community)
4 |
5 | 
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 |
--------------------------------------------------------------------------------