Skip to Content

Flutter

This page shows how to Create the Flutter platform client using platform channels

TL;DR

The sample repository demonstrates a Flutter bridge. Use the iOS example below to integrate Graffity AR Cloud 5.1.1.

Step 1 - Clone this git repo

Github - Graffity-Technologies/graffity-viewer

Step 2 - Put YOUR_ACCESS_TOKEN in TextField and press Submit Button

Create the Flutter platform client

Step 1 - Import the necessary packages

import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart';

Step 2 - Construct the channel to establish communication between Flutter and platform-specific code

Read More: Flutter Docs

class _MyHomePageState extends State<MyHomePage> { static const platform = MethodChannel('samples.flutter.dev/ar');

Step 3 - Invoke a method on the method channel

Here is how you get your Access Token

... static const platform = MethodChannel('samples.flutter.dev/ar'); String accessToken = "YOUR_ACCESS_TOKEN"; Future<void> _openARFunction() async { await platform.invokeMethod('openAR', {'accessToken': accessToken}); } ...

Step 4 - Use your function

... ElevatedButton( onPressed: _openARFunction, child: Text( 'Open AR',), ), ...

iOS with Flutter

Use iOS 18.0+, install the GraffityARCloud product, and add camera and location descriptions. Copy the complete UIKit ARExperienceViewController into the Runner target. It retains the SDK, embeds the AR view, and shuts down before its native Close button dismisses it.

Register a method channel in your Flutter app delegate. Match the channel name and methods to your Dart code.

AppDelegate.swift
import UIKit import Flutter @main @objc class AppDelegate: FlutterAppDelegate { private weak var arHost: ARExperienceViewController? private var arChannel: FlutterMethodChannel? override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) if let controller = window?.rootViewController as? FlutterViewController { let channel = FlutterMethodChannel( name: "samples.flutter.dev/ar", binaryMessenger: controller.binaryMessenger ) arChannel = channel channel.setMethodCallHandler { [weak self, weak controller] call, result in Task { @MainActor in guard let self, let controller else { result(FlutterError( code: "NO_PRESENTER", message: "The Flutter host is unavailable.", details: nil )) return } switch call.method { case "openAR": guard let args = call.arguments as? [String: Any], let token = args["accessToken"] as? String else { result(FlutterError( code: "INVALID_ARGUMENTS", message: "accessToken must be a string.", details: nil )) return } guard self.arHost == nil, controller.presentedViewController == nil else { result(FlutterError( code: "AR_ALREADY_OPEN", message: "Close the presented view first.", details: nil )) return } do { let host = try ARExperienceViewController(accessToken: token) host.modalPresentationStyle = .fullScreen self.arHost = host controller.present(host, animated: true) { result(nil) } } catch { result(FlutterError( code: "AR_INIT_FAILED", message: error.localizedDescription, details: nil )) } case "closeAR": if let host = self.arHost { await host.endSession() host.dismiss(animated: true) { result(nil) } self.arHost = nil } else { result(nil) } default: result(FlutterMethodNotImplemented) } } } } return super.application( application, didFinishLaunchingWithOptions: launchOptions ) } }

This registration example assumes a Flutter app exposing its root controller through window at launch. If your Flutter app uses a scene-based or custom engine setup, register the same channel when that engine and controller become available.

import 'package:flutter/services.dart'; const arChannel = MethodChannel('samples.flutter.dev/ar'); Future<void> openAR(String accessToken) async { try { await arChannel.invokeMethod<void>('openAR', {'accessToken': accessToken}); } on PlatformException catch (error) { // Show your app's error UI. throw Exception(error.message ?? 'Unable to open AR.'); } } Future<void> closeAR() => arChannel.invokeMethod<void>('closeAR');

The open result confirms presentation, not localization. Use a separate event channel if Dart needs SDK callbacks. Both the native Close button and the programmatic close method end the session before dismissing. See Flutter’s platform-channel guide .

Android with Flutter

Install Android SDK 5.0.2, use API 28+, and add the manifest permissions and Activity declaration.

Copy the complete GraffityARActivity host and layout into the Android app target and replace its example package with yours. It owns configuration, SDK initialization, permissions, ARCore readiness, and cleanup.

Register the same channel and openAR method used by Dart:

MainActivity.kt
package com.example.app import android.content.Intent import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel class MainActivity : FlutterActivity() { override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel( flutterEngine.dartExecutor.binaryMessenger, "samples.flutter.dev/ar" ).setMethodCallHandler { call, result -> if (call.method != "openAR") { result.notImplemented() return@setMethodCallHandler } val token = call.argument<String>("accessToken") if (token.isNullOrBlank()) { result.error("INVALID_TOKEN", "accessToken must be a nonblank string.", null) return@setMethodCallHandler } try { val intent = Intent(this, GraffityARActivity::class.java) .putExtra(GraffityARActivity.EXTRA_ACCESS_TOKEN, token) startActivity(intent) result.success(null) } catch (error: Exception) { result.error("AR_LAUNCH_FAILED", error.message, null) } } } }

The result confirms Activity launch. The native host then handles permission approval, SDK initialization, and localization. Its Close button and Android Back end the experience and run shutdown. This Android bridge exposes openAR; the iOS bridge above also exposes closeAR.

Use a separate event channel backed by GraffityARCloudListener if Dart needs localization, content, or navigation events. See Flutter’s platform-channel guide .

Report Issues

If you’re facing any issues related to this package, please find out here.