Moving Google Tag Manager from Xamarin to Flutter
Tags: xamarin, flutter, mobile, googletagmanager
Published: 2022-04-26 16:12:42

While working with a major theater chain, I was tasked with implementing Analytics into their Xamarin app. Part of that work included implementing Google Tag Manager. At a later date, I was again tasked with implementing Google Tag Manager but this time, into their Flutter app. I expected the implementation to be similar and definitely easier the second time around, but I found that to be false. The setup had some major twist and turns that I didn't expect.

Although this was sometimes a frustrating challenge, I had fun figuring it out. Most companies running their apps with Xamarin will be moving their apps to a newer SDK like Flutter at some point. Flutter allows cross platform development for Android, iOS, Linux, macOS, Windows, Google Fuchsia, and Web from a single codebase and shared languages. So this move is almost inevitable.

This is where the idea for this blog came from. The transition is complicated, and I could have used a blog like this one to explain the transition to Flutter. Plus, even if you haven’t been faced with this shift yet, there’s a good chance you will in the future. Hopefully, whether it’s now or at a later date, this post will save some of you from spending painful time and effort transitioning.

**In this post, I’ll take you through a step-by-step tutorial of transferring from Xamarin to Flutter. By the end, GTM will be in your Flutter app! Before we get started, I’ve listed a brief overview of the steps below.**

> This post was originally published on the [Keyhole Software blog](https://keyholesoftware.com/2022/04/22/moving-google-tag-manager-from-xamarin-to-flutter/) by [Andy Link](https://keyholesoftware.com/author/alink/) on 4/22/2022.

<b>Android</b>
<ol>
 	<li>Grab the Container Files from Xamarin</li>
 	<li>Copy the Environment Container Files into Flutter</li>
 	<li>Add the Default Container File</li>
 	<li>Create the Python Copy Script for Prod</li>
 	<li>Add the GTM Dependency</li>
</ol>
<b>iOS</b>
<ol>
 	<li>Grab the Container Files from Xamarin</li>
 	<li>Copy the Environment Container Files into Flutter</li>
 	<li>Add the Default Container File</li>
 	<li>Create the Xcode Build Phase (prod Run Script)</li>
 	<li>Add the GTM Dependency</li>
</ol>
No matter how daunting a task this may seem, together, we’ll get through it. This blog should seriously help ease your pain when moving your current GTM setup in Xamarin to Flutter. Let’s get started!
<h2>Android</h2>
We’ll begin with Android.
<h3>Step 1: Grab the Android Container Files from Xamarin</h3>
First, let's grab all the <code>GTM-XXXXXXX.json</code> container files from your Droid Xamarin project. The files will be located in <code>Droid\Assets\containers</code>.

<img class="alignnone wp-image-27083" src="https://keyholesoftware.com/wp-content/uploads/Link-1.png" alt="" width="375" height="294" />

Copy these files to your Desktop, so you can easily access and copy them later.

<img class="alignnone wp-image-27084" src="https://keyholesoftware.com/wp-content/uploads/Link-2.jpg" alt="" width="737" height="170" />

Great, you now have your Android container files ready to copy into your Flutter app!
<h3>Step 2: Copy your Android Environment Container Files into Flutter</h3>
You can place these container files anywhere inside the <code>android\app</code> folder. For my app, I’ve placed them under <code>android\app\google_services\dev\containers</code> and <code>android\app\google_services\prod\containers</code>.

<img class="alignnone wp-image-27085" src="https://keyholesoftware.com/wp-content/uploads/Link-3.png" alt="" width="377" height="307" />

The new android folders will act as a storing location for each environment's container file. These files won’t directly be used during runtime, but one of these will be copied into the proper runtime folder during app build.
<h3>Step 3: Add the Default Android Container File</h3>
If you need/want GTM setup for local android development, you’ll still need to add the dev container file inside <code>android\app\src\main\containers</code> as the default file. This way, Flutter can always reference a file during runtime without any file movement.

<img class="alignnone wp-image-27086" src="https://keyholesoftware.com/wp-content/uploads/Link-4.png" alt="" width="377" height="363" />
<p style="padding-left: 40px;"><b><i>Note:</i></b> The <a href="https://developers.google.com/tag-platform/tag-manager/android/v5#add-gtm-to-project" target="_blank" rel="noopener">official documents</a> state you’ll need your container file inside the <code>android\app\assets\containers</code> folder during runtime. My setup was a tad different since my app requires this file be in the <code>android\app\src\main\assets\containers</code> folder. You’ll have to confirm which location works for you.</p>
For my setup, I not only wanted GTM working locally for Android, but I also wanted to use the same dev container file for every environment outside of the prod environment. Adding the dev container file into <code>android\app\src\main\containers</code> as the default file has kept me from needing to create multiple copy scripts for each environment. There’s only a need for one copy script for prod!
<h3>Step 4: Create the Android Prod Copy Script</h3>
In Xamarin, the environment container files existed side by side in the <code>Droid\Assets\containers</code> folder. This was only possible because the <code>Droid.csproj</code> file controlled which file the project included based on the project's current configuration (ex. Debug, Dev, Test, Prod).

<img class="alignnone wp-image-27087" src="https://keyholesoftware.com/wp-content/uploads/Link-5.jpg" alt="" width="658" height="176" />

In Xamarin and Flutter, it's <b>REQUIRED</b> that only a single Android container file is included in the <code>containers</code> folder during runtime. Otherwise, the app doesn’t know which file to use and will use the first container file it finds. This is a no go if you care about your test data being logged in production and vice-versa!

Since Flutter doesn’t have a <code>Droid.csproj</code> file to accomplish the Xamarin solution, we’ll need to create another solution to swap the container file inside Flutter’s <code>android\app\assets\containers</code> folder.

For my Flutter app, the easiest solution was to create a python script that Jenkins would execute. Inside my Jenkins folder, I added a new <code>android_prod.py</code> file.

<img class="alignnone wp-image-27088" src="https://keyholesoftware.com/wp-content/uploads/Link-6.png" alt="" width="437" height="296" />

Then, I added this code:

```
import os
import shutil
 
## Override Google Tag Manager container file with the PROD version
# Deletes GTM file in containers folder
os.remove("android/app/src/main/assets/containers/GTM-XXXXXXX.json")
# Copies Prod GTM File to the needed containers folder
shutil.copyfile("android/app/google_services/prod/containers/GTM-XXXXXXX.json", "android/app/src/main/assets/containers/GTM-XXXXXXX.json")
```

<p style="padding-left: 40px;"><b><i>Note:</i></b> Replace the <code>GTM-XXXXXXXX.json</code> with your appropriate dev and prod container file names.</p>
Your setup should now work for both dev and prod environments. We’re almost done with the Android setup. The last step is to install the GTM packages.
<h3>Step 5: Add the Android GTM Dependency</h3>
Add…

```
compile "com.google.android.gms:play-services-tagmanager:17.0.0" 
```

…as a dependency to your <code>app\build.gradle</code> file like so.

<img class="alignnone wp-image-27089" src="https://keyholesoftware.com/wp-content/uploads/Link-7.jpg" alt="" width="730" height="184" />

And now the Android setup is complete! Nice work. Let’s move to the iOS setup.
<h2>iOS</h2>
<h3>Step 1:Grab the iOS Container Files from Xamarin</h3>
First, let's grab all the <code>GTM-XXXXXXX.json</code> container files from your iOS Xamarin project. The files will be located in <code>iOS\container</code>.

<img class="alignnone wp-image-27090" src="https://keyholesoftware.com/wp-content/uploads/Link-8.png" alt="" width="388" height="367" />

Copy these files to your Desktop so you can easily access and copy them later. Now, on your Desktop, create a folder called container and create subfolders called dev and prod inside.

<img class="alignnone wp-image-27091" src="https://keyholesoftware.com/wp-content/uploads/Link-9.jpg" alt="" width="681" height="326" />

Copy the iOS dev and prod GTM container files you pulled from Xamarin into each subfolder.

Great, you now have your iOS container files ready to copy into your Flutter app!
<h3>Step 2: Copy the iOS Environment Container Files into Flutter</h3>
First, open your project in Xcode.

Next, you’ll copy that new container folder you created on your Desktop into the Xcode project navigator. Copy it inside to the <code>Runner\Runner</code> folder.
<p style="padding-left: 40px;"><b><i>Note:</i></b> It’s required that you add the container folder through Xcode. Xcode won’t see the folder reference if you add it through the Mac Finder file explorer.</p>
<img class="alignnone wp-image-27094" src="https://keyholesoftware.com/wp-content/uploads/Link-10.jpg" alt="" width="366" height="384" />

The new ios folders will act as a storing location for each environment's container file. These files won’t directly be used during runtime, but one of these will be copied into the proper runtime folder during app build.
<h3>Step 3: Add the Default iOS Container File</h3>
If you need/want GTM setup for local iOS development, you’ll still need to add the dev container file inside <code>Runner\Runner\container</code> as the default file. This way, Flutter can always reference a file during runtime without any file movement.

<img class="alignnone wp-image-27095" src="https://keyholesoftware.com/wp-content/uploads/Link-11.jpg" alt="" width="354" height="336" />
<p style="padding-left: 40px;"><b><i>Note:</i></b> The <a href="https://developers.google.com/tag-platform/tag-manager/ios/v5" target="_blank" rel="noopener">official documents</a> state you’ll need to put your container file inside the <code>ios\Runner\container</code> folder during runtime.</p>
For my setup, I not only wanted GTM working locally for iOS, but I also wanted to use the same dev container file for every environment outside of the prod environment. Adding the dev container file into <code>ios\Runner\container</code> as the default file has kept me from needing to create multiple copy scripts for each environment. There’s only a need for one copy script for prod!
<h3>Step 4: Create the iOS Xcode Build Phase (prod Run Script)</h3>
In Xamarin, the environment container files existed side by side in the <code>iOS\container</code> folder. This was only possible because the <code>iOS.csproj</code> file controlled which file the project included based on the project's current configuration (ex. Debug, Dev, Test, Prod).

<img class="alignnone wp-image-27096" src="https://keyholesoftware.com/wp-content/uploads/Link-12.jpg" alt="" width="594" height="159" />

In Xamarin and Flutter it's <b>REQUIRED</b> that only a single iOS container file is included in the <code>container</code> folder during runtime. Otherwise, the app doesn’t know which file to use and will use the first container file it finds. This is a no go if you care about your test data being logged in production and vice-versa!

Since Flutter doesn’t have a <code>iOS.csproj</code> file, to accomplish the Xamarin solution, we’ll need to create another solution to swap the container file inside Flutter’s <code>ios\Runner\container</code> folder.

For my Flutter app, the easiest solution was to create a Build Phase (Run Script) inside Xcode.

In Xcode, go to <code>Runner</code> -&gt; <code>Targets Runner</code> -&gt; <code>Build Phase</code> and click the <code>+</code> button to create a new Run Script. Call it <i>Copy environments GoogleTagManager config file</i>.

<img class="alignnone size-full wp-image-27111" src="https://keyholesoftware.com/wp-content/uploads/Screen-Shot-2022-04-25-at-4.53.39-PM.jpg" alt="" width="1200" height="469" />

Inside the new Run Script add this code:

```
# Removes all GTM-#######.json files inside only the parent container folder. This does not delete the GTM-#######.json files inside the dev and prod subfolders. We need the subfolder GTM-#######.json files intact so we can grab them during the copy process farther down the file
rm -vf ${PROJECT_DIR}/Runner/container/GTM*

# Environment Constants
PRODUCTION_ENVIRONMENT=Release-production
PRODUCTION_GTM_FILE_NAME=GTM-XXXXXXX.json
PROD_ENVIRONMENT_FOLDER=prod
DEV_GTM_FILE_NAME=GTM-XXXXXXX.json
DEV_ENVIRONMENT_FOLDER=dev

# Variables are adjusted if CONFIGURATION is equal to the PRODUCTION_ENVIRONMENT
ENVIRONMENT=$DEV_ENVIRONMENT_FOLDER
GTM_FILE_NAME=$DEV_GTM_FILE_NAME
if [[ "$CONFIGURATION" == "$PRODUCTION_ENVIRONMENT" ]]; then
    GTM_FILE_NAME=$PRODUCTION_GTM_FILE_NAME
    ENVIRONMENT=$PROD_ENVIRONMENT_FOLDER
fi

#Once the ENVIRONMENT and GTM_FILE_NAME are set we build the file location for that environment file
GTM_INFO_FILE=${PROJECT_DIR}/Runner/container/${ENVIRONMENT}/${GTM_FILE_NAME}

# Make sure the GTM_FILE_NAME exists
echo "Looking for ${GTM_FILE_NAME} in ${GTM_INFO_FILE}"
if [ ! -f $GTM_INFO_FILE ]; then
    echo "No ${GTM_FILE_NAME} found. Please ensure it's in the proper directory."
    exit 1
else
    echo "${GTM_FILE_NAME} config file was found"
fi
# Get a reference to the destination location for the GTM-XXXXXXX.json
# This is the default location where Firebase init code expects to find GTM-XXXXXXX.json file
GTM_DESTINATION=${PROJECT_DIR}/Runner/container/${GTM_FILE_NAME}

echo "Will copy ${GTM_FILE_NAME} to final destination: ${GTM_DESTINATION}"

# Copies GTM_INFO_FILE into the GTM_DESTINATION
cp ${GTM_INFO_FILE} ${GTM_DESTINATION}
```

<p style="padding-left: 40px;"><b><i>Note:</i></b> Replace the GTM-XXXXXXXX.json with your appropriate dev and prod container file names.</p>
Your setup should now work for both dev and prod environments. We’re almost done with the iOS setup. The last step is to install the GTM dependencies.
<h3>Step 5: Add the iOS GTM Dependency</h3>
Navigate to the iOS folder and look for a Podfile. If the Podfile already exists, skip these next steps.
<ol>
 	<li>Open the terminal</li>
 	<li>Run <code>sudo gem install cocoapods</code></li>
 	<li>Change to the iOS project directory</li>
 	<li>Run <code>pod init</code></li>
</ol>
Once you’ve created or located your Podfile, you’ll add the dependency into the Podfile.

<img class="alignnone wp-image-27098" src="https://keyholesoftware.com/wp-content/uploads/Link-14.jpg" alt="" width="663" height="268" />

In the terminal, change directories to the <code>ios</code> folder and run <code>pod install</code>.

iOS setup is complete.

Great job! Android and iOS are now set up to use GTM.

<img class="alignnone wp-image-27099" src="https://keyholesoftware.com/wp-content/uploads/Link-15.jpeg" alt="" width="555" height="312" />
<h2>Wrapping Up</h2>
Transitioning to Flutter allows you to take advantage of writing code a single time, in the same languages. It also overcomes the traditional limitations of cross-platform work.

After walking through this post, you now have GTM in your new Flutter app! It's a big accomplishment and one that'll benefit you for the foreseeable future.

Thank you for reading! If you found this helpful, check out some of the other posts on the <a href="https://keyholesoftware.com/blog/">Keyhole Dev Blog</a>.