Flutter AR-MOD widget for embedding AR features in flutter.

What is the ARMOD SDK?

In short, ARMOD's solution is an AR experience platform solution similar to Snapchat (Lens Studio) and Facebook (SparkAR)!

AR-MOD is a derivative framework based on Unity ARFoundation. MOD in AR-MOD means Modification in English, meaning: modification and module. This concept is widely used in games, corresponding to modifiable video games. Famous games such as Warcraft, Red Alert, Half-Life, CS, Victory Day and more!

We transplant the MOD concept into AR technology to give users more freedom to create the AR creative interactive experience content they need! In this process, users do not need to worry about AR-SDK algorithm and code implementation, but only need to devote themselves to the production of AR creative interactive experience content. With only a small amount of code, you can use all the capabilities of AR-MOD on the APP to create greater commercial value.

Getting Started

  1. Install this plugin to your flutter project. If you do not know how to install the Flutter Package you can click here to see the document.

    Install Guid

    The AR-MOD SDK currently provides a plug-in package for Flutter. You can install it through flutter_armod_widget: ^VERSION in your flutter project pubspec.yaml !

    # Other config
        sdk: flutter
      flutter_armod_widget: ^0.0.3
    # Other config
  2. Go to PhantomsXR github respository. Download and Unzip it.

  3. Choose iOS or Android platform from the options below to set.

    Android Setup
    1. Go to the location of your FLUTTER SDK PATH/.pub-cache/hosted/pub.dartlang.org/flutter_armod_widget-VERSION/ folder, then paste the libs to android platform folder.

      Maybe hide the pub-cache folder. You need to turn on the Show hidden folders option.

    2. Run Flutter pub get command in your termial.
    iOS Setup
    1. Create the ThirdParties folder to your XCode project.

    2. Import UnityFramework.framework to the folder(ThridParties).

    3. Add the Framewrok to Xcode -> Targets -> Your APP -> General -> Franework,Libraries, and Embedded Content area, And set the Embed mode to Embed & Sign.

    4. Execute the cd iOS command and run Pod install command in your termial.

    5. Double-Click to open the Runner.xcworkspace file. It will be launch the XCode app.

    6. If you're using Swift, open the ios/Runner/AppDelegate.swift file and change the following:

          import UIKit
          import Flutter
      +    import flutter_armod_widget
          @objc class AppDelegate: FlutterAppDelegate {
              override func application(
                  _ application: UIApplication,
                  didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
              ) -> Bool {
      +           InitARMODIntegrationWithOptions(argc: CommandLine.argc, argv: CommandLine.unsafeArgv, launchOptions)
      +           let nativeCalls: AnyClass? = NSClassFromString("FrameworkLibAPI")
      +           nativeCalls?.registerAPIforNativeCalls(ARMODCallbackAPI())
                  GeneratedPluginRegistrant.register(with: self)
                  return super.application(application, didFinishLaunchingWithOptions: launchOptions)

      If you're using Objective-C, open the ios/Runner/main.m file and change the following:

      +    #import "flutter_armod_widget.swift.h"
          int main(int argc, char * argv[]) {
                @autoreleasepool {
      +             InitARMODIntegration(argc, argv);
      +             [NSClassFromString(@"FrameworkLibAPI") registerAPIforNativeCalls:[ARMODCallbackAPI alloc]];
                    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    7. Edit the info.plist

      +        <key>io.flutter.embedded_views_preview</key>
      +        <string>YES</string>
      +        <key>Privacy - Camera Usage Description</key>
      +        <string>$(PRODUCT_NAME) uses Cameras</string>
      +       <key>NSBonjourServices</key>
      +       <string>_dartobservatory._tcp</string>
    8. Add #import <UnityFramework/NativeCallProxy.h> to Runner-Bridging-Header.h file.

         #import "GeneratedPluginRegistrant.h"
      +   #import <UnityFramework/NativeCallProxy.h>
  4. Then write a new screen(Flutter) for AR-MOD

import 'dart:async';

import 'package:armod_flutter_store/src/model/data.dart';
import 'package:armod_flutter_store/src/themes/theme.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_armod_widget/flutter_armod_widget.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter/services.dart';

import '../config/phantomsxrConfig.dart';

class ARView extends StatefulWidget {
  ARView({Key? key}) : super(key: key);

  ARViewState createState() => ARViewState();

class ARViewState extends State<ARView> {
  late ARMODWidgetController _armodWidgetController;
  bool _onWillPop = false;
  bool _isClosedByBack = false;
  void initState() {

  void dispose() {

  Widget _appBar() {
    return SafeArea(
        top: true,
        child: GestureDetector(
          child: Container(
            padding: AppTheme.padding,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                  width: 25,
                  height: 25,
                  alignment: Alignment.center,
                  decoration: BoxDecoration(
                    color: Colors.white,
                    borderRadius: BorderRadius.all(Radius.circular(15)),
                  child: Icon(Icons.close, color: Colors.black54, size: 20),
          onTap: () async {
            await _onBackPressed();

  Widget build(BuildContext context) {
    return new WillPopScope(
      onWillPop: _onBackPressed,
      child: new Scaffold(
        body: Stack(
          children: [
              onARMODCreated: onARMODCreate,
              onARMODExit: onARMODExit,
              onARMODLaunch: onARMODLaunch,
              onAddLoadingOverlay: onAddLoadingOverlay,
              onDeviceNotSupport: onDeviceNotSupport,
              onNeedInstallARCoreService: onNeedInstallARCoreService,
              onOpenBuiltInBrowser: onOpenBuiltInBrowser,
              onPackageSizeMoreThanPresetSize: onPackageSizeMoreThanPresetSize,
              onRecognitionComplete: onRecognitionComplete,
              onRecognitionStart: onRecognitionStart,
              onRemoveLoadingOverlay: onRemoveLoadingOverlay,
              onSdkInitialized: onSdkInitialized,
              onThrowException: onThrowException,
              onTryAcquireInformation: onTryAcquireInformation,
              onUpdateLoadingProgress: onUpdateLoadingProgress,
              fullscreen: true,

  ///Handling the back event
  Future<bool> _onBackPressed() async {
    //Close AR-MOD SDK

    while (!_onWillPop) {
      //We need to delayed executed because release AR-MOD operation is async.
      //May need to wait one more frame
      await Future.delayed(Duration(milliseconds: 1));
    _isClosedByBack = true;
    return _onWillPop;

  showAlertDialog(BuildContext context, String title, String msg, bool close) {
    // set up the button
    Widget okButton = TextButton(
      child: Text("OK"),
      onPressed: () async {
        if (close) {
          //Dismiss loading ui

          //Dismiss alert
          Navigator.of(context, rootNavigator: true).pop();

          //Dismiss view
          await _onBackPressed();

    // set up the AlertDialog
    AlertDialog alert = AlertDialog(
      title: Text(title),
      content: Text(msg),
      actions: [

    // show the dialog
      barrierDismissible: false,
      context: context,
      builder: (BuildContext context) {
        return alert;

  Future<void> onARMODCreate(controller) async {
    this._armodWidgetController = controller;
    if (await _armodWidgetController.isLoaded() != false)

  void onARMODLaunch() {
    var orientationId =
        MediaQuery.of(context).orientation == Orientation.portrait ? '1' : '2';


        Duration(milliseconds: 125),
        () => {

  void onThrowException(String errorMsg, int erorCode) {
    showAlertDialog(context, "(Error:$erorCode)", errorMsg, true);

  void onARMODExit() {
    //Wait to release all asset
        Duration(milliseconds: 500),
        () => {
              _onWillPop = true,

              //Close by AR-Experiences
              if (!_isClosedByBack) Navigator.of(context).pop(true),

  void onUpdateLoadingProgress(progress) {
        status: '${(progress * 100).toStringAsFixed(0)}%');

  Future<String> onTryAcquireInformation(String opTag) async {
    await Future.delayed(Duration(seconds: 3));
    return "onTryAcquireInformation_$opTag";

  void onAddLoadingOverlay() {
      ..indicatorType = EasyLoadingIndicatorType.fadingCircle
      ..maskColor = Colors.red.withOpacity(0.5)
      ..dismissOnTap = false;

  void onRemoveLoadingOverlay() {

  void onDeviceNotSupport() {
        "Device Not Supported",
        "Your device is not supoorted! \n Will downgrade to normal version",

  void onRecognitionStart() {}

  void onNeedInstallARCoreService() {
    showAlertDialog(context, "ARCore Service Need",
        "You need to install the ARCore service!", false);

  void onSdkInitialized() {}

  void onOpenBuiltInBrowser(url) {}

  void onPackageSizeMoreThanPresetSize(currentSize, presetSize) {}

  void onRecognitionComplete() {}