<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Michael Otieno Olang</title>
    <description>The latest articles on DEV Community by Michael Otieno Olang (@mikeyolang).</description>
    <link>https://dev.to/mikeyolang</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F837727%2F29d59d4b-ae20-4bac-8d62-d62a478bf456.jpeg</url>
      <title>DEV Community: Michael Otieno Olang</title>
      <link>https://dev.to/mikeyolang</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mikeyolang"/>
    <language>en</language>
    <item>
      <title>Meta App Events in Flutter for Android: Setup, Tracking, and Ad Attribution</title>
      <dc:creator>Michael Otieno Olang</dc:creator>
      <pubDate>Fri, 13 Nov 2026 06:23:46 +0000</pubDate>
      <link>https://dev.to/mikeyolang/meta-app-events-in-flutter-for-android-setup-tracking-and-ad-attribution-2i4g</link>
      <guid>https://dev.to/mikeyolang/meta-app-events-in-flutter-for-android-setup-tracking-and-ad-attribution-2i4g</guid>
      <description>&lt;p&gt;A practical walkthrough of Meta App IDs, Android SDK setup, booking funnel events, and testing app campaign conversions.&lt;/p&gt;

&lt;p&gt;I recently added Meta App Events to a Flutter booking app so we could measure app installs, see where people leave the booking flow, and eventually optimize Meta campaigns for paid tickets. The SDK was sending events, but one question remained: &lt;strong&gt;if my ad account has a different ID from my app, how does Meta connect a ticket sale to an ad?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the setup and testing guide I wish I had while working through it. It covers &lt;strong&gt;Android app events&lt;/strong&gt;. An iOS build needs its own SDK and measurement setup; adding Android code does not enable iOS tracking.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, identify the three IDs
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ID&lt;/th&gt;
&lt;th&gt;What it identifies&lt;/th&gt;
&lt;th&gt;Where it belongs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Meta App ID&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The app registered in Meta for Developers&lt;/td&gt;
&lt;td&gt;Android SDK configuration and the app selected in an app campaign&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ad Account ID&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The account that owns and pays for campaigns&lt;/td&gt;
&lt;td&gt;Meta Ads Manager and business asset access; it does &lt;strong&gt;not&lt;/strong&gt; go in the mobile SDK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pixel ID&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A website tracking source&lt;/td&gt;
&lt;td&gt;Website Pixel or web Conversions API setup, if you measure website activity&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It is normal for all three numbers to differ. The important comparison is &lt;strong&gt;the Meta App ID inside the Android app versus the App ID selected for the campaign&lt;/strong&gt;. If those point to different Meta apps, the campaign will not use the events from the SDK integration I tested.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ad account runs an app campaign and selects a Meta app
                         ↓
Someone interacts with that ad and installs or opens the app
                         ↓
The app's SDK sends Activate App, Search, Purchase, etc. to its Meta App ID
                         ↓
Meta applies its attribution rules to eligible ad interactions and app events
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app sends events to Meta, not directly to the ad account. Meta already knows which account ran the campaign. An event arriving in Events Manager proves that event collection works; it does not, by itself, prove a campaign received credit for the event.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I needed before writing code
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A Meta for Developers app for the real mobile app, with its &lt;strong&gt;App ID&lt;/strong&gt; and &lt;strong&gt;Client Token&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The Android package name and launch activity from the production app, plus its Play Store listing.&lt;/li&gt;
&lt;li&gt;The Android signing key hashes for builds I would test and distribute.&lt;/li&gt;
&lt;li&gt;A Meta business/ad account that can access the registered app.&lt;/li&gt;
&lt;li&gt;A test Android device and a build containing the SDK.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I kept the App ID and Client Token from &lt;strong&gt;the same Meta app&lt;/strong&gt;. The website Pixel ID was not used in Android configuration.&lt;/p&gt;

&lt;p&gt;Facebook Login is not required for App Events. The booking app can report opens and purchases without asking customers to sign in with Facebook.&lt;/p&gt;

&lt;p&gt;For my TRAVLER Android build, the production package is &lt;code&gt;com.ma3app&lt;/code&gt;, the launch activity is &lt;code&gt;com.ma3app.MainActivity&lt;/code&gt;, and the SDK App ID is &lt;code&gt;1320645970220890&lt;/code&gt;. I compare that last number with the app selected in Ads Manager. The Client Token comes from the root &lt;code&gt;.env&lt;/code&gt; at build time; its value does not belong in this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Register the Android app with Meta
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;Meta for Developers → My Apps&lt;/strong&gt;, open the intended app. Under its app settings, add the Android platform and enter the app's package name, the fully qualified activity that opens it, and the relevant store details. In this Flutter project, the package and activity are represented by &lt;code&gt;applicationId&lt;/code&gt; in &lt;code&gt;android/app/build.gradle.kts&lt;/code&gt; and &lt;code&gt;MainActivity&lt;/code&gt; in the Android source. Verify them from the release build rather than guessing from a sample manifest.&lt;/p&gt;

&lt;p&gt;Copy the &lt;strong&gt;App ID&lt;/strong&gt; from the developer app settings. Find the &lt;strong&gt;Client Token&lt;/strong&gt; in that &lt;em&gt;same app's&lt;/em&gt; advanced/security settings. Meta's menu names can move, so the important check is the app shown at the top of each settings page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Signing key hashes
&lt;/h3&gt;

&lt;p&gt;Meta's Android platform configuration asks for Android key hashes. Add the development hash for local debug builds and the hash of the certificate that signs the app users actually download. If Google Play App Signing signs the distributed APKs, use its &lt;strong&gt;App Signing certificate&lt;/strong&gt;, not just your upload key.&lt;/p&gt;

&lt;p&gt;For the standard Android debug keystore, a key hash can be calculated with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;keytool -exportcert -alias androiddebugkey \
  -keystore &lt;span class="s2"&gt;"$HOME/.android/debug.keystore"&lt;/span&gt; \
  -storepass android -keypass android \
  &lt;span class="o"&gt;|&lt;/span&gt; openssl sha1 -binary &lt;span class="o"&gt;|&lt;/span&gt; openssl base64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For an exported DER certificate from Play Console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl x509 -inform DER -in play_app_signing.der -outform DER \
  &lt;span class="o"&gt;|&lt;/span&gt; openssl sha1 -binary &lt;span class="o"&gt;|&lt;/span&gt; openssl base64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is a &lt;strong&gt;Base64 SHA-1 key hash&lt;/strong&gt;. It is different from the &lt;strong&gt;SHA-256 fingerprint&lt;/strong&gt; used in Android App Links' &lt;code&gt;assetlinks.json&lt;/code&gt;. Those two values solve different problems and cannot be substituted for each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Add the Android SDK to Flutter's native project
&lt;/h2&gt;

&lt;p&gt;The Flutter UI does not initialize Meta's Android SDK by itself. I added Meta's core Android dependency to &lt;code&gt;android/app/build.gradle.kts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"com.facebook.android:facebook-core:19.0.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;19.0.0&lt;/code&gt; is the version this project was built and tested with; check Meta's current release before using the snippet in a new project. Gradle must be able to resolve it from Maven Central.&lt;/p&gt;

&lt;p&gt;The project's root &lt;code&gt;.env&lt;/code&gt; contains the client token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;META_CLIENT_TOKEN=YOUR_CLIENT_TOKEN_FROM_THE_SAME_META_APP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Flutter's Dart &lt;code&gt;.env&lt;/code&gt; loader does not automatically place that value into Android resources. The native Gradle build reads the file and generates a string resource. A shortened version of the approach is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="n"&gt;metaClientToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rootProject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"../.env"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readLines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trimStart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"META_CLIENT_TOKEN="&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substringAfter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"="&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'"'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'\''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;android&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;defaultConfig&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;resValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"facebook_client_token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metaClientToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In production code, fail the build if the value is missing and support an environment variable in CI. Keep &lt;code&gt;.env&lt;/code&gt; out of Git. This keeps the token out of source control, &lt;strong&gt;but it is still packaged into the Android app&lt;/strong&gt;; a local &lt;code&gt;.env&lt;/code&gt; is not a secret vault for values shipped to a device.&lt;/p&gt;

&lt;p&gt;I put the App ID in &lt;code&gt;android/app/src/main/res/values/strings.xml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;resources&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"facebook_app_id"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;YOUR_META_APP_ID&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/resources&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I added the metadata inside &lt;code&gt;&amp;lt;application&amp;gt;&lt;/code&gt; in &lt;code&gt;android/app/src/main/AndroidManifest.xml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;uses-permission&lt;/span&gt; &lt;span class="na"&gt;android:name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"android.permission.INTERNET"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;application&lt;/span&gt; &lt;span class="na"&gt;...&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta-data&lt;/span&gt;
        &lt;span class="na"&gt;android:name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"com.facebook.sdk.ApplicationId"&lt;/span&gt;
        &lt;span class="na"&gt;android:value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"@string/facebook_app_id"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta-data&lt;/span&gt;
        &lt;span class="na"&gt;android:name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"com.facebook.sdk.ClientToken"&lt;/span&gt;
        &lt;span class="na"&gt;android:value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"@string/facebook_client_token"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta-data&lt;/span&gt;
        &lt;span class="na"&gt;android:name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"com.facebook.sdk.AutoLogAppEventsEnabled"&lt;/span&gt;
        &lt;span class="na"&gt;android:value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta-data&lt;/span&gt;
        &lt;span class="na"&gt;android:name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"com.facebook.sdk.AutoLogMetaDataEnabled"&lt;/span&gt;
        &lt;span class="na"&gt;android:value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/application&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With valid manifest metadata, Meta's Android library initializes automatically. With automatic App Events enabled, it also logs app activation; I did &lt;strong&gt;not&lt;/strong&gt; add a second manual &lt;code&gt;activateApp()&lt;/code&gt; call. The SDK's own source documents this behavior. Also check the automatic App Events setting in Meta Events Manager, because Meta says both the client setting and the Events Manager setting affect automatic logging.&lt;/p&gt;

&lt;p&gt;This project also sets &lt;code&gt;FacebookSdk.setGraphApiVersion("v24.0")&lt;/code&gt; before creating its logger because the tested SDK version otherwise used an older Graph API version. That is a project-specific compatibility setting. Recheck it when upgrading the SDK or when Meta changes supported API versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Decide exactly what each event means
&lt;/h2&gt;

&lt;p&gt;Tracking is only useful if an event fires at a meaningful point. I mapped the booking flow before writing the Flutter calls:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;When it fires&lt;/th&gt;
&lt;th&gt;What it tells me&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fb_mobile_activate_app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Automatic&lt;/td&gt;
&lt;td&gt;SDK observes an app session&lt;/td&gt;
&lt;td&gt;The app was opened; this is not a ticket sale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;travler_home_view&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Custom&lt;/td&gt;
&lt;td&gt;Booking home becomes visible&lt;/td&gt;
&lt;td&gt;Entry into the booking flow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;travler_search_screen_view&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Custom&lt;/td&gt;
&lt;td&gt;Search/results screen opens&lt;/td&gt;
&lt;td&gt;User reached the search stage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fb_mobile_search&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Standard Search&lt;/td&gt;
&lt;td&gt;User submits a route search&lt;/td&gt;
&lt;td&gt;Intent to find a trip&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;travler_search_results_view&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Custom&lt;/td&gt;
&lt;td&gt;Results load, with &lt;code&gt;result_count&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Search succeeded, including zero results&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;travler_search_results_error&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Custom&lt;/td&gt;
&lt;td&gt;Results request fails&lt;/td&gt;
&lt;td&gt;A technical drop-off point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fb_mobile_initiated_checkout&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Standard Initiated Checkout&lt;/td&gt;
&lt;td&gt;Payments page opens with a valid amount&lt;/td&gt;
&lt;td&gt;User reached checkout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fb_mobile_purchase&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Standard Purchase&lt;/td&gt;
&lt;td&gt;Payment is confirmed successful&lt;/td&gt;
&lt;td&gt;A paid ticket conversion&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The standard events use Meta's event constants or its &lt;code&gt;logPurchase&lt;/code&gt; API. The custom names are specific to this app. A &lt;em&gt;Purchase&lt;/em&gt; must not fire when someone merely taps Pay, opens a pending booking, or sees a payment request. It fires after the payment-success path confirms the booking was paid.&lt;/p&gt;

&lt;p&gt;For accurate interpretation, do not treat raw event totals as a perfect person-by-person funnel. One user can search several times, return to checkout, or reopen the app. Use the counts to locate likely drop-offs, then compare unique users or sessions in a suitable analytics report before calculating conversion rates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Bridge Flutter actions to native App Events
&lt;/h2&gt;

&lt;p&gt;I used a Flutter &lt;code&gt;MethodChannel&lt;/code&gt; because this project was adding Android tracking first. The Dart side invokes a named action; &lt;code&gt;MainActivity&lt;/code&gt; maps that action to Meta's Android logger. Both sides must use the &lt;strong&gt;same channel name and method names&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A reduced Dart example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s1"&gt;'package:flutter/foundation.dart'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s1"&gt;'package:flutter/services.dart'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MetaEvents&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;_channel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MethodChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'com.ma3app/meta_app_events'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;_android&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;kIsWeb&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;defaultTargetPlatform&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;TargetPlatform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;android&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;searchSubmitted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_android&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invokeMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'logSearchSubmitted'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;purchase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;required&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;bookingId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;required&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;required&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;_android&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invokeMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'logPurchase'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s1"&gt;'bookingId'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bookingId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'amount'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'currency'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The native handler creates an &lt;code&gt;AppEventsLogger&lt;/code&gt; and maps these calls to standard or custom events. The relevant Android calls look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AppEventsLogger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;applicationContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Search submitted:&lt;/span&gt;
&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AppEventsConstants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="no"&gt;EVENT_NAME_SEARCHED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Checkout started with a valid positive amount:&lt;/span&gt;
&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;AppEventsConstants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="no"&gt;EVENT_NAME_INITIATED_CHECKOUT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;Bundle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apply&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;putString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AppEventsConstants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="no"&gt;EVENT_PARAM_CURRENCY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;currencyCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Payment confirmed with a valid positive amount:&lt;/span&gt;
&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logPurchase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;valueOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;Currency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currencyCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual channel handler validates numeric amounts and ISO currency codes, returns Flutter errors for invalid arguments, and logs the extra screen/result events. It also keeps purchase deduplication keyed by booking reference on the device, so two success callbacks do not log two purchases for one booking. Event delivery is asynchronous: a successful method call means the native logger accepted the event, while the SDK may flush it to Meta later.&lt;/p&gt;

&lt;p&gt;Only pass parameters needed for measurement. Do not send names, phone numbers, payment credentials, access tokens, or raw logs as event parameters. If a booking reference can identify a customer, keep it locally for deduplication unless there is a reviewed reason to transmit it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Connect the registered app to campaigns
&lt;/h2&gt;

&lt;p&gt;In Meta Business settings, find the registered mobile app and make it available to the ad account that will run campaigns. In &lt;strong&gt;Ads Manager&lt;/strong&gt;, create an app campaign, select the Android app, and confirm that the selected &lt;strong&gt;App ID matches the SDK App ID&lt;/strong&gt;. Select an available performance goal/event appropriate to the campaign; event optimization options can depend on the app's eligibility and event volume.&lt;/p&gt;

&lt;p&gt;For install campaigns, the app and its store listing must be correct. For ticket-sale campaigns, the app must actually send paid &lt;strong&gt;Purchase&lt;/strong&gt; events. A Pixel ID is relevant if a separate campaign sends people to the &lt;em&gt;website&lt;/em&gt; and measures purchases there; it does not replace the native app's App ID.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Test in three layers
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A. Confirm local SDK initialization
&lt;/h3&gt;

&lt;p&gt;Run a build that contains the integration. In a debug build, I log four booleans without printing the token itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;initialized=true autoLog=true appIdMatches=true clientTokenPresent=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;appIdMatches&lt;/code&gt; compares the SDK's loaded ID to the Android resource. It does &lt;strong&gt;not&lt;/strong&gt; prove that I selected the correct app in Ads Manager.&lt;/p&gt;

&lt;h3&gt;
  
  
  B. Confirm an event is created and sent
&lt;/h3&gt;

&lt;p&gt;Open the app and use Android logcat. One focused command is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;adb logcat -s \
  TravlerMetaSDK:I \
  FacebookSDK.AppEvents:D \
  FacebookSDK.com.facebook.appevents.AppEventQueue:D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For my Android test build, the logs showed &lt;code&gt;fb_mobile_activate_app&lt;/code&gt;, then &lt;code&gt;Result: Success&lt;/code&gt; after an App Events flush. They also showed &lt;code&gt;travler_home_view&lt;/code&gt; and another successful flush. That is strong evidence the SDK initialized and reported successful delivery. It does &lt;strong&gt;not&lt;/strong&gt; yet prove that a Purchase event was sent or that any campaign received credit.&lt;/p&gt;

&lt;p&gt;Logcat can include device IDs and other telemetry. Extract only the event names and send results when sharing a diagnostic log.&lt;/p&gt;

&lt;h3&gt;
  
  
  C. Confirm the right event appears in Meta
&lt;/h3&gt;

&lt;p&gt;In &lt;strong&gt;Events Manager&lt;/strong&gt;, select the &lt;strong&gt;app data source for the same App ID&lt;/strong&gt;, not the website Pixel. Check Test events or the app's event overview for Activate App, Search, Initiated Checkout, and Purchase as you trigger each stage. For Purchase, complete a legitimate test payment on a build with the integration and verify the amount and currency.&lt;/p&gt;

&lt;p&gt;Finally, test a campaign that selects that same app. Organic or debug events may appear in Events Manager without showing as &lt;strong&gt;ad-attributed conversions&lt;/strong&gt; in Ads Manager. Attribution requires an eligible ad interaction and follows Meta's reporting rules. A local &lt;code&gt;Result: Success&lt;/code&gt; is a delivery check, not a campaign ROI result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ad Account ID and App ID differ:&lt;/strong&gt; Expected. Do not copy the Ad Account ID into Android resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pixel ID and App ID differ:&lt;/strong&gt; Expected. Check the app data source for native app events.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Campaign selects a different App ID:&lt;/strong&gt; Give the ad account access to the correct app and select it, or reconfigure the SDK with the intended app's App ID and matching Client Token and ship a new build. Events already sent to the old app do not move automatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Activate App locally:&lt;/strong&gt; Check that the installed build has the SDK, the manifest has the correct values, and automatic App Events are enabled. Do not add a duplicate manual activation call without first checking the SDK configuration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Activate App appears, but Purchase does not:&lt;/strong&gt; Check the actual payment-success callbacks, amount validation, and booking deduplication. Opening the payments page only tests Initiated Checkout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Events Manager has Purchase, but Ads Manager has no conversions:&lt;/strong&gt; Check the campaign's selected app, business access, release build, ad interaction, and attribution settings. A test purchase with no Meta ad interaction is not an ad-attributed purchase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debug works, production does not:&lt;/strong&gt; Check the release signing certificate/key hash, Play distribution, package name, app version, and build-time Client Token. Test the distributed release on a physical device.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rule I keep returning to is simple: &lt;strong&gt;the Android SDK and the app selected in the Meta campaign must refer to the same Meta App ID.&lt;/strong&gt; The Ad Account ID identifies who runs the ads; the Pixel ID is for website measurement.&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.facebookblueprint.com/student/path/253008-use-app-events-to-target-optimize-measure" rel="noopener noreferrer"&gt;Meta Blueprint: Use app events to reach, optimize and measure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/meta/facebook-android-sdk/blob/main/facebook-core/src/main/java/com/facebook/appevents/AppEventsLogger.kt" rel="noopener noreferrer"&gt;Meta Android SDK: AppEventsLogger and automatic activation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/meta/facebook-android-sdk/blob/main/facebook-core/src/main/java/com/facebook/FacebookSdk.kt" rel="noopener noreferrer"&gt;Meta Android SDK: FacebookSdk settings and initialization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/meta/facebook-android-sdk/blob/main/facebook-core/src/main/AndroidManifest.xml" rel="noopener noreferrer"&gt;Meta Android SDK: library manifest and initialization provider&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>android</category>
      <category>flutter</category>
      <category>meta</category>
      <category>analytics</category>
    </item>
    <item>
      <title>Apple Watch &amp; iMessage Integration in Flutter</title>
      <dc:creator>Michael Otieno Olang</dc:creator>
      <pubDate>Thu, 09 Jul 2026 09:01:50 +0000</pubDate>
      <link>https://dev.to/mikeyolang/apple-watch-imessage-integration-in-flutter-38f2</link>
      <guid>https://dev.to/mikeyolang/apple-watch-imessage-integration-in-flutter-38f2</guid>
      <description>&lt;h3&gt;
  
  
  A Complete Developer Guide
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;App Bundle ID:&lt;/strong&gt; &lt;code&gt;com.example.demoapp&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;App Group:&lt;/strong&gt; &lt;code&gt;group.com.example.demoapp&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Targets:&lt;/strong&gt; &lt;code&gt;Runner&lt;/code&gt; (iPhone) · &lt;code&gt;DemoWatch Watch App&lt;/code&gt; · &lt;code&gt;DemoMessages MessagesExtension&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Architecture Overview&lt;/li&gt;
&lt;li&gt;Dependencies Added&lt;/li&gt;
&lt;li&gt;Xcode Setup (One-Time)&lt;/li&gt;
&lt;li&gt;
Apple Watch Integration

&lt;ul&gt;
&lt;li&gt;4.1 Flutter Side — WatchService
&lt;/li&gt;
&lt;li&gt;4.2 Syncing Data to the Watch
&lt;/li&gt;
&lt;li&gt;4.3 Listening for Watch Actions in Flutter
&lt;/li&gt;
&lt;li&gt;4.4 Native Watch UI (SwiftUI)
&lt;/li&gt;
&lt;li&gt;4.5 Watch Face Complications
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
iMessage Integration

&lt;ul&gt;
&lt;li&gt;5.1 Flutter Side — IMessageSyncService
&lt;/li&gt;
&lt;li&gt;5.2 Syncing Trip Data for iMessage
&lt;/li&gt;
&lt;li&gt;5.3 Native iMessage UI (UIKit/Swift)
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Data Flow Diagrams&lt;/li&gt;
&lt;li&gt;Publishing to the App Store&lt;/li&gt;
&lt;li&gt;Build &amp;amp; Archive Checklist&lt;/li&gt;
&lt;li&gt;Troubleshooting &amp;amp; Challenges Experienced&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. Architecture Overview
&lt;/h2&gt;

&lt;p&gt;Flutter &lt;strong&gt;cannot&lt;/strong&gt; run its UI engine on Apple Watch or inside iMessage extensions. Instead, we use two communication bridges:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Extension&lt;/th&gt;
&lt;th&gt;Bridge Technology&lt;/th&gt;
&lt;th&gt;Direction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Apple Watch&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;WatchConnectivity&lt;/code&gt; framework via &lt;code&gt;watch_connectivity&lt;/code&gt; Flutter package&lt;/td&gt;
&lt;td&gt;iPhone ↔ Watch (two-way, real-time)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;iMessage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;App Groups&lt;/code&gt; shared &lt;code&gt;UserDefaults&lt;/code&gt; container via &lt;code&gt;shared_preference_app_group&lt;/code&gt; package&lt;/td&gt;
&lt;td&gt;Flutter → iMessage (one-way, cached)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────┐         WatchConnectivity          ┌─────────────────────┐
│   Flutter App        │  ◄──────────────────────────────►  │  DemoWatch App   │
│   (iPhone)           │                                     │  (Apple Watch)      │
└─────────────────────┘                                     └─────────────────────┘
          │
          │  App Groups (UserDefaults)
          │  group.com.example.demoapp
          ▼
┌─────────────────────┐
│  iMessage Extension  │
│  DemoMessages      │
└─────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Dependencies Added
&lt;/h2&gt;

&lt;p&gt;The following packages were added to &lt;a href="//pubspec.yaml"&gt;&lt;code&gt;pubspec.yaml&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;watch_connectivity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^0.2.8&lt;/span&gt;        &lt;span class="c1"&gt;# Apple Watch two-way communication&lt;/span&gt;
  &lt;span class="na"&gt;shared_preference_app_group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^2.1.0&lt;/span&gt;  &lt;span class="c1"&gt;# Shared container for iMessage&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flutter pub get
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Xcode Setup (One-Time)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 App Groups Capability
&lt;/h3&gt;

&lt;p&gt;App Groups allow all three targets to share a single data container (&lt;code&gt;group.com.example.demoapp&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;You must add this capability to &lt;strong&gt;each target&lt;/strong&gt; in Xcode:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;code&gt;ios/Runner.xcworkspace&lt;/code&gt; in Xcode.&lt;/li&gt;
&lt;li&gt;In the Project Navigator (left panel), select &lt;strong&gt;Runner&lt;/strong&gt; target.&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Signing &amp;amp; Capabilities&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;+ Capability&lt;/strong&gt; → &lt;strong&gt;App Groups&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Add: &lt;code&gt;group.com.example.demoapp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Repeat steps 2–5 for &lt;strong&gt;DemoWatch Watch App&lt;/strong&gt; target.&lt;/li&gt;
&lt;li&gt;Repeat steps 2–5 for &lt;strong&gt;DemoMessages MessagesExtension&lt;/strong&gt; target.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ Confirmation: Check the entitlements file at&lt;br&gt;
&lt;code&gt;ios/DemoWatch Watch App/DemoWatch Watch App.entitlements&lt;/code&gt; — it should contain:&lt;/p&gt;


&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;com.apple.security.application-groups&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;group.com.example.demoapp&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3.2 Complication Data Source (Apple Watch only)
&lt;/h3&gt;

&lt;p&gt;To enable watch face complications:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select the &lt;strong&gt;DemoWatch Watch App&lt;/strong&gt; target in Xcode.&lt;/li&gt;
&lt;li&gt;Go to the &lt;strong&gt;Info&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Add a new row with key: &lt;code&gt;CLKComplicationDataSource&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Set value to: &lt;code&gt;ComplicationController&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3.3 Watch Target Platform Settings
&lt;/h3&gt;

&lt;p&gt;The Watch target must resolve as a watchOS target during archive. If Xcode resolves it as &lt;code&gt;iphoneos&lt;/code&gt;, the Watch asset catalog will be compiled as an iOS asset catalog and the archive will fail with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Assets.xcassets: The stickers icon set, app icon set, or icon stack named "AppIcon" did not have any applicable content.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the &lt;code&gt;DemoWatch Watch App&lt;/code&gt; target, confirm these build settings in Debug, Release, and Profile:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Required value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SDKROOT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;watchos&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SUPPORTED_PLATFORMS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;watchos watchsimulator&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TARGETED_DEVICE_FAMILY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;4&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ASSETCATALOG_COMPILER_APPICON_NAME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AppIcon&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AccentColor&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The current project pins these in &lt;code&gt;ios/Runner.xcodeproj/project.pbxproj&lt;/code&gt;. This matters because the project-level Flutter/iOS configuration sets &lt;code&gt;SDKROOT = iphoneos&lt;/code&gt; and &lt;code&gt;SUPPORTED_PLATFORMS = iphoneos&lt;/code&gt;; the Watch target must explicitly override that inherited iOS platform.&lt;/p&gt;

&lt;p&gt;You can verify the resolved target settings with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xcodebuild &lt;span class="nt"&gt;-showBuildSettings&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-project&lt;/span&gt; ios/Runner.xcodeproj &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-scheme&lt;/span&gt; &lt;span class="s2"&gt;"DemoWatch Watch App"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-configuration&lt;/span&gt; Release &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-destination&lt;/span&gt; &lt;span class="s2"&gt;"generic/platform=watchOS"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-derivedDataPath&lt;/span&gt; /tmp/travler-derived-data &lt;span class="se"&gt;\&lt;/span&gt;
  | rg &lt;span class="s2"&gt;"PLATFORM_NAME|SDKROOT|SUPPORTED_PLATFORMS|TARGETED_DEVICE_FAMILY|BUILT_PRODUCTS_DIR"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output should include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BUILT_PRODUCTS_DIR = .../Release-watchos
PLATFORM_NAME = watchos
SDKROOT = .../WatchOS.platform/.../WatchOS*.sdk
SUPPORTED_PLATFORMS = watchos watchsimulator
TARGETED_DEVICE_FAMILY = 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.4 Watch AppIcon Asset Catalog
&lt;/h3&gt;

&lt;p&gt;The Watch app icon set lives at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ios/DemoWatch Watch App/Assets.xcassets/AppIcon.appiconset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Contents.json&lt;/code&gt; must declare watch-specific icon slots, not only the 1024 marketing icon. The catalog should include these families:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Sizes currently covered&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;notificationCenter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;24x24, 27.5x27.5, 33x33&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;companionSettings&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;29x29 at 2x and 3x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;appLauncher&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;40x40, 44x44, 46x46, 50x50, 51x51, 54x54&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;quickLook&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;86x86, 98x98, 108x108, 117x117, 129x129&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;watch-marketing&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1024x1024&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If new icon artwork is generated, preserve the filenames declared in &lt;code&gt;Contents.json&lt;/code&gt; or update the JSON and PNGs together. A missing filename, wrong idiom, or iOS-only icon set will break archive.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Apple Watch Integration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1 Flutter Side — WatchService
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;a href="//lib/services/watch_service.dart"&gt;&lt;code&gt;lib/services/watch_service.dart&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This service is the bridge between your Flutter app and the Apple Watch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:async'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter/foundation.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:watch_connectivity/watch_connectivity.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WatchService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;WatchConnectivity&lt;/span&gt; &lt;span class="n"&gt;_watch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WatchConnectivity&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;StreamSubscription&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;_messageSubscription&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// All raw messages from the Watch&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;_messageController&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StreamController&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;messageStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_messageController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Only action commands from the Watch (e.g., quick_book, cancel_booking)&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;_actionController&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StreamController&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;actionStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_actionController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;WatchService&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_messageSubscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_watch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;messageStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;_messageController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;containsKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'action'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_actionController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;isSupported&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_watch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isSupported&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;isReachable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_watch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isReachable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_messageSubscription&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;_messageController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;_actionController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.2 Syncing Data to the Watch
&lt;/h3&gt;

&lt;p&gt;Call &lt;code&gt;syncTravelData()&lt;/code&gt; whenever the user's data changes — after login, booking, or wallet top-up. The data is sent using &lt;strong&gt;Application Context&lt;/strong&gt;, which means the Watch receives the latest state even if it was not connected at the time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;watchService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WatchService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;watchService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;syncTravelData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;walletBalance:&lt;/span&gt; &lt;span class="mf"&gt;125.50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;walletCurrency:&lt;/span&gt; &lt;span class="s"&gt;'Ksh'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;nextTripDestination:&lt;/span&gt; &lt;span class="s"&gt;'Nairobi → Mombasa'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;nextTripDate:&lt;/span&gt; &lt;span class="s"&gt;'Aug 12, 2026'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;nextTripTime:&lt;/span&gt; &lt;span class="s"&gt;'08:30 AM'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;nextTripSeat:&lt;/span&gt; &lt;span class="s"&gt;'14B'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;qrCodeData:&lt;/span&gt; &lt;span class="s"&gt;'TICKET-XYZ-12345'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// the raw ticket/QR string&lt;/span&gt;
  &lt;span class="nl"&gt;activeBookingsCount:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;unreadNotifications:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Best places to call this:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After a successful booking API response&lt;/li&gt;
&lt;li&gt;After the user tops up their wallet&lt;/li&gt;
&lt;li&gt;In your home screen &lt;code&gt;initState()&lt;/code&gt; when the app opens&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.3 Listening for Watch Actions in Flutter
&lt;/h3&gt;

&lt;p&gt;When the user presses &lt;strong&gt;Quick Book&lt;/strong&gt; or &lt;strong&gt;Cancel&lt;/strong&gt; on the Apple Watch, the Watch sends a message to Flutter via &lt;code&gt;actionStream&lt;/code&gt;. Listen to this in your BLoC, provider, or main widget tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;watchService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WatchService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;watchService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;actionStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;actionData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;actionData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'action'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;'quick_book'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;// Trigger your booking flow / open the booking screen&lt;/span&gt;
      &lt;span class="n"&gt;NavigationKeys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mainNav&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentState&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;pushNamed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/booking'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;'cancel_booking'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;destination&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;actionData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'destination'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="c1"&gt;// Call your cancel API&lt;/span&gt;
      &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BookingBloc&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancelBookingEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;destination:&lt;/span&gt; &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.4 Native Watch UI (SwiftUI)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;a href="//ios/DemoWatch%20Watch%20App/ContentView.swift"&gt;&lt;code&gt;ios/DemoWatch Watch App/ContentView.swift&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Watch app has a &lt;strong&gt;4-tab swipeable interface&lt;/strong&gt;. Each tab is a SwiftUI view powered by &lt;code&gt;WatchViewModel&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tab&lt;/th&gt;
&lt;th&gt;View&lt;/th&gt;
&lt;th&gt;Content&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TripDashboardView&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Next trip destination, date, time, seat + Quick Book &amp;amp; Cancel buttons&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TicketQRView&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Auto-generated QR code scannable at the boarding point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;WalletView&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wallet balance + active bookings count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SupportNotificationsView&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Call Support button + unread notification count&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;a href="//ios/DemoWatch%20Watch%20App/WatchViewModel.swift"&gt;&lt;code&gt;ios/DemoWatch Watch App/WatchViewModel.swift&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;WatchViewModel&lt;/code&gt; is the native Swift equivalent of &lt;code&gt;WatchService&lt;/code&gt;. It implements &lt;code&gt;WCSessionDelegate&lt;/code&gt; to receive data from Flutter and manages all published state for the SwiftUI views.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// How the Watch receives data from Flutter:&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;WCSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;didReceiveApplicationContext&lt;/span&gt; &lt;span class="nv"&gt;applicationContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;DispatchQueue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;applicationContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// How the Watch sends actions back to Flutter:&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;sendActionToFlutter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[:])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isReachable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;replyHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;errorHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.5 Watch Face Complications
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;a href="//ios/DemoWatch%20Watch%20App/ComplicationController.swift"&gt;&lt;code&gt;ios/DemoWatch Watch App/ComplicationController.swift&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Complications display DemoTravel info right on the Apple Watch clock face — without the user needing to open the app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Supported complication families:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Family&lt;/th&gt;
&lt;th&gt;Watch Face&lt;/th&gt;
&lt;th&gt;What's Shown&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;graphicCorner&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Infograph&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;🚌 Mombasa&lt;/code&gt; + departure time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;graphicCircular&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Infograph Modular&lt;/td&gt;
&lt;td&gt;Bus icon + time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;graphicBezel&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Infograph&lt;/td&gt;
&lt;td&gt;Circle with full destination curved around it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;modularLarge&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Modular&lt;/td&gt;
&lt;td&gt;3-row table: Trip / Time / Seat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;utilitarianLarge&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Utility&lt;/td&gt;
&lt;td&gt;&lt;code&gt;🚌 Nairobi → Mombasa · 08:30&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;utilitarianSmall&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Utility&lt;/td&gt;
&lt;td&gt;&lt;code&gt;🚌 08:30&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;modularSmall&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Modular&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Bus&lt;/code&gt; + time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;circularSmall&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Color / Chronograph&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Bus&lt;/code&gt; + time&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;How complications stay up to date:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;WatchViewModel.parseData()&lt;/code&gt; receives new data from Flutter, it automatically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Writes the data to &lt;code&gt;UserDefaults(suiteName: "group.com.example.demoapp")&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Calls &lt;code&gt;CLKComplicationServer.sharedInstance().reloadTimeline(for:)&lt;/code&gt; on all active complications&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This means the watch face updates instantly every time you call &lt;code&gt;syncTravelData()&lt;/code&gt; from Flutter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The &lt;code&gt;ComplicationController&lt;/code&gt; reads from UserDefaults:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;defaults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UserDefaults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;suiteName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"group.com.example.demoapp"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;destination&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;forKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"nextTripDestination"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"No Trip"&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;time&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;forKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"nextTripTime"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"--"&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;seat&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;forKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"nextTripSeat"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"--"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. iMessage Integration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 Flutter Side — IMessageSyncService
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;a href="//lib/services/imessage_sync_service.dart"&gt;&lt;code&gt;lib/services/imessage_sync_service.dart&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This service writes trip and wallet data into the shared App Group container so the iMessage extension can read them in Swift.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:convert'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:shared_preference_app_group/shared_preference_app_group.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;_kAppGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'group.com.example.demoapp'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DemoTrip&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;seat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;qrCodeData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;DemoTrip&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;seat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;qrCodeData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;toJson&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;'id'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'destination'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'origin'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'date'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'time'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'seat'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;seat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'qrCodeData'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;qrCodeData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IMessageSyncService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;_tripsKey&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'demo_imessage_trips'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;_walletKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'demo_imessage_wallet'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Call once at app startup (e.g. in main.dart)&lt;/span&gt;
  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;SharedPreferenceAppGroup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setAppGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_kAppGroup&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;syncAll&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DemoTrip&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;trips&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;walletBalance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;walletCurrency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;syncTrips&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trips&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;syncWalletBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;walletBalance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;walletCurrency&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;syncTrips&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DemoTrip&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;trips&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;encoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jsonEncode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trips&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toJson&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;SharedPreferenceAppGroup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_tripsKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoded&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;syncWalletBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;SharedPreferenceAppGroup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;_walletKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;jsonEncode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;'balance'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'currency'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5.2 Syncing Trip Data for iMessage
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1 — Initialise once at app startup&lt;/strong&gt; (e.g., in &lt;code&gt;main.dart&lt;/code&gt; before &lt;code&gt;runApp&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;WidgetsFlutterBinding&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ensureInitialized&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// ... existing initialisation ...&lt;/span&gt;

  &lt;span class="c1"&gt;// Initialise iMessage sync&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;iMessageSync&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;IMessageSyncService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;iMessageSync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="n"&gt;runApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2 — Sync whenever data changes:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;iMessageSync&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;IMessageSyncService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;iMessageSync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;syncAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;walletBalance:&lt;/span&gt; &lt;span class="mf"&gt;125.50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;walletCurrency:&lt;/span&gt; &lt;span class="s"&gt;'Ksh'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;trips:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;DemoTrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;id:&lt;/span&gt; &lt;span class="s"&gt;'booking-001'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;origin:&lt;/span&gt; &lt;span class="s"&gt;'Nairobi'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;destination:&lt;/span&gt; &lt;span class="s"&gt;'Mombasa'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;date:&lt;/span&gt; &lt;span class="s"&gt;'Aug 12, 2026'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;time:&lt;/span&gt; &lt;span class="s"&gt;'08:30 AM'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;seat:&lt;/span&gt; &lt;span class="s"&gt;'14B'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;qrCodeData:&lt;/span&gt; &lt;span class="s"&gt;'TICKET-XYZ-12345'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;DemoTrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;id:&lt;/span&gt; &lt;span class="s"&gt;'booking-002'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;origin:&lt;/span&gt; &lt;span class="s"&gt;'Mombasa'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;destination:&lt;/span&gt; &lt;span class="s"&gt;'Nairobi'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;date:&lt;/span&gt; &lt;span class="s"&gt;'Aug 15, 2026'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;time:&lt;/span&gt; &lt;span class="s"&gt;'02:00 PM'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;seat:&lt;/span&gt; &lt;span class="s"&gt;'7A'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;qrCodeData:&lt;/span&gt; &lt;span class="s"&gt;'TICKET-ABC-67890'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5.3 Native iMessage UI (UIKit/Swift)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;a href="//ios/DemoMessages%20MessagesExtension/MessagesViewController.swift"&gt;&lt;code&gt;ios/DemoMessages MessagesExtension/MessagesViewController.swift&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The iMessage extension is built with UIKit. It:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Reads trip and wallet data from &lt;code&gt;UserDefaults(suiteName: "group.com.example.demoapp")&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Displays a scrollable list of upcoming trips&lt;/li&gt;
&lt;li&gt;When a trip is tapped, inserts a rich &lt;code&gt;MSMessage&lt;/code&gt; bubble into the chat&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Reading from the shared container in Swift:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;defaults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UserDefaults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;suiteName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"group.com.example.demoapp"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Read trips&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;tripsString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;forKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"demo_imessage_trips"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tripsString&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;using&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utf8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;rawList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kt"&gt;JSONSerialization&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jsonObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as?&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;trips&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rawList&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compactMap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kt"&gt;DemoTrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Read wallet&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;walletString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;forKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"demo_imessage_wallet"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;walletString&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;using&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utf8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kt"&gt;JSONSerialization&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jsonObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;walletBalance&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"balance"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="k"&gt;as?&lt;/span&gt; &lt;span class="kt"&gt;Double&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;walletCurrency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as?&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"Ksh"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Sending a rich iMessage bubble:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;sendTrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;trip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;DemoTrip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;conversation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentConversation&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;MSMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selectedMessage&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="kt"&gt;MSSession&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;layout&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;MSMessageTemplateLayout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"🚌 &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;trip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt; → &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;trip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subcaption&lt;/span&gt;          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;trip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt; at &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;trip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trailingCaption&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Seat &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;trip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seat&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trailingSubcaption&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Trip Booking"&lt;/span&gt;
    &lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateQRImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;trip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;qrCodeData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// QR code&lt;/span&gt;

    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;layout&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"https://example.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;

    &lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;completionHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Important: do not name your stored conversation property &lt;code&gt;activeConversation&lt;/code&gt;. &lt;code&gt;MSMessagesAppViewController&lt;/code&gt; already exposes an inherited &lt;code&gt;activeConversation&lt;/code&gt; property. Use a separate name such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;currentConversation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;MSConversation&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;

&lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;willBecomeActive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="nv"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;MSConversation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;currentConversation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conversation&lt;/span&gt;
    &lt;span class="nf"&gt;loadData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What the recipient sees:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The recipient sees a custom rich bubble in their chat containing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;🚌 Nairobi → Mombasa&lt;/code&gt; (caption)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Aug 12, 2026 at 08:30 AM&lt;/code&gt; (subcaption)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Seat 14B&lt;/code&gt; (trailing)&lt;/li&gt;
&lt;li&gt;QR code image embedded in the bubble&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  6. Data Flow Diagrams
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Apple Watch — Data Flow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Flutter (Dart)                        Apple Watch (Swift)
─────────────────                     ────────────────────
WatchService.syncTravelData()
        │
        │  WatchConnectivity
        │  updateApplicationContext()
        │ ─────────────────────────►  WatchViewModel.didReceiveApplicationContext()
        │                                      │
        │                                      ├──► Updates @Published UI state
        │                                      ├──► Persists to UserDefaults (App Group)
        │                                      └──► Reloads watch face complications
        │
        │  WCSession.sendMessage()
        │ ◄─────────────────────────  WatchViewModel.sendActionToFlutter()
        │           (user taps Quick Book / Cancel on Watch)
        │
WatchService.actionStream emits
        │
        └──► Your BLoC / Provider handles the action
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  iMessage — Data Flow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Flutter (Dart)                        iMessage Extension (Swift)
─────────────────                     ──────────────────────────
IMessageSyncService.syncAll()
        │
        │  UserDefaults
        │  (suiteName: group.com.example.demoapp)
        │ ─────────────────────────►  MessagesViewController.loadData()
        │                                      │
        │                                      ├──► Renders trip list
        │                                      └──► User taps trip → sends MSMessage bubble
        │
        │  (No return path — iMessage is one-way)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  7. Publishing to the App Store
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ &lt;strong&gt;Good news:&lt;/strong&gt; The Apple Watch and iMessage extensions are &lt;strong&gt;bundled inside your main iOS app&lt;/strong&gt;. You do not submit them separately. When users download DemoTravel from the App Store, the Watch app automatically installs on their paired Apple Watch.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step-by-step publishing process:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1 — Archive the app:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flutter build ipa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or in Xcode: &lt;strong&gt;Product → Archive&lt;/strong&gt; (with "Any iOS Device" selected as the destination).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2 — Open Xcode Organizer:&lt;/strong&gt;&lt;br&gt;
After archiving, Xcode Organizer opens automatically. Select your archive and click &lt;strong&gt;Distribute App&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3 — Upload to App Store Connect:&lt;/strong&gt;&lt;br&gt;
Choose &lt;strong&gt;App Store Connect → Upload&lt;/strong&gt;. Follow the prompts. The Watch and iMessage extensions are included automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4 — Add Apple Watch Screenshots:&lt;/strong&gt;&lt;br&gt;
In App Store Connect, navigate to your app listing. You will now see a section for &lt;strong&gt;Apple Watch Screenshots&lt;/strong&gt;. Take screenshots from the Watch Simulator in Xcode and upload them here. Apple requires these before the app can be reviewed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Required Watch screenshot sizes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;40mm: 394 × 324 px&lt;/li&gt;
&lt;li&gt;41mm: 352 × 430 px (Series 7/8/9)&lt;/li&gt;
&lt;li&gt;44mm: 368 × 448 px&lt;/li&gt;
&lt;li&gt;45mm: 396 × 484 px&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 5 — Submit for Review:&lt;/strong&gt;&lt;br&gt;
Once screenshots are uploaded and everything looks good, click &lt;strong&gt;Submit for Review&lt;/strong&gt;. Apple reviews the Watch and iMessage extensions as part of the same review process.&lt;/p&gt;


&lt;h2&gt;
  
  
  8. Build &amp;amp; Archive Checklist
&lt;/h2&gt;

&lt;p&gt;Use this checklist before creating an App Store or TestFlight archive that includes Apple Watch and iMessage targets.&lt;/p&gt;
&lt;h3&gt;
  
  
  8.1 Before archiving
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Open &lt;code&gt;ios/Runner.xcworkspace&lt;/code&gt;, not &lt;code&gt;ios/Runner.xcodeproj&lt;/code&gt;, when using Xcode manually.&lt;/li&gt;
&lt;li&gt;Confirm the &lt;code&gt;Runner&lt;/code&gt; scheme includes dependencies on:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;DemoWatch Watch App&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DemoMessages MessagesExtension&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Confirm &lt;code&gt;DemoWatch Watch App&lt;/code&gt; resolves as watchOS:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;PLATFORM_NAME = watchos&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BUILT_PRODUCTS_DIR&lt;/code&gt; contains &lt;code&gt;Release-watchos&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SUPPORTED_PLATFORMS = watchos watchsimulator&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Confirm &lt;code&gt;DemoMessages MessagesExtension&lt;/code&gt; resolves as iPhoneOS:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;PLATFORM_NAME = iphoneos&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PRODUCT_TYPE = com.apple.product-type.app-extension.messages&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Confirm all targets share the App Group:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Runner&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DemoWatch Watch App&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DemoMessages MessagesExtension&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  8.2 Useful build-only verification
&lt;/h3&gt;

&lt;p&gt;This command checks the Watch and iMessage target build path without running tests or static analysis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xcodebuild build &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-project&lt;/span&gt; ios/Runner.xcodeproj &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-scheme&lt;/span&gt; &lt;span class="s2"&gt;"DemoWatch Watch App"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-configuration&lt;/span&gt; Release &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-destination&lt;/span&gt; &lt;span class="s2"&gt;"generic/platform=watchOS"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-derivedDataPath&lt;/span&gt; /tmp/travler-derived-data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;CODE_SIGNING_ALLOWED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;NO
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Successful Watch asset catalog output looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CompileAssetCatalogVariant ... DemoWatch Watch App/Assets.xcassets
... --app-icon AppIcon --target-device watch --platform watchos
/* com.apple.actool.compilation-results */
.../Assets.car
LinkAssetCatalog ... DemoWatch Watch App/Assets.xcassets
note: Emplaced .../DemoWatch Watch App.app/Assets.car
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  8.3 What not to run for this workflow
&lt;/h3&gt;

&lt;p&gt;For fast archive debugging, skip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flutter &lt;span class="nb"&gt;test
&lt;/span&gt;flutter analyze
dart analyze
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The archive blockers here are Xcode target configuration, asset catalogs, and native Swift extension compilation. Unit/widget tests and Dart static analysis do not diagnose those issues.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Troubleshooting &amp;amp; Challenges Experienced
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Watch data not updating?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Make sure &lt;code&gt;syncTravelData()&lt;/code&gt; is being called &lt;strong&gt;after&lt;/strong&gt; a booking or login.&lt;/li&gt;
&lt;li&gt;Confirm the Watch app is installed on a paired Watch.&lt;/li&gt;
&lt;li&gt;On a physical device, both the iPhone and Watch must be connected.&lt;/li&gt;
&lt;li&gt;Check that the App Group &lt;code&gt;group.com.example.demoapp&lt;/code&gt; is listed in &lt;strong&gt;both&lt;/strong&gt; the Runner and Watch targets in Xcode.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  iMessage extension shows "No upcoming trips"?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Make sure &lt;code&gt;IMessageSyncService.init()&lt;/code&gt; is called at app startup &lt;strong&gt;before&lt;/strong&gt; any sync calls.&lt;/li&gt;
&lt;li&gt;Confirm &lt;code&gt;syncAll()&lt;/code&gt; has been called at least once.&lt;/li&gt;
&lt;li&gt;Verify the App Group identifier &lt;code&gt;group.com.example.demoapp&lt;/code&gt; is set on &lt;strong&gt;both&lt;/strong&gt; Runner and DemoMessages targets.&lt;/li&gt;
&lt;li&gt;Double check the key names in Swift (&lt;code&gt;demo_imessage_trips&lt;/code&gt;, &lt;code&gt;demo_imessage_wallet&lt;/code&gt;) match the constants in Dart.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Watch face complications not appearing?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Confirm &lt;code&gt;CLKComplicationDataSource = ComplicationController&lt;/code&gt; is set in the Watch target's Info.plist in Xcode.&lt;/li&gt;
&lt;li&gt;Long-press your watch face → Edit → browse Complications for "DemoTravel".&lt;/li&gt;
&lt;li&gt;After calling &lt;code&gt;syncTravelData()&lt;/code&gt; in Flutter, the complication should update within seconds.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Build error: &lt;code&gt;No such module 'CoreImage.CIFilterBuiltins'&lt;/code&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;This module does not exist on watchOS. Use plain &lt;code&gt;import CoreImage&lt;/code&gt; instead.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;CIFilter(name: "CIQRCodeGenerator")&lt;/code&gt; instead of &lt;code&gt;CIFilter.qrCodeGenerator()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;Image(cgImage:, scale:, label:)&lt;/code&gt; in SwiftUI instead of &lt;code&gt;Image(uiImage:)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;pod install&lt;/code&gt; fails with xcodeproj compatibility error?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;This is a known CocoaPods issue with Xcode 26+. Run &lt;code&gt;flutter run&lt;/code&gt; from the command line instead of &lt;code&gt;pod install&lt;/code&gt; directly — Flutter's tooling handles the pod installation correctly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Archive error: &lt;code&gt;AppIcon did not have any applicable content&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Symptom:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/ios/DemoWatch Watch App/Assets.xcassets:
The stickers icon set, app icon set, or icon stack named "AppIcon" did not have any applicable content.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Root causes seen in this project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Watch target was resolving as &lt;code&gt;iphoneos&lt;/code&gt;, so Xcode was trying to compile watch-only icon slots as iOS icons.&lt;/li&gt;
&lt;li&gt;The Watch &lt;code&gt;AppIcon.appiconset/Contents.json&lt;/code&gt; only exposed &lt;code&gt;watch-marketing&lt;/code&gt; content or incomplete watch slots.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Fix:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In &lt;code&gt;ios/Runner.xcodeproj/project.pbxproj&lt;/code&gt;, ensure the &lt;code&gt;DemoWatch Watch App&lt;/code&gt; target has:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SDKROOT = watchos;
SUPPORTED_PLATFORMS = "watchos watchsimulator";
TARGETED_DEVICE_FAMILY = 4;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In &lt;code&gt;ios/DemoWatch Watch App/Assets.xcassets/AppIcon.appiconset/Contents.json&lt;/code&gt;, declare actual &lt;code&gt;idiom: watch&lt;/code&gt; entries for &lt;code&gt;notificationCenter&lt;/code&gt;, &lt;code&gt;companionSettings&lt;/code&gt;, &lt;code&gt;appLauncher&lt;/code&gt;, and &lt;code&gt;quickLook&lt;/code&gt;, plus &lt;code&gt;watch-marketing&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Confirm every filename referenced by &lt;code&gt;Contents.json&lt;/code&gt; exists and has the expected pixel dimensions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Verification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq empty &lt;span class="s2"&gt;"ios/DemoWatch Watch App/Assets.xcassets/AppIcon.appiconset/Contents.json"&lt;/span&gt;
sips &lt;span class="nt"&gt;-g&lt;/span&gt; pixelWidth &lt;span class="nt"&gt;-g&lt;/span&gt; pixelHeight &lt;span class="s2"&gt;"ios/DemoWatch Watch App/Assets.xcassets/AppIcon.appiconset/"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then confirm the target is not resolving as iPhoneOS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xcodebuild &lt;span class="nt"&gt;-showBuildSettings&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-project&lt;/span&gt; ios/Runner.xcodeproj &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-scheme&lt;/span&gt; &lt;span class="s2"&gt;"DemoWatch Watch App"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-configuration&lt;/span&gt; Release &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-destination&lt;/span&gt; &lt;span class="s2"&gt;"generic/platform=watchOS"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-derivedDataPath&lt;/span&gt; /tmp/travler-derived-data &lt;span class="se"&gt;\&lt;/span&gt;
  | rg &lt;span class="s2"&gt;"PLATFORM_NAME|SDKROOT|SUPPORTED_PLATFORMS|BUILT_PRODUCTS_DIR"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Build error: &lt;code&gt;No available simulator runtimes for platform watchsimulator&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This can happen when &lt;code&gt;actool&lt;/code&gt; runs in a restricted environment or Xcode cannot access simulator runtime services.&lt;/p&gt;

&lt;p&gt;Symptoms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;No available simulator runtimes for platform watchsimulator.
SimServiceContext supportedRuntimes=[]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fix:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run the build from normal Terminal or Xcode, not a restricted sandbox.&lt;/li&gt;
&lt;li&gt;Open Xcode once and let it finish installing components.&lt;/li&gt;
&lt;li&gt;Check &lt;strong&gt;Xcode → Settings → Platforms&lt;/strong&gt; and install the required watchOS simulator runtime if missing.&lt;/li&gt;
&lt;li&gt;Use a device/generic watchOS destination for archive:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xcodebuild build &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-project&lt;/span&gt; ios/Runner.xcodeproj &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-scheme&lt;/span&gt; &lt;span class="s2"&gt;"DemoWatch Watch App"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-configuration&lt;/span&gt; Release &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-destination&lt;/span&gt; &lt;span class="s2"&gt;"generic/platform=watchOS"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-derivedDataPath&lt;/span&gt; /tmp/travler-derived-data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;CODE_SIGNING_ALLOWED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;NO
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Build error: &lt;code&gt;activeConversation&lt;/code&gt; override in iMessage extension
&lt;/h3&gt;

&lt;p&gt;Symptom:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;overriding property must be as accessible as its enclosing type
cannot override with a stored property 'activeConversation'
ambiguous use of 'activeConversation'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cause:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;MSMessagesAppViewController&lt;/code&gt; already has an inherited &lt;code&gt;activeConversation&lt;/code&gt; property. A stored property with the same name in &lt;code&gt;MessagesViewController&lt;/code&gt; conflicts with it.&lt;/p&gt;

&lt;p&gt;Fix:&lt;/p&gt;

&lt;p&gt;Rename the custom property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;currentConversation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;MSConversation&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;

&lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;willBecomeActive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="nv"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;MSConversation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;currentConversation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conversation&lt;/span&gt;
    &lt;span class="nf"&gt;loadData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;sendTrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;trip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;DemoTrip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;conversation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentConversation&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// insert MSMessage...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Build reaches &lt;code&gt;Run Script&lt;/code&gt; and appears stuck
&lt;/h3&gt;

&lt;p&gt;After Watch assets and iMessage compile issues are fixed, the build can spend a long time in Flutter/Xcode script phases, especially &lt;code&gt;Run Script&lt;/code&gt;, &lt;code&gt;Thin Binary&lt;/code&gt;, or Flutter embed steps.&lt;/p&gt;

&lt;p&gt;What to check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confirm previous failures are gone before interrupting. In the log, Watch asset success looks like &lt;code&gt;Emplaced .../DemoWatch Watch App.app/Assets.car&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Run from a normal Terminal when possible so script phases can access the full user environment.&lt;/li&gt;
&lt;li&gt;If the script is silent for several minutes, rerun with Xcode's report navigator open to see the exact active phase.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Documentation generated for DemoTravel v3.5.4 — Apple Watch &amp;amp; iMessage Integration&lt;/em&gt;&lt;br&gt;
&lt;em&gt;This sample project is provided for educational purposes.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Bundle ID: &lt;code&gt;com.example.demoapp&lt;/code&gt; · App Group: &lt;code&gt;group.com.example.demoapp&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>ios</category>
      <category>mobile</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Flutter Apps and Android's 16KB Page Size Requirement: A Complete Developer Guide</title>
      <dc:creator>Michael Otieno Olang</dc:creator>
      <pubDate>Thu, 04 Sep 2025 06:44:24 +0000</pubDate>
      <link>https://dev.to/mikeyolang/flutter-apps-and-androids-16kb-page-size-requirement-a-complete-developer-guide-1p71</link>
      <guid>https://dev.to/mikeyolang/flutter-apps-and-androids-16kb-page-size-requirement-a-complete-developer-guide-1p71</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftxwvmggb07w2t8wzrvwp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftxwvmggb07w2t8wzrvwp.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;br&gt;
Google Play Store is rolling out a significant change that every Android developer needs to know about. Starting November 1, 2025, all new apps and app updates targeting Android 15+ must be compatible with 16KB memory pages. This requirement has caught many developers off guard, especially those working with Flutter applications.&lt;br&gt;
In this comprehensive guide, we'll dive deep into what this means, why it's happening, and most importantly, how to ensure your Flutter apps are ready.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understanding Memory Page Sizes&lt;/strong&gt;&lt;br&gt;
Memory pages are fixed-size blocks of virtual memory that the operating system uses to manage RAM efficiently. Think of them as standardized containers for data that help the OS organize and access memory. For years, Android devices have predominantly used 4KB page sizes. This worked well for devices with limited RAM, but as smartphones evolved with more memory and processing power, larger page sizes became beneficial.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why 16KB Pages?&lt;/strong&gt;&lt;br&gt;
The shift to 16KB pages isn't arbitrary. &lt;br&gt;
Here's what the research shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster App Launches: Up to 30% improvement in cold start times&lt;/li&gt;
&lt;li&gt;Better Memory Efficiency: Reduced memory fragmentation&lt;/li&gt;
&lt;li&gt;Improved System Performance: Faster boot times and overall responsiveness&lt;/li&gt;
&lt;li&gt;Enhanced Battery Life: More efficient memory management reduces CPU overhead&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step-by-Step Implementation Guide&lt;/strong&gt;&lt;br&gt;
1.Update Flutter SDK&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check current version&lt;/span&gt;
flutter &lt;span class="nt"&gt;--version&lt;/span&gt;

&lt;span class="c"&gt;# Update to latest stable&lt;/span&gt;
flutter upgrade

&lt;span class="c"&gt;# Verify you're on the latest stable channel&lt;/span&gt;
flutter channel stable
flutter upgrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Minimum recommended versions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flutter SDK: 3.16.0 or later&lt;/li&gt;
&lt;li&gt;Dart SDK: 3.2.0 or later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2.Update Android Gradle Plugin (AGP)&lt;br&gt;
In your &lt;code&gt;android/build.gradle&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gradle"&gt;&lt;code&gt;&lt;span class="k"&gt;buildscript&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;kotlin_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'1.9.10'&lt;/span&gt;
    &lt;span class="k"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Update to AGP 8.5.0 or later&lt;/span&gt;
        &lt;span class="n"&gt;classpath&lt;/span&gt; &lt;span class="s1"&gt;'com.android.tools.build:gradle:8.1.0'&lt;/span&gt;
        &lt;span class="n"&gt;classpath&lt;/span&gt; &lt;span class="s2"&gt;"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3.Update Android NDK&lt;br&gt;
In &lt;code&gt;android/app/build.gradle&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gradle"&gt;&lt;code&gt;&lt;span class="n"&gt;android&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;compileSdkVersion&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;
    &lt;span class="n"&gt;ndkVersion&lt;/span&gt; &lt;span class="s2"&gt;"28.x.xxxxxxx"&lt;/span&gt; 

    &lt;span class="n"&gt;compileOptions&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;sourceCompatibility&lt;/span&gt; &lt;span class="n"&gt;JavaVersion&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;VERSION_1_8&lt;/span&gt;
        &lt;span class="n"&gt;targetCompatibility&lt;/span&gt; &lt;span class="n"&gt;JavaVersion&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;VERSION_1_8&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4.Dependency Audit&lt;br&gt;
Check if you have the latest versions of every dependency in the &lt;code&gt;pubspec.yaml&lt;/code&gt; file&lt;/p&gt;

&lt;p&gt;Critical Plugins to Check&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Focus on these plugin categories:&lt;/li&gt;
&lt;li&gt;Camera/Media: &lt;code&gt;camera, image_picker, video_player&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Database: &lt;code&gt;sqflite, hive, objectbox&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Networking: &lt;code&gt;dio&lt;/code&gt;with native interceptors&lt;/li&gt;
&lt;li&gt;Firebase: All Firebase plugins&lt;/li&gt;
&lt;li&gt;Maps: &lt;code&gt;google_maps_flutter&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Payment: &lt;code&gt;stripe_android&lt;/code&gt;, payment gateway SDKs&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flutter</category>
      <category>android</category>
      <category>androiddev</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Navigating the Journey: Becoming a Mobile App Developer</title>
      <dc:creator>Michael Otieno Olang</dc:creator>
      <pubDate>Sun, 31 Mar 2024 15:42:25 +0000</pubDate>
      <link>https://dev.to/mikeyolang/navigating-the-journey-becoming-a-mobile-app-developer-4f2l</link>
      <guid>https://dev.to/mikeyolang/navigating-the-journey-becoming-a-mobile-app-developer-4f2l</guid>
      <description>&lt;p&gt;We'll embark on a journey together into the realm of mobile app development. In today's digital age, mobile applications have become an integral part of our daily lives, from communication and entertainment to productivity and commerce. If you've ever wondered what it takes to create these innovative tools that reside in our pockets, you're in the right place. In this guide, we'll explore the steps, tools, and resources you need to kickstart your career as a mobile app developer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Landscape:
&lt;/h2&gt;

&lt;p&gt;Before diving into the technical aspects, it's crucial to grasp the landscape of mobile app development. Mobile platforms such as iOS and Android dominate the market, each with its own set of development tools, languages, and guidelines. Additionally, cross-platform frameworks like React Native and Flutter offer the ability to build apps for multiple platforms using a single codebase. Understanding these options will help you chart your path forward.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning the Fundamentals:
&lt;/h2&gt;

&lt;p&gt;As with any craft, mastering the fundamentals is essential for success. Start by familiarizing yourself with programming languages commonly used in mobile app development, such as Java, Kotlin, Swift, or Dart. Online platforms like Codecademy, Udemy, and Coursera offer comprehensive courses tailored to beginners.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing Your Platform:
&lt;/h2&gt;

&lt;p&gt;Once you've gained a solid understanding of programming fundamentals, it's time to choose your platform. Are you drawn to the sleek design and user experience of iOS apps, or do you prefer the flexibility and customization options of Android? Your decision will influence the tools and technologies you'll need to learn next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exploring Development Tools:
&lt;/h2&gt;

&lt;p&gt;Equipped with a programming language and platform of choice, it's time to explore the development tools available to you. For iOS development, Xcode is the primary Integrated Development Environment (IDE) used by developers, while Android Studio is the go-to choice for Android development. Additionally, familiarize yourself with version control systems like Git, which are indispensable for collaborative development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mastering Mobile UI/UX Design:
&lt;/h2&gt;

&lt;p&gt;In the competitive world of mobile apps, user interface (UI) and user experience (UX) design play a crucial role in determining the success of your app. Learn the principles of mobile UI/UX design, including navigation patterns, typography, and usability testing. Tools like Sketch, Figma, and Adobe XD can aid in prototyping and designing your app's interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Embracing Continuous Learning:
&lt;/h2&gt;

&lt;p&gt;Mobile app development is a dynamic field that constantly evolves with new technologies and trends. Embrace a mindset of continuous learning by staying updated on the latest developments, attending conferences, and participating in online communities. Engage with fellow developers, seek mentorship, and don't be afraid to experiment with new ideas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Your Portfolio:
&lt;/h2&gt;

&lt;p&gt;As you gain proficiency in mobile app development, start building a portfolio of projects to showcase your skills to potential employers or clients. Whether it's a personal project, freelance work, or contributions to open-source projects, a strong portfolio demonstrates your capabilities and passion for the craft.&lt;/p&gt;

</description>
      <category>mobile</category>
      <category>softwaredevelopment</category>
      <category>career</category>
      <category>learning</category>
    </item>
    <item>
      <title>Excited to share insights on Android Development Testing!</title>
      <dc:creator>Michael Otieno Olang</dc:creator>
      <pubDate>Mon, 11 Mar 2024 08:55:59 +0000</pubDate>
      <link>https://dev.to/mikeyolang/excited-to-share-insights-on-android-development-testing-3ahn</link>
      <guid>https://dev.to/mikeyolang/excited-to-share-insights-on-android-development-testing-3ahn</guid>
      <description>&lt;p&gt;In the world of mobile app development, ensuring seamless user experiences is paramount. That's why Testing on Android development holds such crucial significance. 🛠️&lt;/p&gt;

&lt;p&gt;Here are some key points I'd like to highlight:&lt;/p&gt;

&lt;p&gt;1️⃣ Unit Testing: Building robust and reliable code starts here. Unit testing allows developers to test individual components in isolation, ensuring they function as expected.&lt;/p&gt;

&lt;p&gt;2️⃣ Integration Testing: Assembling the pieces of your app? Integration testing ensures that these components work together seamlessly, catching any potential conflicts or issues.&lt;/p&gt;

&lt;p&gt;3️⃣ UI Testing: Your app's interface is the user's gateway to functionality. UI testing verifies that each element behaves as intended, providing a smooth and intuitive experience.&lt;/p&gt;

&lt;p&gt;4️⃣ Automation: Embracing automation accelerates testing cycles and enhances efficiency. Tools like Espresso and Robolectric streamline testing processes, enabling developers to focus more on innovation.&lt;/p&gt;

&lt;p&gt;5️⃣ Device Fragmentation: Testing across various devices and OS versions is key to ensuring compatibility and performance consistency. Emulators and real-device testing help tackle this challenge effectively.&lt;/p&gt;

&lt;p&gt;Incorporating these testing strategies into your Android development workflow not only enhances the quality of your app but also boosts user satisfaction and retention. &lt;/p&gt;

</description>
      <category>testing</category>
      <category>android</category>
      <category>flutter</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>A Simple Dart Tutorial</title>
      <dc:creator>Michael Otieno Olang</dc:creator>
      <pubDate>Wed, 11 Oct 2023 18:57:32 +0000</pubDate>
      <link>https://dev.to/mikeyolang/a-simple-dart-tutorial-5ho2</link>
      <guid>https://dev.to/mikeyolang/a-simple-dart-tutorial-5ho2</guid>
      <description>&lt;h2&gt;
  
  
  Dart Tutorial
&lt;/h2&gt;

&lt;p&gt;Welcome to the Dart Tutorial! This tutorial is designed to help you get started with Dart programming. Whether you're a beginner or an experienced developer, you'll find valuable information here to enhance your skills in Dart.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Dart Tutorial&lt;/li&gt;
&lt;li&gt;Table of Contents&lt;/li&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Prerequisites&lt;/li&gt;
&lt;li&gt;Getting Started&lt;/li&gt;
&lt;li&gt;Topics Covered&lt;/li&gt;
&lt;li&gt;
Dart Basics

&lt;ul&gt;
&lt;li&gt;1. Hello, Dart!&lt;/li&gt;
&lt;li&gt;2. Variables and Data Types&lt;/li&gt;
&lt;li&gt;3. Control Flow&lt;/li&gt;
&lt;li&gt;4. Functions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Contributing&lt;/li&gt;

&lt;li&gt;Happy coding!&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Dart is a modern, open-source programming language developed by Google. It's known for its speed, efficiency, and ease of learning. Dart can be used for web, mobile, server, and desktop applications.&lt;/p&gt;

&lt;p&gt;This tutorial aims to provide a comprehensive introduction to Dart, covering the basics and gradually progressing to more advanced concepts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before starting this tutorial, make sure you have the following prerequisites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic understanding of programming concepts&lt;/li&gt;
&lt;li&gt;Code editor (e.g., Visual Studio Code, IntelliJ IDEA)&lt;/li&gt;
&lt;li&gt;Dart SDK installed on your system&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you haven't installed Dart SDK yet, you can download it from the &lt;a href="https://dart.dev/get-dart" rel="noopener noreferrer"&gt;official Dart website&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;To get started with Dart, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://dart.dev/get-dart" rel="noopener noreferrer"&gt;Install Dart SDK&lt;/a&gt; on your system.&lt;/li&gt;
&lt;li&gt;Set up your development environment by configuring a code editor (e.g., Visual Studio Code) for Dart.&lt;/li&gt;
&lt;li&gt;Clone this &lt;a href="https://github.com/mikeyolang/Dart-Tutorial.git" rel="noopener noreferrer"&gt;repository&lt;/a&gt;`&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Topics Covered
&lt;/h2&gt;

&lt;p&gt;This tutorial covers the following topics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dart basics&lt;/li&gt;
&lt;li&gt;Variables and data types&lt;/li&gt;
&lt;li&gt;Control flow (if-else, switch, loops)&lt;/li&gt;
&lt;li&gt;Functions and error handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feel free to explore the code and documentation for each topic in detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dart Basics
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Hello, Dart!
&lt;/h3&gt;

&lt;p&gt;The traditional "Hello, World!" program in Dart:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;dart&lt;br&gt;
void main() {&lt;br&gt;
  print('Hello, Dart!');&lt;br&gt;
}&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Variables and Data Types
&lt;/h3&gt;

&lt;p&gt;In Dart, you can declare variables using &lt;code&gt;var&lt;/code&gt;, &lt;code&gt;int&lt;/code&gt;, &lt;code&gt;double&lt;/code&gt;, &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;bool&lt;/code&gt;, and more. Example:&lt;br&gt;
&lt;code&gt;&lt;/code&gt;&lt;code&gt;dart&lt;br&gt;
void main() {&lt;br&gt;
  var name = 'John Doe';  // String&lt;br&gt;
  int age = 30;           // Integer&lt;br&gt;
  double height = 6.2;     // Double&lt;br&gt;
  bool isStudent = true;   // Boolean&lt;br&gt;
}&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;br&gt;
Dart is a statically typed language, meaning that variable types are known at compile time. However, you can use &lt;code&gt;var&lt;/code&gt; to have Dart infer the type.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Control Flow
&lt;/h3&gt;

&lt;p&gt;Dart supports common control flow structures like &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;else&lt;/code&gt;, &lt;code&gt;switch&lt;/code&gt;, and &lt;strong&gt;loops&lt;/strong&gt; (&lt;code&gt;for&lt;/code&gt;, &lt;code&gt;while&lt;/code&gt;, &lt;code&gt;do-while&lt;/code&gt;). Example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`dart&lt;br&gt;
void main() {&lt;br&gt;
  var age = 30;&lt;/p&gt;

&lt;p&gt;if (age &amp;lt; 18) {&lt;br&gt;
    print('You are a minor.');&lt;br&gt;
  } else if (age &amp;gt;= 18 &amp;amp;&amp;amp; age &amp;lt; 60) {&lt;br&gt;
    print('You are an adult.');&lt;br&gt;
  } else {&lt;br&gt;
    print('You are a senior citizen.');&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Functions
&lt;/h3&gt;

&lt;p&gt;Creating a simple function in Dart:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`dart&lt;br&gt;
    void main() {&lt;br&gt;
      print(greet('John Doe'));&lt;br&gt;
    }&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;String greet(String name) {
  return 'Hello, $name!';
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt;&lt;br&gt;
Dart also supports &lt;strong&gt;anonymous functions&lt;/strong&gt; (functions without a name) and &lt;strong&gt;arrow functions&lt;/strong&gt; (functions with a single expression). Example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`dart&lt;br&gt;
void main() {&lt;br&gt;
  var numbers = [1, 2, 3, 4, 5];&lt;/p&gt;

&lt;p&gt;// Anonymous function&lt;br&gt;
  numbers.forEach((number) {&lt;br&gt;
    print(number);&lt;br&gt;
  });&lt;/p&gt;

&lt;p&gt;// Arrow function&lt;br&gt;
  numbers.forEach((number) =&amp;gt; print(number));&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Contributing
&lt;/h2&gt;

&lt;p&gt;If you find any issues or have suggestions for improvement, please feel free to open an issue or create a pull request in my &lt;a href="https://github.com/mikeyolang/Dart-Tutorial.git" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;. We welcome contributions from the community to make this tutorial even better!&lt;/p&gt;

&lt;p&gt;To contribute, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fork the repository.&lt;/li&gt;
&lt;li&gt;Create a new branch for your contribution.&lt;/li&gt;
&lt;li&gt;Make your changes and commit them.&lt;/li&gt;
&lt;li&gt;Push the changes to your fork.&lt;/li&gt;
&lt;li&gt;Open a pull request with a detailed description of your contribution.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Happy coding!
&lt;/h2&gt;

</description>
      <category>tutorial</category>
      <category>dart</category>
      <category>beginners</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>My Flutter Journey</title>
      <dc:creator>Michael Otieno Olang</dc:creator>
      <pubDate>Thu, 02 Mar 2023 22:14:53 +0000</pubDate>
      <link>https://dev.to/mikeyolang/my-flutter-journey-372f</link>
      <guid>https://dev.to/mikeyolang/my-flutter-journey-372f</guid>
      <description>&lt;p&gt;Flutter is a framework based on dart and developed by google for creating cross-platform applications. It can be used to create android, iOS, Windows, Mac OS, and Linux applications under one code base. It also uses Firebase as its back-end but you can also use the various back-end tech stacks for your back- end such as node js and many others.&lt;/p&gt;

&lt;p&gt;I was introduced to flutter 4 months ago by a tech friend who by that time was using Kotlin to develop mobile applications, majorly android applications. We were to create our association's Mobile app within a week and he suggested that for ease and productivity we use the flutter framework for the app. We were both new to the framework but we really wanted to try out this new mobile development technique. We had downloaded a number of YouTube crash courses and some clone apps tutorials.&lt;/p&gt;

&lt;p&gt;I never knew what I was doing, I never knew what was happening but I could only follow the YouTube tutor. After some two days, our UI was done and we presented the app as our project&lt;/p&gt;

&lt;p&gt;I am still a beginner in the field but these are some of the advantages of Flutter I have noticed so far;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Flutter is easy to learn&lt;/li&gt;
&lt;li&gt;Flutter has a great UI design which is simple to implement&lt;/li&gt;
&lt;li&gt;Flutter uses widgets&lt;/li&gt;
&lt;li&gt;Running Apps is relatively easy&lt;/li&gt;
&lt;li&gt;Its beginner friendly&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I would really advise you guys to try out Flutter&lt;br&gt;
Thank you&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>mobile</category>
      <category>android</category>
      <category>dart</category>
    </item>
    <item>
      <title>Creating a simple Linked List In C programming</title>
      <dc:creator>Michael Otieno Olang</dc:creator>
      <pubDate>Thu, 17 Nov 2022 14:00:57 +0000</pubDate>
      <link>https://dev.to/mikeyolang/creating-a-simple-linked-list-in-c-programming-49bc</link>
      <guid>https://dev.to/mikeyolang/creating-a-simple-linked-list-in-c-programming-49bc</guid>
      <description>&lt;p&gt;&lt;strong&gt;Linked Lists&lt;/strong&gt; are list of elements in which the various elements are linked together. Data in a linked list are stored in a &lt;strong&gt;linear manner&lt;/strong&gt;. The Data are placed in &lt;strong&gt;nodes&lt;/strong&gt; and the nodes are connected in a specific desired fashion.&lt;br&gt;
&lt;strong&gt;Node&lt;/strong&gt; is a container or a box which contains data and other information in it. In a list a node is connected to a different node forming a chain of nodes.&lt;br&gt;
The first step in making a linked list is making a node which will store data into it.&lt;br&gt;
&lt;strong&gt;C program for a Linked List&lt;/strong&gt;&lt;br&gt;
We first create a structure named node which stores an integer named data and a pointer named next to another structure (node).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;struct node
{
    int data;
    struct node *next;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then make two nodes (structure named node) and allocating space to them using the &lt;code&gt;malloc&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;struct node *a, *b;
    a = malloc(sizeof(struct node));
    b = malloc(sizeof(struct node));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then set the values of data of the nodes a and b.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    a-&amp;gt;data = 10;
    b-&amp;gt;data = 20;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then store the address of b in the next variable of a. Thus, next of a is now storing the address of b or we can say the next of a is pointing to b.Since there is no node for the next of b, so we are making next of b null.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    a-&amp;gt;next = b;
    b-&amp;gt;next = NULL;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then Print to see the output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;printf("%d\n%d\n", a-&amp;gt;data, a-&amp;gt;next-&amp;gt;data);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below is the Combined Code&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmjrswjzdz4pcrvyhl8rc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmjrswjzdz4pcrvyhl8rc.png" alt=" " width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>welcome</category>
      <category>beginners</category>
      <category>spanish</category>
    </item>
    <item>
      <title>Shell Programming Using C Programming Language</title>
      <dc:creator>Michael Otieno Olang</dc:creator>
      <pubDate>Sat, 11 Jun 2022 18:38:03 +0000</pubDate>
      <link>https://dev.to/mikeyolang/shell-programming-using-c-programming-language-4acb</link>
      <guid>https://dev.to/mikeyolang/shell-programming-using-c-programming-language-4acb</guid>
      <description>&lt;p&gt;The following C_Program is used to append text from one file to the end of another file. Does any one have an Idea on how I can implement the code using the &lt;strong&gt;COMMANDLINE ARGUMENTS&lt;/strong&gt; and the various &lt;strong&gt;FLAGS&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fazppxmjeb7qeugx87s8p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fazppxmjeb7qeugx87s8p.png" alt=" " width="800" height="1150"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>shellprogramming</category>
      <category>c</category>
      <category>programming</category>
      <category>unix</category>
    </item>
    <item>
      <title>A Must have Vs Code Extensions</title>
      <dc:creator>Michael Otieno Olang</dc:creator>
      <pubDate>Sat, 28 May 2022 15:39:35 +0000</pubDate>
      <link>https://dev.to/mikeyolang/a-must-have-vs-code-extensions-1h33</link>
      <guid>https://dev.to/mikeyolang/a-must-have-vs-code-extensions-1h33</guid>
      <description>&lt;ol&gt;
&lt;li&gt;Gitlens&lt;/li&gt;
&lt;li&gt;Better Comments&lt;/li&gt;
&lt;li&gt;Prettier&lt;/li&gt;
&lt;li&gt;ESLint
If there are any,kindly add them comment sections. &lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>vscode</category>
    </item>
  </channel>
</rss>
