skip to Main Content

Each time I run my App, the Notification dialog comes way before the App tracking authorization. I’d like the user to see at first the App tracking Transparency dialog before the Notification dialog. I tried requesting the tracking authorization await AppTrackingTransparency.requestTrackingAuthorization(); before the runApp() or after it and above the Push Notification request await PushNotificationService().setupInteractedMessage(); and added the key in info.plist, however, the result is the same, notification dialog comes first. Kindly check my code.

int? initScreen;

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // await Firebase.initializeApp();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  // runApp(MyApp());
  SharedPreferences preferences = await SharedPreferences.getInstance();
  initScreen = await preferences.getInt('initScreen');
  await preferences.setInt('initScreen', 1);
  // MobileAds.instance.initialize();
  runApp(MyApp());
  //await AppTracking
  // final status = await AppTrackingTransparency.requestTrackingAuthorization();
  await AppTrackingTransparency.requestTrackingAuthorization();
  await PushNotificationService().setupInteractedMessage();
  MobileAds.instance.initialize();
  RemoteMessage? initialMessage =
      await FirebaseMessaging.instance.getInitialMessage();
  if (initialMessage != null) {
    // App received a notification when it was killed
  }
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  // final TrackingStatus status;
  //
  // MyApp({required this.status});

  static FirebaseAnalytics analytics = FirebaseAnalytics.instance;
  static FirebaseAnalyticsObserver observer =
      new FirebaseAnalyticsObserver(analytics: analytics);

  @override
  Widget build(BuildContext context) {
    // String statusText;

    // switch (status) {
    //   case TrackingStatus.authorized:
    //     statusText = 'Tracking Status Authorized';
    //     break;
    //   case TrackingStatus.denied:
    //     statusText = 'Tracking Status Denied';
    //     break;
    //   case TrackingStatus.notDetermined:
    //     statusText = 'Tracking Status Not Determined';
    //     break;
    //   case TrackingStatus.notSupported:
    //     statusText = 'Tracking Status Not Supported';
    //     break;
    //   case TrackingStatus.restricted:
    //     statusText = 'Tracking Status Restricted';
    //     break;
    //   default:
    //     statusText = 'You should not see this...';
    //     break;
    // }

    return GetMaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Culture Islamique',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      // home:

      initialRoute: initScreen == 0 || initScreen == null ? 'onboard' : 'home',
      routes: {
        'home': (context) => navbar(analytics: analytics, observer: observer),
        'onboard': (context) => onboardingHomePage(),
      },
    );
  }
}```

[![enter image description here][1]][1]


  [1]: https://i.stack.imgur.com/R3eHd.png

2

Answers


  1. Chosen as BEST ANSWER

    IOS does not allow to display multiple native dialogs.

    Here is my workaround inspired by the example provided by the app-tracking-transparency package on pub.dev

    First, I removed the await AppTrackingTransparency.requestTrackingAuthorization(); from main instead call it in the HomeScreen (navbar).

    Next, in the HomeScreen call this function initPlugin() in initState() see the code.

    class navbar extends StatefulWidget {
      const navbar({
        Key? key,
        required this.analytics,
        required this.observer,
      }) : super(key: key);
    
      final FirebaseAnalytics analytics;
      final FirebaseAnalyticsObserver observer;
    
      @override
      _navbarState createState() => _navbarState();
    }
    
    class _navbarState extends State<navbar> {
      String _authStatus = 'Unknown';
      int currentindex = 0;
    
    
      // Platform messages are asynchronous, so we initialize in an async method.
      Future<void> initPlugin() async {
        final TrackingStatus status =
            await AppTrackingTransparency.trackingAuthorizationStatus;
        setState(() => _authStatus = '$status');
        // If the system can show an authorization request dialog
        if (status == TrackingStatus.notDetermined) {
          // Show a custom explainer dialog before the system dialog
          await showCustomTrackingDialog(context);
          // Wait for dialog popping animation
          await Future.delayed(const Duration(milliseconds: 200));
          // Request system's tracking authorization dialog
          final TrackingStatus status =
              await AppTrackingTransparency.requestTrackingAuthorization();
          setState(() => _authStatus = '$status');
        }
    
        // final uuid = await AppTrackingTransparency.getAdvertisingIdentifier();
        final uuid = await AppTrackingTransparency.getAdvertisingIdentifier();
        print("UUID: $uuid");
      }
    
      Future<void> showCustomTrackingDialog(BuildContext context) async =>
          await showDialog<void>(
            context: context,
            builder: (context) => AlertDialog(
              title: const Text('Dear User'),
              content: const Text(
                "We care about your privacy and data security. We keep this app free by showing ads. Can we continue to use your data to tailor ads for you?nnYou can change your choice anytime in the app settings. Our partners will collect data and use a unique identifier on your device to show you ads.",
              ),
              actions: [
                TextButton(
                  onPressed: () => Navigator.pop(context),
                  style: TextButton.styleFrom(
                      backgroundColor: color.AppColor.homePageDetail),
                  child: const Text(
                    'Continue',
                    style: TextStyle(color: Colors.white),
                  ),
                ),
              ],
            ),
          );
    
      @override
      void initState() {
        super.initState();
    // It is safer to call native code using addPostFrameCallback after the widget has been fully built and initialized.
        // Directly calling native code from initState may result in errors due to the widget tree not being fully built at that point. 
        WidgetsFlutterBinding.ensureInitialized()
            .addPostFrameCallback((_) => initPlugin());
        print("Tracking status: $_authStatusn");
      }
    
      List ListOfColors = [
        HomeScreen(),
        All(),
        ProfileScreen(),
      ];
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: ListOfColors[currentindex],
          bottomNavigationBar: BottomNavyBar(
            selectedIndex: currentindex,
            onItemSelected: (index) {
              setState(
                () {
                  interstitialAd?.show();
                  currentindex = index;
                  print("$_authStatus");
                },
              );
            },
            items: [
              BottomNavyBarItem(
                  icon: Icon(Icons.question_answer),
                  title: Text("Quiz"),
                  activeColor: Color(0xFF6985e8),
                  inactiveColor: Color(0xffa2a2b1)),
              BottomNavyBarItem(
                  icon: Icon(Icons.book),
                  title: Text("Histoires "),
                  activeColor: Color(0xFF6985e8),
                  inactiveColor: Color(0xffa2a2b1)),
              BottomNavyBarItem(
                  icon: Icon(Icons.settings),
                  title: Text("Paramètres"),
                  activeColor: Color(0xFF6985e8),
                  inactiveColor: Color(0xffa2a2b1)),
            ],
          ),
        );
      }
    }
    
    

    After that, Once you run your App, the notification permission will come first, followed by the explanatory dialog then the app tracking authorization permission will be requested. This will prevent your app from being rejected from the App Store.

    For more info check the [example][1]

    [1]: https://pub.dev/packages/app_tracking_transparency/example on pub.dev


  2. To ensure that the tracking transparency permission dialog appears before the notification permission dialog in your Flutter iOS app, you need to rearrange your code to request the tracking transparency permission before initializing Firebase and setting up push notifications. Here’s how you can modify your main() function:

    import 'package:flutter/material.dart';
    import 'package:firebase_core/firebase_core.dart';
    import 'package:shared_preferences/shared_preferences.dart';
    import 'package:firebase_analytics/firebase_analytics.dart';
    import 'package:firebase_analytics/observer.dart';
    import 'package:flutter_app_tracking_transparency/flutter_app_tracking_transparency.dart';
    import 'package:firebase_messaging/firebase_messaging.dart';
    import 'package:google_mobile_ads/google_mobile_ads.dart';
    
    Future<void> main() async {
      WidgetsFlutterBinding.ensureInitialized();
    
      // Request tracking transparency permission
      await requestTrackingPermission();
    
      // Initialize Firebase
      await Firebase.initializeApp();
    
      // Set up push notifications
      await setupPushNotifications();
    
      // Initialize Google Mobile Ads
      MobileAds.instance.initialize();
    
      // Get initial message if app was launched from a notification
      await handleInitialMessage();
    
      // Run the app
      runApp(MyApp());
    }
    
    Future<void> requestTrackingPermission() async {
      await AppTrackingTransparency.requestTrackingAuthorization();
    }
    
    Future<void> setupPushNotifications() async {
      // Set up push notifications here
      // For example, using Firebase Cloud Messaging
      FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
      FirebaseMessaging.onMessage.listen((RemoteMessage message) {
        // Handle incoming message when the app is in the foreground
      });
      FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
        // Handle notification when the app is in the background or terminated
      });
    }
    
    Future<void> handleInitialMessage() async {
      RemoteMessage? initialMessage =
          await FirebaseMessaging.instance.getInitialMessage();
      if (initialMessage != null) {
        // Handle the initial message if app was launched from a notification
      }
    }
    
    void _firebaseMessagingBackgroundHandler(RemoteMessage message) {
      // Handle background messages
    }
    
    class MyApp extends StatelessWidget {
      static FirebaseAnalytics analytics = FirebaseAnalytics();
      static FirebaseAnalyticsObserver observer = FirebaseAnalyticsObserver(analytics: analytics);
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Your App',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          navigatorObservers: [observer],
          home: // Your home screen widget,
        );
      }
    }
    

    In this modified code:

    1. Here we have requestTrackingPermission() function, which requests tracking transparency permission using the flutter_app_tracking_transparency package.
    2. setupPushNotifications() function handles the setup for push notifications using Firebase Messaging.
    3. handleInitialMessage() function handles the initial message when the app is launched from a notification.
    4. These functions are called sequentially in the main() function before initializing Firebase, setting up push notifications, and running the app. This ensures that the tracking transparency permission dialog appears before any other dialogs related to permissions or services.
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search