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-viewerStep 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.
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:
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.