{"id":13730586,"url":"https://github.com/audiooffler/JucyFluttering","last_synced_at":"2025-05-08T03:30:59.286Z","repository":{"id":48130902,"uuid":"298602723","full_name":"audiooffler/JucyFluttering","owner":"audiooffler","description":"A simple iOS \u0026Android example for how to integrate Flutter (Dart) as user interface and JUCE (C++) as backend.","archived":false,"fork":false,"pushed_at":"2021-03-19T15:41:37.000Z","size":101,"stargazers_count":111,"open_issues_count":4,"forks_count":20,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-08-04T02:09:39.093Z","etag":null,"topics":["android","cpp","dart","flutter","ios","juce"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/audiooffler.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-09-25T14:54:16.000Z","updated_at":"2024-08-02T15:57:48.000Z","dependencies_parsed_at":"2022-08-22T11:20:42.217Z","dependency_job_id":null,"html_url":"https://github.com/audiooffler/JucyFluttering","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/audiooffler%2FJucyFluttering","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/audiooffler%2FJucyFluttering/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/audiooffler%2FJucyFluttering/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/audiooffler%2FJucyFluttering/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/audiooffler","download_url":"https://codeload.github.com/audiooffler/JucyFluttering/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224695584,"owners_count":17354432,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","cpp","dart","flutter","ios","juce"],"created_at":"2024-08-03T02:01:16.882Z","updated_at":"2024-11-14T21:31:16.166Z","avatar_url":"https://github.com/audiooffler.png","language":"C","funding_links":[],"categories":["Integrations"],"sub_categories":[],"readme":"# Jucy Fluttering\n\nA simple mobile app example (iOS, Android) that uses a Flutter (Dart) UI and JUCE (C++) as backend.\n\n## Project Setup and Boilerplates\n\n- Created a JUCE GUI project `JucyFluttering` with Android and iOS exporters (well it does not really show a GUI, but has a message loop running, to provide timers, event handling etc.)\n\n- Created a flutter plugin project (subfolder `fluttering`) for platforms android and iOs, using  Java for android, and ObjectiveC for iOS\n  - `flutter create --template=plugin --platforms=android,ios -a java -i objc fluttering`\n  - the .dart source file are placed in `fluttering/lib`\n\nJUCE is already does a good job at setting up all the platform-dependend boilerplate code to instantiate and run iOs and Android apps. Some adjustments had to be done to do the Flutter instantiation part too, as documented here:\n - [Adding Flutter to Android](https://flutter.dev/docs/development/add-to-app/android) \n -  [Adding Flutter to iOS](https://flutter.dev/docs/development/add-to-app/ios/)\n\n### Android Application and Android Activity\n\nMade custom activity and application Java class files, placed in `Sources/Android`. \n - The Actvity extends the FlutteringActivity class\n - The Application initialises JUCE, but also and instantiates, pre-warms and caches the Flutter Engine, and sets the initial Flutter navigation route to '`/`'\n\n### Android Exporter Setup in Projucer Project and Android Gradle\n\nSome manual configuration for the Projucers Android exporter was necessary:\n- Java Source code folders:\n  ``` \n  Source/Android\n  ```\n\n- Custom Android Activity:\n    ```\n    eu.selfhost.audiooffler.jucyfluttering.FlutteringActivity\n    ```\n\n- Custom Android Application:\n    ```\n    eu.selfhost.audiooffler.jucyfluttering.FlutteringApplication\n    ```\n\n-  Module Dependencies:\n   ```\n   implementation project(':flutter')\n   ```\n  \n- Extra module's build.gradle content: \n    ```\n    defaultConfig {\n        ndk {\n            // filter for architectures supported by Flutter\n            abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'\n        }\n    }\n\n    compileOptions {\n        // Flutter Android engine uses Java 8 features\n        sourceCompatibility 1.8\n        targetCompatibility 1.8\n    }\n    ```\n- Custom settings.gradle content:\n    ```\n    setBinding(new Binding([gradle: this]))\n    evaluate(new File(\n        settingsDir.parentFile.parentFile,\n        'fluttering/.android/include_flutter.groovy'\n    ))\n    ```\n- Android Theme:\n  ```\n  @android:style/Theme.NoTitleBar\n  ```\n\nProjucer can't create / manipulate a `gradle.properties` file, but this is needed for Flutter\n - The file with the following content was manually placed at `Builds/Android` folder of this repository, it won't get overwritten by the Projucre, just do not delete it manually:\n\n    ```\n    android.useAndroidX=true\n    android.enableJetifier=true\n    ```\n\n### iOS UIApplicationDelegate\n\nMade a custom App Delegate class, placed at `Sources/iOS`. This is an [UIApplicationDelegate](https://developer.apple.com/documentation/uikit/uiapplicationdelegate) for running JUCE and Flutter in an iOS app. It's `.mm` File also has the `START_JUCE_APPLICATION_WITH_CUSTOM_DELEGATE` call to automatically start the JUCEApplication with this Delegate, `as documented in JUCE/modules/juce_events/messages/juce_Initialisation.h`. The delegate takes care of:\n- Juce Initialisation and handling\n- Flutter Engine Initalisation and handling\n- UIWindow ([showing the Flutter View](https://flutter.dev/docs/development/add-to-app/ios/add-flutter-screen?tab=vc-objective-c-tab))\n- FlutterViewController\n- FlutterPluginAppLifeCycleDelegate\n\n### iOS CocooaPods and iOS Exporter Setup in Projucer Project \n\nCocoaPods is needed for FLutter integration. \n- If do not have it installed yet (check with `which pod`), do so with `sudo gem install cocoapod`\n- A Podfile was made manually and placed in this repository (`Builds/iOS/Podfile`), containing the following. The Projucer won't overwite it, just do not delete it manually.\n  ```\n    platform :ios, '9.0'\n\n    flutter_application_path = '../../fluttering'\n    load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')\n\n    target 'Jucy Fluttering - App' do\n    \n    install_all_flutter_pods(flutter_application_path)\n\n    end\n  ```\n\nIn Projucer, some iOS export settings had to be configured:\n- To make it work with CocoaPods (as found in adamski's thread in the [JUCE forum](https://forum.juce.com/t/solved-cocoapods-and-introjucer-generated-projects-linker-error-react-native/16426)), for all build target configuations (Release/Debug), set Binary Location:\n  ```\n  $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)\n  ```\n- Custom PList\n    ```\n    \u003cplist\u003e\n        \u003cdict\u003e\n            \u003ckey\u003eUIBackgroundModes\u003c/key\u003e\n            \u003carray\u003e\n                \u003cstring\u003efetch\u003c/string\u003e\n                \u003cstring\u003eremote-notification\u003c/string\u003e\n            \u003c/array\u003e\n        \u003c/dict\u003e\n    \u003c/plist\u003e\n    ```\n## Build and Debug\n\n### Android    \n\nNo extra steps needed building. The project can be opened with Android Studio from the Projucer or by opening the `Builds/Android` project folder in Android Studio.\nIf dart support is enabled for the project in Android Studio, it even attaches the flutter debugger.\nElse, while running / debugging, one may call `flutter attach ` from terminal (first go to `cd ./fluttering`).\n\n### iOS\n\nAfter saving / exporting with Projucer, the XCode Project can't simply be build as usual for JUCE projects. Two more steps are necassary:\n- from terminal **run `pod install`** (first go to `cd ./Builds/iOS`)\n- then **open the generated `.xcworkspace` project** and build that\n  - the .xcodeproj would fail, due to the missing Flutter dependencies\n\nWhile running / debugging from xCode, one may call `flutter attach ` from terminal (first go to `cd ./fluttering`).\n\n## Calls and Messages between JUCE/C++ and Flutter/Dart\n\nCheck out the [Flutter/dart:ffi documentation](https://flutter.dev/docs/development/platform-integration/c-interop) for the basic setup of accessing nativ C++ code from Flutter/dart.\n\nSome examples can be found in `Sources/JucyFlutteringInterop.h` and the dart counterparts in `fluttering/lib/juce_fluttering_interop.dart`.\n\n### Dart code for executing simple JUCE/C++ functions\n- Externalized JUCE/C++ functions can be called from Fluttter/Dart via [dart:ffi NativeFunction](https://dart.dev/guides/libraries/c-interop)s, since the dart isolate (thread) will execute it.\n- Some boilercode / wrapping is still needed. See `fluttering/lib/juce_fluttering_interop.dart`.\n\n### Getting Strings from JUCE/C++ into dart\n- For getting `juce::Strings`, JUCE has to return UTF-8 char* pointers\n- If the String is local it has to be copied to memory or it will get lost on return. \n- This can be done using `toRawUTF8`, `malloc` and `strcpy`, see the example in `EXTERN_C const char *getAppName()` in `Source/JucyFlutteringInterop.h`, using the helper function `const char *copyStringToUTF8(String juceString)` from the same file\n- The dart caller then can convert this to a dart String, using `fromUtf8` and call `free` on the returned `Pointer\u003cUtf8\u003e`, utilizing  [import `package:ffi/ffi.dart`](https://pub.dev/packages/ffi). See tthe example in `fluttering/lib/juce_fluttering_interop.dart`\n\n### Asynchronous Messages from JUCE/C++ to Flutter (instead of callbacks)\n\nProblem:\n- Access to classes and values is only allowed from the same isolate! \n- JUCE Message Loop (e.g. `juce::Timer`) or AudioCallback Threads (e.g. `juce::AudioProcessor`) won't run in the same isolate as Flutter/Dart UI.\n- Therefore, just registering callback [dart:ffi function pointers](https://api.flutter.dev/flutter/dart-ffi/Pointer/fromFunction.html) in your JUCE/C++ code will ***not*** be sufficient. (Except if the callback was called from some C++ function that was started from dart).\n\nSolution:\n\n- Different isolates can communicate by sending values through ports (see [ReceivePort](https://api.flutter.dev/flutter/dart-isolate/ReceivePort-class.html), [SendPort](https://api.flutter.dev/flutter/dart-isolate/SendPort-class.html)).\n- To access the dart native API from C++, files from\n[GitHub: dart-lang/sdk/runtime/include](https://github.com/dart-lang/sdk/tree/master/runtime/include) where included in the JUCE project [Source/DartApiDL](file://./Source/DartApiDL/) (the folder was added to the header search paths for all Build/Debug targets). see https://github.com/mraleph/go_dart_ffi_example\n- Init by calling the NativeAPI from dart (that will be the receiver) and tell JUCE/C++ the port.\n- From then on JUCE/C++ may send messages containing objects with int, double or UTF-8 char* string pointers to the dart receiver port.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faudiooffler%2FJucyFluttering","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faudiooffler%2FJucyFluttering","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faudiooffler%2FJucyFluttering/lists"}