go-flutter-desktop/go-flutter
Flutter on Windows, MacOS and Linux - based on Flutter Embedding, Go and GLFW.
Mixed signals — read the receipts
weakest axisno tests detected; no CI workflows detected
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 3w ago
- ✓12 active contributors
- ✓BSD-3-Clause licensed
Show all 6 evidence items →Show less
- ⚠Concentrated ownership — top contributor handles 55% of recent commits
- ⚠No CI workflows detected
- ⚠No test directory detected
What would change the summary?
- →Use as dependency Mixed → Healthy if: add a test suite
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Forkable" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/go-flutter-desktop/go-flutter)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/go-flutter-desktop/go-flutter on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: go-flutter-desktop/go-flutter
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/go-flutter-desktop/go-flutter shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
WAIT — Mixed signals — read the receipts
- Last commit 3w ago
- 12 active contributors
- BSD-3-Clause licensed
- ⚠ Concentrated ownership — top contributor handles 55% of recent commits
- ⚠ No CI workflows detected
- ⚠ No test directory detected
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live go-flutter-desktop/go-flutter
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/go-flutter-desktop/go-flutter.
What it runs against: a local clone of go-flutter-desktop/go-flutter — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in go-flutter-desktop/go-flutter | Confirms the artifact applies here, not a fork |
| 2 | License is still BSD-3-Clause | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 50 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of go-flutter-desktop/go-flutter. If you don't
# have one yet, run these first:
#
# git clone https://github.com/go-flutter-desktop/go-flutter.git
# cd go-flutter
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of go-flutter-desktop/go-flutter and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "go-flutter-desktop/go-flutter(\\.git)?\\b" \\
&& ok "origin remote is go-flutter-desktop/go-flutter" \\
|| miss "origin remote is not go-flutter-desktop/go-flutter (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(BSD-3-Clause)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"BSD-3-Clause\"" package.json 2>/dev/null) \\
&& ok "license is BSD-3-Clause" \\
|| miss "license drift — was BSD-3-Clause at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "application.go" \\
&& ok "application.go" \\
|| miss "missing critical file: application.go"
test -f "embedder/embedder.go" \\
&& ok "embedder/embedder.go" \\
|| miss "missing critical file: embedder/embedder.go"
test -f "glfw.go" \\
&& ok "glfw.go" \\
|| miss "missing critical file: glfw.go"
test -f "plugin/binary-messenger.go" \\
&& ok "plugin/binary-messenger.go" \\
|| miss "missing critical file: plugin/binary-messenger.go"
test -f "event-loop.go" \\
&& ok "event-loop.go" \\
|| miss "missing critical file: event-loop.go"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 50 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~20d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/go-flutter-desktop/go-flutter"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
go-flutter is an unofficial desktop port of Flutter that runs on Windows, macOS, and Linux by implementing Google's Flutter Embedding API in Go and GLFW. It lets developers write cross-platform desktop applications using the same Dart/Flutter codebase used for iOS/Android, with the Flutter engine communicating through Go bindings for platform-specific input handling, rendering via OpenGL, and lifecycle management. Monolithic Go package structure: the root github.com/go-flutter-desktop/go-flutter contains core logic (application.go, event-loop.go, lifecycle.go), with isolated subsystems under internal/ (keyboard, opengl, tasker, debounce, currentthread) and embedder/ (FFI bridge to Flutter's C API). Plugin system lives in plugin/ subdirectory with codec implementations (binary-codec.go, message channels). Platform-specific code branches at internal/keyboard_darwin.go vs internal/keyboard_pc.go.
👥Who it's for
Flutter mobile developers who want to extend their iOS/Android apps to desktop without rewriting UI logic; desktop application developers who prefer Flutter's reactive UI paradigm over Electron or Qt; and open-source contributors interested in cross-platform Go tooling and custom Flutter engine embeddings.
🌱Maturity & risk
Actively maintained but early-to-mid stage: the project has solid Go Report Card ratings, reasonable GitHub stars (reflected in 'Awesome Flutter' badge), and demonstrates working implementations across three major platforms. However, presence of isolated test files (e.g., plugin/binary-codec_test.go) alongside untested core files, plus reliance on the separate hover CLI tool for typical workflows, indicates the core library prioritizes breadth over production-grade test coverage.
Moderate risk: the project depends heavily on flutter/flutter's embedding API stability (external dependency risk), has minimal test coverage for critical paths like isolate.go, event-loop.go, and glfw.go, and is maintained by a community team rather than Google/Flutter authors, creating potential version drift. The C FFI boundary (embedder/embedder.h, embedder_helper.c) introduces memory safety concerns if Flutter engine APIs change unexpectedly.
Active areas of work
The README mentions active work on hot-reload (issue #129) and an expanding plugin system with MethodChannel support (marked with * and ** in README). The dependency on recent versions of go-gl/glfw (August 2022) and go-gl/gl (December 2021) suggests ongoing maintenance to track upstream OpenGL/GLFW improvements.
🚀Get running
Clone the repo, then use the official hover CLI tool (referenced in README and dependencies): go install github.com/go-flutter-desktop/hover@latest. For direct Go package integration, add require github.com/go-flutter-desktop/go-flutter v1.x.x to your go.mod. See hover repo for end-to-end setup; raw go-flutter is typically not used standalone.
Daily commands:
No traditional 'dev server'. This is a library, not an app. Typical workflow: use hover CLI (hover run) which wraps go-flutter. For direct Go development, import the package and call option.Application(), window.SetIcon(), etc. See README and go-flutter wiki for example app patterns.
🗺️Map of the codebase
application.go— Core Flutter application lifecycle and initialization—entry point for all desktop Flutter apps built with this framework.embedder/embedder.go— Wraps the Flutter C API (embedder.h) and manages the Flutter engine lifecycle; essential for understanding native integration.glfw.go— GLFW window management and platform event loop integration—bridges native windowing system to Flutter engine.plugin/binary-messenger.go— Core plugin communication protocol; all platform channels and method invocations flow through this abstraction.event-loop.go— Main event dispatcher and task scheduling for the Flutter runtime; coordinates timing between platform and engine.window.go— Window configuration and platform-specific window properties; essential for multi-platform desktop support.embedder/embedder.h— C header defining the Flutter Embedding API contract; understanding this is critical for engine integration.
🛠️How to make changes
Add a new platform channel method (request/response RPC)
- Create a new method channel handler by embedding a
*plugin.MethodChannelin your plugin struct (plugin/method-channel.go) - Define your method handler function that receives
*plugin.MethodCalland returns result or error (plugin/method-handler.go) - Call
channel.InvokeMethod("methodName", args)to invoke from Go; Dart calls viaplatform.invokeMethod()(plugin/binary-messenger.go) - Use
plugin.StandardMethodCodecto automatically serialize Dart types (int, String, Map, List) to Go (plugin/standard-method-codec.go)
Add a new streaming event channel (continuous updates)
- Create an
*plugin.EventChannelwith a unique channel name (plugin/event-channel.go) - Implement an
EventSinkto send events downstream (e.g., from a goroutine monitoring a sensor or file) (plugin/event-sink.go) - Set a handler that returns your
EventSinkwhen the Dart side subscribes (plugin/event-channel.go) - Send events via
sink.Success(data)orsink.Error()from any goroutine; Flutter automatically deserializes via codec (plugin/binary-messenger.go)
Configure application window properties and lifecycle
- Create a new
Optionusingfunc(c *Config)and pass it toRun()or apply it viaOption.Apply()(option.go) - Common options:
WindowInitialDimensions(),WindowDimensionLimits(),WindowInitialPosition(),AddPlugin()(application.go) - For lifecycle hooks, use
ApplicationOptions.AddCallbackOnFinish()or handleapplication.LifecycleDelegate(lifecycle.go) - Apply window-specific config (icon, title, flags) in the Option builder before engine initialization (
window.go)
Integrate a custom Go plugin with the runtime
- Implement the
plugin.Plugininterface withInitPlugin(messenger plugin.BinaryMessenger)method (plugin/doc.go) - Inside
InitPlugin(), create your method/event channels using the messenger and store handlers (plugin/binary-messenger.go) - Register the plugin by passing it to
WithPlugin()option or via application config (plugin.go) - From Dart, instantiate a matching
MethodChannelorEventChannelwith the same channel name and invoke methods (messenger.go)
🔧Why these technologies
- Go — Cross-platform desktop language with strong C interop; enables native platform integrations (file I/O, system APIs, plugins) without memory safety issues common in C++.
- GLFW (OpenGL Framework) — Lightweight, cross-platform window and input management (Windows/macOS/Linux); Flutter Embedding delegates rendering to platform-provided context.
- Flutter C Embedding API (embedder.h) — Official Flutter engine interface for embedding; replaces the need for Dart VM management—engine is precompiled and headless.
- Plugin Channel Architecture (Binary Messenger) — Decouples platform code from Dart; standardized codec (Standard/JSON/String) enables type-safe, async Dart↔Go communication.
⚖️Trade-offs already made
-
Headless Flutter engine (no built-in window manager)
- Why: Allows desktop-specific window behavior (multi-window, native decorations) unavailable in Flutter's mobile implementation.
- Consequence: GLFW event loop and window management must be manually orchestrated; more control but higher complexity than Flutter framework abstractions.
-
Go for platform integration vs. C/C++
- Why: Safety, GC, standard library, and faster iteration; simpler memory management in plugin code.
- Consequence: Slight runtime overhead vs. native code; cgo adds build complexity; some platform APIs require cgo wrappers.
-
Binary messaging over code generation
- Why: Minimal boilerplate; messages work for any Dart↔Go pair without build-time code gen.
- Consequence: No compile-time type checking between Dart and Go; errors surface at runtime; codec mismatches are runtime bugs.
-
Single-threaded engine (with task scheduling on engine thread)
- Why: Matches Flutter engine constraint; simplifies state management and rendering synchronization.
- Consequence: Long-running platform operations must offload to goroutines; results must marshal back to engine thread via Tasker.
🚫Non-goals (don't propose these)
- Does not provide hot reload / hot restart (compiled Go binary; no Dart VM mode).
- Does not support Flutter's web platform target.
- Does not implement web-style responsive layout abstractions (relies on Flutter framework for UI layout).
- Does not provide automatic native platform API bindings (developers must write cgo or use third-party Go libraries).
🪤Traps & gotchas
(1) FFI memory management: embedder_helper.c uses C memory; crashes can occur if Go doesn't correctly pin Flutter engine pointers across CGo calls. (2) Platform-specific GLFW initialization: GLFW has macOS-specific main-thread requirements (some GLFW calls must run on the main thread); check glfw.go for platform-specific goroutine handling. (3) Keyboard layout caching: internal/keyboard/ caches layouts at startup; changing system keyboard at runtime may not be reflected. (4) No official Flutter support: Flutter team doesn't officially support go-flutter; engine API breakage (between Flutter releases) can silently break this library.
🏗️Architecture
💡Concepts to learn
- Flutter Embedding API — go-flutter's entire purpose is to implement this C API contract (defined by flutter/flutter); understanding it explains why the FFI layer exists and what the engine expects from the platform layer
- CGo (C Foreign Function Interface) — go-flutter uses CGo extensively in
embedder/to call Flutter's C API; developers must understand memory management and goroutine safety at the C boundary - Event-loop pattern with task prioritization —
event-loop.goandinternal/priorityqueue/implement a deferred task scheduler to coordinate Flutter frame updates, input events, and plugin callbacks; critical for understanding async execution order - Message codec (binary serialization) — The plugin system in
plugin/binary-codec.godefines how Dart messages are marshalled to Go; required for plugin authors to extend functionality - Platform abstraction via build tags — go-flutter uses separate files (
keyboard_darwin.go,keyboard_pc.go) with Go build tags to isolate OS-specific code; essential pattern for cross-platform Go projects - OpenGL context management —
internal/opengl/andglfw.gomanage the OpenGL rendering context across platforms; understanding context threading is needed to debug rendering issues - Debouncing and task coalescing —
internal/debounce/is used to batch rapid events (e.g., window resize) into single Flutter frame updates; prevents excessive re-renders
🔗Related repos
flutter/flutter— Upstream Flutter framework; go-flutter implements the Embedding API defined herego-flutter-desktop/hover— Official CLI tool that wraps go-flutter; handles project scaffolding, hot-reload, and build orchestration for end usersgo-flutter-desktop/examples— Runnable example apps demonstrating go-flutter in practice (stocks app, navigation, plugins)go-gl/glfw— Upstream Go bindings for GLFW; go-flutter depends on this for windowing and input on all platformsflutter-rs/flutter-rs— Parallel Rust-based Flutter desktop embedding; shares same goal but different language/approach for comparison
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive unit tests for plugin/standard-message-codec.go and plugin/standard-method-codec.go
The plugin package has several codec implementations (standard-message-codec, standard-method-codec, binary-codec, json-method-codec) with existing test files, but standard-message-codec_test.go and standard-method-codec_test.go appear minimal based on the file list. These codecs are critical for Flutter platform channel communication. Expanding test coverage for edge cases (nil values, nested maps, type conversions, endianness) would improve reliability across Windows, MacOS, and Linux platforms.
- [ ] Expand plugin/standard-message-codec_test.go with tests for all supported types and edge cases
- [ ] Expand plugin/standard-method-codec_test.go with tests for method call encoding/decoding
- [ ] Add platform-specific tests in plugin/endian.go tests for big/little endian scenarios
- [ ] Verify test coverage reaches >80% for both codec files
Add GitHub Actions CI workflow for multi-platform builds (Windows, macOS, Linux)
The repo has .github/ISSUE_TEMPLATE configured but no visible CI workflows. For a cross-platform desktop framework, automated builds on Windows, macOS, and Linux are essential to catch platform-specific regressions. This would validate that go-flutter and its embedder still builds correctly on all three targets for each commit.
- [ ] Create .github/workflows/build.yml with matrix strategy for windows-latest, macos-latest, ubuntu-latest
- [ ] Add build steps for embedder/build.go and main package compilation
- [ ] Include go test -v ./... and go test ./plugin/... for all platforms
- [ ] Add optional step to build a sample Flutter app using go-flutter
Add integration tests and documentation for keyboard event handling across platforms
The repo has internal/keyboard/keyboard_darwin.go and internal/keyboard/keyboard_pc.go suggesting platform-specific keyboard handling, plus key-events.go at the root. However, there are no visible _test.go files for keyboard functionality. Adding tests would ensure key event translation, special key handling (Ctrl, Alt, Cmd), and text input work consistently across Windows, macOS, and Linux.
- [ ] Create key-events_test.go with tests for common key codes and modifier combinations
- [ ] Create internal/keyboard/keyboard_test.go for platform-agnostic keyboard logic
- [ ] Add platform-specific test files: internal/keyboard/keyboard_darwin_test.go and keyboard_pc_test.go
- [ ] Document expected key event behavior and platform differences in doc.go or README
🌿Good first issues
- Add unit tests for
isolate.go(manages Dart isolate lifecycle); currently untested but critical for plugin safety - Expand
plugin/basic-message-channel_test.goto cover error cases and codec edge cases missing from current coverage - Document the platform-specific keyboard scan code mapping in
internal/keyboard/keyboard_pc.govskeyboard_darwin.gowith a comparison table in the wiki
⭐Top contributors
Click to expand
Top contributors
- @renovate[bot] — 55 commits
- @pchampio — 29 commits
- @jld3103 — 4 commits
- @bltavares — 3 commits
- @GeertJohan — 2 commits
📝Recent commits
Click to expand
Recent commits
0fcb3ed— chore(deps): bump golang.org/x/text from 0.3.7 to 0.3.8 (#685) (dependabot[bot])fc98333— fix(deps): update module github.com/stretchr/testify to v1.8.1 (#679) (renovate[bot])531709d— fix(deps): update github.com/go-gl/glfw/v3.3/glfw digest to 55e207c (#673) (renovate[bot])f538461— Implement new method channels to avoid warnings (#669) (bltavares)34eaff6— Update glfw.go (pchampio)fb13e99— fix(deps): update github.com/go-gl/glfw/v3.3/glfw digest to 63cf1f4 (#664) (renovate[bot])81c0e5f— wrap glfw.PostEmptyEvent funcion to prevent panic (#579) (pchampio)729801f— fixes pixel ration on macos (#458) (pchampio)b1f6af9— Fix segfault when running with Flutter 3.0 (#663) (bltavares)f2fd800— Fix VersionUnavailable warning (#662) (bltavares)
🔒Security observations
The go-flutter project shows moderate security concerns, primarily driven by significantly outdated dependencies (Go 1.12 from 2019, GLFW from 2022) that lack modern security patches. The plugin system with multiple codec implementations presents potential deserialization risks requiring careful input validation. The project lacks formal security documentation and vulnerability disclosure policies. While the core architecture appears sound for a desktop Flutter framework, the outdated toolchain and dependencies represent the most critical remediation priority. Immediate action required: upgrade Go version and all dependencies to current stable releases, implement input validation in codec deserializers, and establish security policies.
- High · Outdated Go Version —
go.mod. The project specifies 'go 1.12' in go.mod, which was released in February 2019 and is far beyond its support lifecycle. This version lacks critical security patches and bug fixes for over 4 years. Fix: Update to a supported Go version (1.21 or later). Run 'go mod tidy' after updating the go directive to ensure compatibility. - High · Outdated GLFW Dependency —
go.mod. The GLFW dependency (github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220806181222-55e207c401ad) uses a pseudo-version from August 2022. This library handles window management and input, making it a critical attack surface. No recent updates suggests potential unpatched vulnerabilities. Fix: Update GLFW to the latest stable release. Check the upstream repository for security patches and update regularly. - Medium · Outdated Text Processing Library —
go.mod. The golang.org/x/text v0.3.8 dependency is outdated (released in 2022). Text processing libraries can have security implications for Unicode handling and character encoding attacks. Fix: Update golang.org/x/text to v0.13.0 or later using 'go get -u golang.org/x/text'. - Medium · Potential Input Validation Gaps in Plugin System —
plugin/ directory (particularly binary-codec.go, standard-message-codec.go, standard-method-codec.go). The codebase includes a plugin system with multiple codec implementations (binary-codec, json-method-codec, standard-message-codec). Deserialization of untrusted data without proper validation could lead to code execution or DoS attacks. Fix: Implement strict input validation for all codec deserialization. Use allowlists for acceptable types. Add fuzzing tests for codec implementations. Review unsafe deserialization patterns. - Medium · Missing Security Headers and Configuration Documentation —
Repository root. No visible security configuration files (SECURITY.md, security policy) or documented security best practices for users integrating this library. Fix: Create a SECURITY.md file documenting: (1) security policy, (2) vulnerability reporting process, (3) supported versions, (4) security best practices for integration. - Low · Hardcoded Test Assets —
mascot.png, stocks.jpg. The repository contains image files (mascot.png, stocks.jpg) which could potentially be used as attack vectors if served or embedded without proper handling. Fix: Ensure all assets are validated for integrity. Consider removing unnecessary test/demo files from production builds. Implement Content Security Policy if embedding is supported. - Low · Missing Dependency Checksums —
go.sum. The go.sum file is referenced but not shown. Ensure all transitive dependencies have verified checksums to prevent supply chain attacks. Fix: Verify go.sum contains checksums for all direct and transitive dependencies. Run 'go mod verify' to validate integrity. Use 'go get' with the '-u' flag to update dependencies securely.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.