Skip to content

trsonic/juce_flutter

juce_flutter logo

juce_flutter

A JUCE module for embedding Flutter UIs in audio plugins (VST3, AU) and standalone apps.

Status: Experimental — macOS and Windows working.

Example plugin running as VST3 in REAPER
Example plugin running as a VST3 in REAPER — Flutter UI with real-time audio analysis

What it does

Drop a FlutterComponent into your JUCE plugin editor and get a full Flutter UI rendered at 60fps. Works on macOS (Metal/Impeller) and Windows (ANGLE/D3D), in standalone apps and DAW hosts (tested in REAPER).

#include <juce_flutter/juce_flutter.h>

class MyEditor : public juce::AudioProcessorEditor {
    juce_flutter::FlutterComponent flutter;
public:
    MyEditor(AudioProcessor& p) : AudioProcessorEditor(p) {
        flutter.loadFromPluginBundle();
        addAndMakeVisible(flutter);
        setSize(800, 600);
    }
    void resized() override { flutter.setBounds(getLocalBounds()); }
};

How it works

On each platform, Flutter is hosted in a child window that tracks the JUCE component's position:

macOS                                     Windows
JUCE Component (host window)              JUCE Component (host window)
  └─ NSPanel (borderless)                   └─ Child HWND
       └─ FlutterViewController                 └─ FlutterDesktopViewController
            └─ Metal/Impeller                         └─ ANGLE/D3D

On macOS, Flutter's Metal/Impeller renderer requires being the contentViewController of an NSWindow, so we create a borderless NSPanel and attach it as a child of the host window. On Windows, a child HWND hosts the FlutterDesktopViewController via standard SetParent() + WS_CHILD.

Engine and view controller creation is deferred until the component is on-screen — creating them during construction blocks the host's run loop. See docs/ARCHITECTURE.md for details.

Requirements

  • macOS: macOS 13+ (arm64)
  • Windows: Windows 10+, Visual Studio 2022 with C++ desktop workload
  • JUCE 7+ or 8+
  • Flutter SDK with desktop support enabled
  • CMake 3.22+

Building the example

The example plugin includes real-time audio analysis (RMS, peak, crest factor, 32-band spectrum, waveform) computed in C++ and visualized in Flutter.

Clone

git clone https://github.com/trsonic/juce_flutter.git
cd juce_flutter

macOS

cd example
./build.sh run standalone        # Build + run standalone
./build.sh run reaper            # Build + install VST3 + launch REAPER
./build.sh build                 # Build only
./build.sh install               # Install VST3 to ~/Library/Audio/Plug-Ins/VST3/
./build.sh clean                 # Clean build directory

Windows

cd example
.\build_win.ps1 run standalone   # Build + run standalone
.\build_win.ps1 build            # Build only
.\build_win.ps1 install          # Install VST3 to %LOCALAPPDATA%\Programs\Common Files\VST3\
.\build_win.ps1 clean            # Clean build directory

Both scripts handle the full pipeline: build the Flutter UI, compile the C++ plugin, and embed all Flutter assets into the app/VST3 bundle.

Manual CMake build

If you prefer to run CMake directly, build the Flutter UI first:

# macOS
cd example/flutter_ui && flutter build macos --release && cd ../..

# Windows
cd example\flutter_ui && flutter build windows --release && cd ..\..

Then configure and build:

# macOS
cd example
cmake -B build -DCMAKE_BUILD_TYPE=Release \
  -DFLUTTER_ENGINE_PATH="/path/to/FlutterMacOS.framework"
cmake --build build -j8
# Windows
cd example
cmake -B build_win -G "Visual Studio 17 2022" -A x64 `
  -DFLUTTER_ENGINE_PATH="C:\flutter\bin\cache\artifacts\engine\windows-x64-release"
cmake --build build_win --config Release -j

After building, you need to embed Flutter into the bundles. See the build scripts for the exact copy steps, or docs/ARCHITECTURE.md for the expected bundle layouts.

Using in your own project

Via CMake FetchContent

include(FetchContent)

FetchContent_Declare(JUCE
    GIT_REPOSITORY https://github.com/juce-framework/JUCE.git
    GIT_TAG 8.0.12
    GIT_SHALLOW TRUE)

FetchContent_Declare(juce_flutter
    GIT_REPOSITORY https://github.com/trsonic/juce_flutter.git
    GIT_TAG main)

FetchContent_MakeAvailable(JUCE juce_flutter)

target_link_libraries(MyPlugin PRIVATE juce_flutter)

Via add_subdirectory

add_subdirectory(path/to/juce_flutter)
target_link_libraries(MyPlugin PRIVATE juce_flutter)

Flutter engine linking

You also need to link the Flutter engine. The setup differs per platform:

macOS — link FlutterMacOS.framework and set rpaths:

set(FLUTTER_FW_DIR "/path/to/dir/containing/FlutterMacOS.framework")
target_compile_options(MyPlugin PRIVATE "-F${FLUTTER_FW_DIR}")
target_link_libraries(MyPlugin PRIVATE "-F${FLUTTER_FW_DIR}" "-framework FlutterMacOS")

set_target_properties(MyPlugin_Standalone PROPERTIES
    BUILD_RPATH "@executable_path/../Frameworks")
set_target_properties(MyPlugin_VST3 PROPERTIES
    BUILD_RPATH "@loader_path/../Frameworks")

Windows — link flutter_windows.dll.lib with delay-load and build the C++ client wrapper:

set(FLUTTER_ENGINE_PATH "C:/flutter/bin/cache/artifacts/engine/windows-x64-release")
target_include_directories(MyPlugin PUBLIC "${FLUTTER_ENGINE_PATH}")
target_link_libraries(MyPlugin_VST3 PRIVATE
    "${FLUTTER_ENGINE_PATH}/flutter_windows.dll.lib" delayimp.lib)
target_link_options(MyPlugin_VST3 PRIVATE "/DELAYLOAD:flutter_windows.dll")

See example/CMakeLists.txt for the full setup including the C++ client wrapper.

C++ to Dart bridge

Send data from C++ to Flutter:

auto* bridge = flutter.getBridge();
bridge->sendParameterUpdate("gain", 0.75f);
bridge->invoke("spectrum", spectrumData);

Receive in Dart:

const channel = MethodChannel('com.jahlion.juce_flutter/bridge');
channel.setMethodCallHandler((call) async {
    if (call.method == 'parameterChanged') {
        final id = call.arguments['id'];
        final value = call.arguments['value'];
        // Update UI...
    }
});

On macOS the bridge uses ObjC FlutterMethodChannel. On Windows it uses the C++ flutter::MethodChannel<> wrapper with flutter::EncodableValue.

Module structure

juce_flutter/
├── juce_flutter.h                    # Module header
├── juce_flutter.mm                   # Module implementation (macOS, ObjC++)
├── juce_flutter.cpp                  # Module implementation (Windows)
├── juce_flutter.moduledesc           # JUCE module descriptor
└── flutter/
    ├── FlutterComponent.h/cpp        # Drop-in juce::Component (cross-platform)
    ├── FlutterRenderer.h             # Abstract renderer interface
    ├── FlutterRenderer_mac.mm        # macOS: child NSPanel + Metal/Impeller
    ├── FlutterRenderer_win.cpp       # Windows: child HWND + ANGLE/D3D
    ├── FlutterBridge.h/cpp           # Bridge base (cross-platform)
    ├── FlutterBridge_mac.mm          # macOS: ObjC FlutterMethodChannel
    ├── FlutterBridge_win.cpp         # Windows: C++ flutter::MethodChannel
    ├── FlutterBundleResolver.mm      # macOS: bundle/asset path resolution
    └── FlutterBundleResolver_win.cpp # Windows: bundle/asset path resolution

Known limitations

  • Child window approach: The Flutter panel is a separate window, so it floats above the host. This works well but means Flutter content can't be clipped by the host window bounds.
  • No custom engine builds: Uses stock Flutter engine from the SDK. The low-level C Embedder API (FlutterEngineRun) is not exported from the standard macOS framework.
  • Code signing (macOS): Embedded frameworks must be re-signed after copying into plugin bundles.
  • Input handling (Windows): Basic mouse/keyboard works automatically via child HWND. Focus management in some DAW hosts and high-DPI scaling may need tweaking.

Research findings (macOS)

Approach Result Notes
NSView subview Transparent Metal needs contentViewController
Child NSPanel 60fps Works in standalone + REAPER VST3
CGWindowListCreateImage Requires permission Screen Recording TCC
NSView cacheDisplayInRect Black output Can't capture Metal layers
C Embedder API Not exported Requires custom engine build

License

MIT — see LICENSE.

About

JUCE module for embedding Flutter UIs into your plugins

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Contributors