=============== LIBRARY RULES =============== From library maintainers: - This plugin reports age signals the OS already holds. It is not identity verification, document scanning, or KYC, and it never returns a date of birth. Only an age range and an assurance tier are available. - Access the plugin through the singleton `AgeRangeSignals.instance`. There is no public constructor. - Import only `package:age_range_signals/age_range_signals.dart`. The `age_range_signals_method_channel` and `age_range_signals_platform_interface` libraries are federated-plugin plumbing, not application API. - Since 0.8.0 the flow is two calls: `await requestAgeSignalsAccess()` first, then `checkAgeSignals()` only when it returns `AgeSignalsAccessStatus.shared`. Calling `checkAgeSignals()` alone skips the access grant. - Call `initialize()` on both platforms. iOS requires `ageGates` and throws `NotInitializedException` without them. Android uses your highest gate as the bar for `verified`, 18 until you supply them, and keeps them if a later call omits them. - On iOS, Apple accepts 1 to 3 age gates and requires at least 2 years between consecutive gates, so `[13, 16, 18]` is valid and `[13, 14]` is rejected by the OS. - `status` is the verdict, `ageRangeSource` is the assurance. `verified` means the reported band starts at or above your highest gate; `supervised` means it falls below. Both platforms apply the same rule, so one check means the same thing on each. - A tier is never proof of age. `AgeRangeSource.tierD` means an ID was checked, and that ID can read 12. Use `ageRangeSource` only to apply a minimum assurance policy, never as the verdict. - `AgeSignalsStatus` values to handle: verified, unknown, declined, supervised, supervisedApprovalPending, supervisedApprovalDenied. `declared` is deprecated and never returned; read `ageRangeSource == AgeRangeSource.tierA` instead. - Status platform split: the two supervisedApproval values are Android only, `declined` is iOS only. On Android a refusal surfaces as `AgeSignalsAccessStatus.notShared` from the access call, not as `declined`. - `requestAgeSignalsAccess()` returns `AgeSignalsAccessStatus`: shared, notShared (a decline, not an error), verificationRequired (send the user to the Play Store), unknown. On iOS it returns shared, or throws below 26.0 or without gates. - `AgeRangeSource` (Android), weakest to strongest: tierA self-declared, tierB parent- or guardian-managed, tierC assessed via credit card, email, selfie, government ID or tax ID, tierD government ID plus selfie or a digital ID. - `SignificantChangeStatus` (Android, supervised users) is approved, pending or declined. Pending or declined reports supervisedApprovalPending/Denied whatever the age, because an outstanding guardian decision is actionable either way. - `ageLower`/`ageUpper` are null when nothing was shared. A `verified` result always has `ageLower` set. `ageUpper` is null only for Play's open-ended 18+ band, so never use it as a proxy for adult. - Android is ActivityAware because Play hosts a sharing prompt. Without an attached Activity, `requestAgeSignalsAccess()` throws `ApiErrorException` with code `PRESENTATION_CONTEXT_UNAVAILABLE`. - `useMockData` throws `MockDataNotAllowedException` when enabled outside debuggable Android builds. Use a debuggable variant when you need mock data. - `useMockData` and `mockData` are Android only, backed by Google's `FakeAgeSignalsManager`. They are silently ignored on iOS, which has no testing utility and requires a real device on iOS 26.2+. - Catch `AgeSignalsException` as the base type for all plugin errors. Each carries a `code` and a `details` string holding platform-specific diagnostics. - Exception subclasses, all suffixed `Exception`: ApiNotAvailable, UnsupportedPlatform, NotInitialized, MissingEntitlement, ApiError, PlayServices, NetworkError, UserNotSignedIn, UserCancelled, MockDataNotAllowed. - `getRequiredRegulatoryFeatures()` needs iOS 26.4+ and an Xcode 26.4+ build. It returns an empty set on Android and throws `UnsupportedPlatformException` on older iOS, so an empty set always means no regulatory action is required, never unknown. - `showSignificantUpdateAcknowledgment()` throws `UnsupportedPlatformException` on Android and iOS below 26.4. Apple reports user dismissal with the same error as real unavailability, so `ApiNotAvailableException` is not proof the sheet never showed. - Platform floors: Android API 23+ with Google Play services; iOS 26.0 for the base API, 26.2 for regional eligibility, 26.4 for regulatory features. iOS also requires Apple's DeclaredAgeRange entitlement. - `AgeSignalsResult.significantChangeApprovalDate` was named `mostRecentApprovalDate` before 0.8.0. The old name still works as a deprecated alias on the result, the mock data, both constructors and `copyWith`. - Google Play policy allows Age Signals data only for delivering age-appropriate experiences. Never route it to advertising, marketing, profiling, or analytics. That risks API termination and app suspension, so never log the result to an analytics SDK. - Play reports fixed age bands (0-12, 13-15, 16-17, 18+) while iOS buckets against your actual gates, so a gate off a band edge quantises upward on Android: with a gate at 15 a 15-year-old is verified on iOS but supervised on Android. ### Basic Example: Check Age Signals with AgeRangeSignals Source: https://github.com/zigapovhe/age_range_signals/blob/main/README.md Use this minimal example as a starting point for apps that need to call the Age Range Signals API. It initializes with age gates [13, 16, 18], requests access, and handles the shared vs. non-shared cases, falling back to showing age-appropriate content when the status is not verified. ```dart import 'package:age_range_signals/age_range_signals.dart'; await AgeRangeSignals.instance.initialize(ageGates: [13, 16, 18]); try { final access = await AgeRangeSignals.instance.requestAgeSignalsAccess(); if (access != AgeSignalsAccessStatus.shared) { // notShared is a decline, not an error. On verificationRequired, point // the user at the Play Store to finish verifying. showAgeAppropriateContent(null, null); return; } final result = await AgeRangeSignals.instance.checkAgeSignals(); if (result.status == AgeSignalsStatus.verified) { showUnrestrictedContent(); } else { // Anything else counts as restricted. Use the range if there is one. showAgeAppropriateContent(result.ageLower, result.ageUpper); } } on AgeSignalsException catch (e) { print('Age check failed: ${e.message}'); } ``` -------------------------------- ### AgeSignalsResult.fromMap example Source: https://github.com/zigapovhe/age_range_signals/blob/main/_autodocs/api-reference/age-signals-result.md Shows the map shape produced by the platform channel, which is also what toMap() emits. The map includes keys for status, ageLower, ageUpper, source, installId, activeParentalControls, ageRangeSource, significantChangeStatus, and significantChangeApprovalDate. ```dart // Shape produced by the platform channel — also what toMap() emits. final result = AgeSignalsResult.fromMap(const { 'status': 'verified', 'ageLower': 18, 'ageUpper': null, 'source': null, 'installId': null, 'activeParentalControls': null, 'ageRangeSource': 'tierC', 'significantChangeStatus': null, 'significantChangeApprovalDate': 1780000000000, }); ``` -------------------------------- ### Usage: copyWith redacting installId Source: https://github.com/zigapovhe/age_range_signals/blob/main/_autodocs/api-reference/age-signals-result.md Example usage of copyWith to redact installId and set status to unknown. ```dart final redacted = result.copyWith(installId: null, status: AgeSignalsStatus.unknown); ``` -------------------------------- ### requestAgeSignalsAccess Source: https://github.com/zigapovhe/age_range_signals/blob/main/_autodocs/api-reference/age-range-signals.md Documentation for the `requestAgeSignalsAccess` method, including its signature, return type, platform behavior, exceptions, and usage examples. ```APIDOC ## `requestAgeSignalsAccess` ### Description Requests access to age signals from the platform. On Android, it may show Play's in-app age-sharing prompt; on iOS, it returns `shared` without UI (consent is gathered inside `checkAgeSignals()`). Only `AgeSignalsAccessStatus.shared` means `checkAgeSignals()` will carry signals. ### Method `Future requestAgeSignalsAccess()` ### Parameters None. ### Return Type `Future` — the access outcome. Possible values: `shared`, `notShared`, `verificationRequired`. ### Platform Behavior - **Android**: Asks Play for access, showing Play's in-app age-sharing prompt when the user's Play settings call for asking first. A refusal is a normal outcome (`AgeSignalsAccessStatus.notShared`), not an error. In mandatory-verification regions Play skips the prompt: verified/supervised users come back `shared`, unverified users `verificationRequired` (complete verification in the Play Store app). - **iOS**: Returns `AgeSignalsAccessStatus.shared` without showing anything (consent is gathered inside `checkAgeSignals()`); a refusal surfaces there as `AgeSignalsStatus.declined`. Not unconditional: throws where it could not honour access, acting as a pre-flight. ### Throws (`AgeSignalsException` subclasses) - `ApiErrorException` with code `PRESENTATION_CONTEXT_UNAVAILABLE` — Android, no activity to present the prompt on. - `UnsupportedPlatformException` — iOS below 26.0 / built without the DeclaredAgeRange SDK. - `NotInitializedException` — iOS, `initialize()` has not supplied age gates. - `ApiNotAvailableException` — Android, Play Age Signals API unavailable (outdated Play Store or not installed from Google Play). - Other `AgeSignalsException` subclasses on API errors. ### Example ```dart final access = await AgeRangeSignals.instance.requestAgeSignalsAccess(); if (access != AgeSignalsAccessStatus.shared) { if (access == AgeSignalsAccessStatus.verificationRequired) { // Android: no in-app flow — send the user to the Play Store app. showVerifyInPlayStoreMessage(); } return; // Block: no signals are available. } ``` ### Error Handling Example ```dart try { final access = await AgeRangeSignals.instance.requestAgeSignalsAccess(); } on AgeSignalsException catch (e) { // Pretending access is fine here would make the guard useless, so the // plugin throws instead of reporting what it cannot honour. print('Access failed: ${e.runtimeType} ${e.message}'); } ``` ``` -------------------------------- ### Handle non-shared access statuses Source: https://github.com/zigapovhe/age_range_signals/blob/main/_autodocs/api-reference/age-range-signals.md Example of checking the access status and handling verificationRequired by showing a message to the user, then blocking if not shared. ```dart final access = await AgeRangeSignals.instance.requestAgeSignalsAccess(); if (access != AgeSignalsAccessStatus.shared) { if (access == AgeSignalsAccessStatus.verificationRequired) { // Android: no in-app flow — send the user to the Play Store app. showVerifyInPlayStoreMessage(); } return; // Block: no signals are available. } ``` -------------------------------- ### Switch on e.code for AgeSignalsException Source: https://github.com/zigapovhe/age_range_signals/blob/main/_autodocs/errors.md Shows how to switch programmatically on e.code (when non-null) and e.details (when the platform supplied diagnostics). The example checks for the 'PRESENTATION_CONTEXT_UNAVAILABLE' code, which indicates no foreground activity on Android. ```dart } on AgeSignalsException catch (e) { if (e.code == 'PRESENTATION_CONTEXT_UNAVAILABLE') { // Android: no foreground activity to present the prompt on. } print('${e.runtimeType}: ${e.message}'); } ``` -------------------------------- ### Simple call to showSignificantUpdateAcknowledgment Source: https://github.com/zigapovhe/age_range_signals/blob/main/_autodocs/api-reference/age-range-signals.md A basic usage example that calls the method with a sample update description. The future completes normally if the user acknowledges the sheet. ```dart await AgeRangeSignals.instance.showSignificantUpdateAcknowledgment( updateDescription: 'We added social features and public profiles to this app.', ); ``` -------------------------------- ### Initialize plugin with custom AgeSignalsMockData for supervised teen Source: https://github.com/zigapovhe/age_range_signals/blob/main/README.md Initializes with a custom mockData scenario for supervised teen (status: supervised, ageLower: 16, ageUpper: 17, installId: 'test_id'). The mockData argument lets you cover any other scenario from Dart without touching Kotlin. Real Play bands are 0-12, 13-15, 16-17; verified mocks default to an open-ended band starting at your highest gate. ```dart await AgeRangeSignals.instance.initialize( ageGates: [13, 16, 18], useMockData: true, mockData: const AgeSignalsMockData( status: AgeSignalsStatus.supervised, ageLower: 16, ageUpper: 17, installId: 'test_install_id', ), ); ``` -------------------------------- ### Migrate to 0.8.0: requestAgeSignalsAccess() before checkAgeSignals() Source: https://github.com/zigapovhe/age_range_signals/blob/main/README.md Shows the required migration from directly calling checkAgeSignals() to first requesting access and then reading signals only if access is shared. The before/after blocks are presented as one unit in the migration guide. ```dart // Before final result = await AgeRangeSignals.instance.checkAgeSignals(); ``` ```dart // After final access = await AgeRangeSignals.instance.requestAgeSignalsAccess(); if (access == AgeSignalsAccessStatus.shared) { final result = await AgeRangeSignals.instance.checkAgeSignals(); } ``` -------------------------------- ### Handling exceptions from showSignificantUpdateAcknowledgment Source: https://github.com/zigapovhe/age_range_signals/blob/main/_autodocs/api-reference/age-range-signals.md A try-catch example that handles the different exceptions that can be thrown: UserCancelledException, ApiNotAvailableException, and UnsupportedPlatformException. Note that ApiNotAvailableException may also indicate dismissal, and UnsupportedPlatformException should never be silently ignored. ```dart try { await AgeRangeSignals.instance.showSignificantUpdateAcknowledgment( updateDescription: 'New in-app purchases are now available.', ); // Acknowledged. } on UserCancelledException { // Person dismissed or cancelled the sheet. } on ApiNotAvailableException { // Unavailable — or the person dismissed it (Apple reports both the same way). } on UnsupportedPlatformException { // Android or iOS <26.4: never silently succeed here. } ``` -------------------------------- ### initialize(List ageGates) Source: https://github.com/zigapovhe/age_range_signals/blob/main/llms.txt Initializes the plugin with age gates. Must be called on both platforms before requesting access. ```APIDOC ## initialize(List ageGates) ### Description Initializes the plugin with the age gates used for age verification. Must be called on both platforms before requesting access. On iOS, ageGates are required and must contain 1 to 3 gates at least 2 years apart. On Android, gates are ignored by the OS but the highest gate is used as the bar for the 'verified' status. ### Method initialize ### Parameters - **ageGates** (List) - Required - List of age gates, e.g., [13, 16, 18]. ### Example ```dart await AgeRangeSignals.instance.initialize(ageGates: [13, 16, 18]); ``` ``` -------------------------------- ### initialize Source: https://github.com/zigapovhe/age_range_signals/blob/main/_autodocs/configuration.md Documentation for the runtime configuration method initialize() of the AgeRangeSignals plugin. ```APIDOC ## [initialize] AgeRangeSignals.instance.initialize(...) ### Description Initializes the AgeRangeSignals plugin with runtime configuration. This is the only runtime configuration surface for the plugin. It must be called before using the plugin's verdict functionality, especially on iOS where gates are required. ### Method initialize ### Endpoint AgeRangeSignals.instance.initialize(...) ### Parameters #### Request Body - **ageGates** (List?) - Optional - Age thresholds used for verdict calculation. iOS: required; without gates, iOS calls throw NotInitializedException. Android: ignored by the API itself, but the plugin uses the highest gate as the bar verified is measured against; until gates are supplied Android uses 18. Apple accepts 1 to 3 gates with at least 2 years between consecutive gates. iOS sorts the list. - **useMockData** (bool) - Optional - Android only. When true, uses Google's FakeAgeSignalsManager instead of the real Play API. Refused (throws MockDataNotAllowedException) in non-debuggable builds. Silently ignored on iOS. - **mockData** (AgeSignalsMockData?) - Optional - Android only. Customizes the fake manager response; when null, the default mock is a supervised user (tierB, 13–15, installId: "test_install_id_12345"). Ignored on iOS. ### Response #### Success Response No return value specified. ### Error Handling - **NotInitializedException** - Thrown on iOS if ageGates are not provided. - **MockDataNotAllowedException** - Thrown on Android if useMockData is true in a non-debuggable build. - **Invalid request error** - Thrown by Apple OS if ageGates are invalid (e.g., [13, 14] with less than 2 years between gates). ``` -------------------------------- ### Check age signals for generally available app (no age restrictions) Source: https://github.com/zigapovhe/age_range_signals/blob/main/README.md Use for an app with no age restrictions. Initialize with broad gates to get a range on iOS, but never block on errors and do not feed results into analytics (Play's terms forbid it). The example prints the status or catches AgeSignalsException with a non-blocking fallback. ```dart import 'package:age_range_signals/age_range_signals.dart'; Future checkAgeSignalsOnce() async { await AgeRangeSignals.instance.initialize(ageGates: [13, 16, 18]); try { final access = await AgeRangeSignals.instance.requestAgeSignalsAccess(); if (access != AgeSignalsAccessStatus.shared) return; final result = await AgeRangeSignals.instance.checkAgeSignals(); print('Age signals status: ${result.status}'); } on AgeSignalsException catch (e) { // Never block the app on this. print('Age signals error: ${e.message}'); } } ``` -------------------------------- ### Basic initialization with age gates Source: https://github.com/zigapovhe/age_range_signals/blob/main/_autodocs/api-reference/age-range-signals.md Initializes the plugin for production on both platforms with age gates [13, 16, 18]. ```dart // Basic: production, both platforms await AgeRangeSignals.instance.initialize(ageGates: [13, 16, 18]); ``` -------------------------------- ### showSignificantUpdateAcknowledgment Source: https://github.com/zigapovhe/age_range_signals/blob/main/_autodocs/api-reference/age-range-signals.md Documentation for the showSignificantUpdateAcknowledgment method, including parameters, return type, exceptions, and usage examples. ```APIDOC ## Method: `showSignificantUpdateAcknowledgment` ### Description Shows Apple's system acknowledgment sheet for significant updates (iOS 26.4+). The method completes normally when the person acknowledges the sheet; every other outcome is an exception. ### Method `showSignificantUpdateAcknowledgment` ### Signature ```dart Future showSignificantUpdateAcknowledgment({ required String updateDescription, }) ``` ### Parameters - **updateDescription** (`String`) - Required - Shown to the user inside Apple's system acknowledgment sheet (iOS 26.4+). Passed to `AgeRangeSignalsPlatform.showSignificantUpdateAcknowledgment`. ### Return Type `Future` — completing normally means the person acknowledged the sheet. Every other outcome is an exception. ### Argument Validation Throws `ArgumentError.value(updateDescription, 'updateDescription', 'must not be empty; it is shown to the user inside the system sheet')` when `updateDescription.trim().isEmpty`. ### Throws - `UnsupportedPlatformException` — on Android and on iOS below 26.4 / pre-26.4 SDK. Deliberate: silently succeeding would let an app believe it satisfied a notification duty when no sheet was shown. - `ApiNotAvailableException` — Apple reports the acknowledgment unavailable. Apple uses the same error when the person dismisses the sheet, so do not treat this as proof the sheet never appeared. - `UserCancelledException` — explicit cancellation (including iOS 27's `declinedOnboarding`). - `ApiErrorException` — other API failures, including no window scene to present on (`PRESENTATION_CONTEXT_UNAVAILABLE`). ### Example ```dart await AgeRangeSignals.instance.showSignificantUpdateAcknowledgment( updateDescription: 'We added social features and public profiles to this app.', ); ``` ### Example with Error Handling ```dart try { await AgeRangeSignals.instance.showSignificantUpdateAcknowledgment( updateDescription: 'New in-app purchases are now available.', ); // Acknowledged. } on UserCancelledException { // Person dismissed or cancelled the sheet. } on ApiNotAvailableException { // Unavailable — or the person dismissed it (Apple reports both the same way). } on UnsupportedPlatformException { // Android or iOS <26.4: never silently succeed here. } ``` ``` -------------------------------- ### AgeRangeSignals.instance.initialize Source: https://github.com/zigapovhe/age_range_signals/blob/main/_autodocs/api-reference/age-range-signals.md Initializes the AgeRangeSignals plugin with optional age gates and mock data. Must be called before checkAgeSignals(). ```APIDOC ## initialize ### Description Initializes the AgeRangeSignals plugin with optional age gates and mock data. Must be called before checkAgeSignals(). ### Method initialize ### Signature ```dart Future initialize({ List? ageGates, bool useMockData = false, AgeSignalsMockData? mockData, }) ``` ### Parameters - **ageGates** (`List?`) - Optional - Age thresholds. iOS requires them (throws `NotInitializedException` without them). Android ignores them for the API but uses the highest gate as the bar `verified` is measured against — 18 until supplied, and a later call that omits them keeps the gates already set. Apple accepts 1–3 gates with at least 2 years between consecutive gates (`[13, 14]` is rejected by the OS; `[13, 16, 18]` is valid). - **useMockData** (`bool`) - Optional, default `false` - Android only. When `true`, uses Google's `FakeAgeSignalsManager` instead of the real API. Refused with `MockDataNotAllowedException` outside debuggable builds. Ignored on iOS. - **mockData** (`AgeSignalsMockData?`) - Optional - Android only. Customizes the fake manager response; when `null` a default supervised (13–15, tierB) mock is used. Ignored on iOS. ### Return Type `Future` — completes when the platform has applied the configuration. ### Throws - `MockDataNotAllowedException` — Android, `useMockData: true` in a non-debuggable build. - On iOS, `useMockData`/`mockData` are silently ignored. ### Request Example ```dart // Basic: production, both platforms await AgeRangeSignals.instance.initialize(ageGates: [13, 16, 18]); ``` ```dart // Android testing with a custom mock scenario try { await AgeRangeSignals.instance.initialize( useMockData: true, mockData: AgeSignalsMockData( status: AgeSignalsStatus.supervised, ageLower: 16, ageUpper: 17, ), ); } on MockDataNotAllowedException catch (e) { // useMockData was requested in a release build — the plugin refuses to // let FakeAgeSignalsManager reach real users. print('Mock data requires a debuggable build: ${e.message}'); } ``` ### Response No response body; completes with `void`. ``` -------------------------------- ### Initialize with mock data for a supervised user Source: https://github.com/zigapovhe/age_range_signals/blob/main/_autodocs/api-reference/age-signals-mock-data.md Initializes the plugin with mock data for a supervised user, setting the status to supervised and an age band of 16-17. This is Android-only. ```dart // Basic: mock a supervised user (Android only) await AgeRangeSignals.instance.initialize( useMockData: true, mockData: AgeSignalsMockData( status: AgeSignalsStatus.supervised, ageLower: 16, ageUpper: 17, ), ); ``` -------------------------------- ### getRequiredRegulatoryFeatures Source: https://github.com/zigapovhe/age_range_signals/blob/main/_autodocs/api-reference/age-range-signals.md Documentation for the `getRequiredRegulatoryFeatures` method, including its signature, return type, platform notes, exceptions, and a usage example. ```APIDOC ## `getRequiredRegulatoryFeatures` ### Description Returns the set of `AgeRegulatoryFeature` values that Apple reports as required for the current user (iOS 26.4+), derived from region and account settings. An empty set means none of the known features apply, but this is not clearance on its own — check `isEligibleForAgeFeatures()` before treating an empty set as permission to skip the prompt. ### Method `getRequiredRegulatoryFeatures` ### Signature ```dart Future> getRequiredRegulatoryFeatures() ``` ### Parameters None. ### Return Type `Future>` — the set of regulatory features required for the current user. ### Platform Notes - On Android the set is always empty: Play has no equivalent concept and implicitly limits itself to covered regions. - iOS below 26.4 and pre-26.4 SDKs throw rather than return an empty set, keeping the empty set unambiguous. ### Exceptions - `UnsupportedPlatformException` — iOS below 26.4 / pre-26.4 SDK. - `ApiNotAvailableException` — Apple reports the service unavailable. - Other `AgeSignalsException` subclasses on API errors. - `ApiErrorException` — timeout (guarded by the 10-second deadline). ### Example ```dart final features = await AgeRangeSignals.instance.getRequiredRegulatoryFeatures(); if (features.contains(AgeRegulatoryFeature.declaredAgeRangeRequired)) { final result = await AgeRangeSignals.instance.checkAgeSignals(); } if (features .contains(AgeRegulatoryFeature.significantAppChangeRequiresAdultNotification)) { await AgeRangeSignals.instance.showSignificantUpdateAcknowledgment( updateDescription: 'We added social features and public profiles.', ); } ``` ``` -------------------------------- ### Catch AgeSignalsException on access request Source: https://github.com/zigapovhe/age_range_signals/blob/main/_autodocs/api-reference/age-range-signals.md Example of wrapping the access request in a try-catch to handle AgeSignalsException, printing the error type and message. ```dart try { final access = await AgeRangeSignals.instance.requestAgeSignalsAccess(); } on AgeSignalsException catch (e) { // Pretending access is fine here would make the guard useless, so the // plugin throws instead of reporting what it cannot honour. print('Access failed: ${e.runtimeType} ${e.message}'); } ``` -------------------------------- ### Stubbing the plugin in a widget test Source: https://github.com/zigapovhe/age_range_signals/blob/main/_autodocs/api-reference/platform-and-method-channel.md Shows how to create a fake platform implementation using MockPlatformInterfaceMixin and set it as the instance in a test. The test verifies that AgeRangeSignals delegates to the platform. ```dart // Stubbing the plugin in a widget test. class _FakePlatform extends AgeRangeSignalsPlatform with MockPlatformInterfaceMixin { bool eligible = false; @override Future isEligibleForAgeFeatures() async => eligible; } void main() { test('delegates to the platform', () async { AgeRangeSignalsPlatform.instance = _FakePlatform(); expect(await AgeRangeSignals.instance.isEligibleForAgeFeatures(), isFalse); }); } ``` -------------------------------- ### initialize method override Source: https://github.com/zigapovhe/age_range_signals/blob/main/_autodocs/api-reference/platform-and-method-channel.md Invokes the 'initialize' channel method with ageGates, useMockData, and mockData (as a map or null). Returns void on success; remaps PlatformException to a custom exception. ```dart @override Future initialize({ List? ageGates, bool useMockData = false, AgeSignalsMockData? mockData, }) async { try { await methodChannel.invokeMethod('initialize', { 'ageGates': ageGates, 'useMockData': useMockData, 'mockData': mockData?.toMap(), }); } on PlatformException catch (e) { throw _handlePlatformException(e); } } ``` -------------------------------- ### initialize abstract method Source: https://github.com/zigapovhe/age_range_signals/blob/main/_autodocs/api-reference/platform-and-method-channel.md Base implementation throws UnimplementedError. Platforms must override. The doc comment notes that iOS requires ageGates, Play ignores them, but Android uses the highest gate as the bar for verified, using 18 until gates are supplied and keeping them if a later call omits them. ```dart Future initialize({ List? ageGates, bool useMockData = false, AgeSignalsMockData? mockData, }) { throw UnimplementedError('initialize() has not been implemented.'); } ``` -------------------------------- ### Usage example: getRequiredRegulatoryFeatures Source: https://github.com/zigapovhe/age_range_signals/blob/main/_autodocs/api-reference/age-range-signals.md Shows how to call getRequiredRegulatoryFeatures and conditionally check age signals or show a significant update acknowledgment based on the returned features. ```dart final features = await AgeRangeSignals.instance.getRequiredRegulatoryFeatures(); if (features.contains(AgeRegulatoryFeature.declaredAgeRangeRequired)) { final result = await AgeRangeSignals.instance.checkAgeSignals(); } if (features .contains(AgeRegulatoryFeature.significantAppChangeRequiresAdultNotification)) { await AgeRangeSignals.instance.showSignificantUpdateAcknowledgment( updateDescription: 'We added social features and public profiles.', ); } ``` -------------------------------- ### Constructor and singleton mechanics for AgeRangeSignalsPlatform Source: https://github.com/zigapovhe/age_range_signals/blob/main/_autodocs/api-reference/platform-and-method-channel.md Defines the constructor with token verification and the static singleton instance. The default instance is created eagerly at library load, so AgeRangeSignals.instance works without registration. ```dart AgeRangeSignalsPlatform() : super(token: _token); static final Object _token = Object(); static AgeRangeSignalsPlatform _instance = MethodChannelAgeRangeSignals(); static AgeRangeSignalsPlatform get instance => _instance; static set instance(AgeRangeSignalsPlatform instance) { PlatformInterface.verifyToken(instance, _token); _instance = instance; } ``` -------------------------------- ### Singleton constructor and instance getter Source: https://github.com/zigapovhe/age_range_signals/blob/main/_autodocs/api-reference/age-range-signals.md Shows the private constructor and static instance getter used to seed the singleton. The instance is created eagerly at library load and is never replaced. ```dart static AgeRangeSignals get instance => _instance; static final AgeRangeSignals _instance = AgeRangeSignals._(); AgeRangeSignals._(); ``` -------------------------------- ### Initialize and check age signals with AgeRangeSignals Source: https://github.com/zigapovhe/age_range_signals/blob/main/llms.txt Initialize the plugin with age gates, request access, and check age signals only if access is shared. Call initialize() on both platforms; iOS requires ageGates and throws NotInitializedException without them, while Android uses the highest gate as the bar for verified. ```dart await AgeRangeSignals.instance.initialize(ageGates: [13, 16, 18]); final access = await AgeRangeSignals.instance.requestAgeSignalsAccess(); if (access == AgeSignalsAccessStatus.shared) { final result = await AgeRangeSignals.instance.checkAgeSignals(); } ``` -------------------------------- ### Usage example for isEligibleForAgeFeatures Source: https://github.com/zigapovhe/age_range_signals/blob/main/_autodocs/api-reference/age-range-signals.md Shows the optional first step in Apple's documented flow: call isEligibleForAgeFeatures() and handle UnsupportedPlatformException with your own regional fallback. If obligated, then call checkAgeSignals(). Note that checkAgeSignals() never calls isEligibleForAgeFeatures() for you. ```dart bool obligated; try { obligated = await AgeRangeSignals.instance.isEligibleForAgeFeatures(); } on UnsupportedPlatformException { obligated = yourOwnRegionalFallback(); // iOS <26.2 or pre-26.2 SDK } if (obligated) { final result = await AgeRangeSignals.instance.checkAgeSignals(); } ``` -------------------------------- ### Initialize plugin with useMockData: true and check default mock age signals Source: https://github.com/zigapovhe/age_range_signals/blob/main/README.md Initializes the plugin with mock data and verifies the default supervised mock scenario. Without a mockData argument, FakeAgeSignalsManager returns a supervised user in Play's 13-15 band (or 0-12 if highest gate is 15 or lower). Use mock data only in debuggable builds; otherwise MockDataNotAllowedException is thrown. ```dart await AgeRangeSignals.instance.initialize( ageGates: [13, 16, 18], useMockData: true, ); final result = await AgeRangeSignals.instance.checkAgeSignals(); print(result.status); // AgeSignalsStatus.supervised print(result.ageLower); // 13 print(result.ageUpper); // 15 print(result.installId); // "test_install_id_12345" ``` -------------------------------- ### Method toMap Source: https://github.com/zigapovhe/age_range_signals/blob/main/_autodocs/api-reference/age-signals-mock-data.md Converts the mock data to the map sent over the platform channel. Keys include status, ageLower, ageUpper, source, installId, accessStatus, ageRangeSource, significantChangeStatus (enum names, null when unset), and significantChangeApprovalDate (epoch milliseconds). MethodChannelAgeRangeSignals.initialize calls mockData?.toMap() and passes the map as the mockData channel argument. ```dart Map toMap() ```