Gio UI – Cross-platform GUI for Go
gioui.org
gioui.org
I find this to be the case with a depressing amount of Google code. They do not seem to have a culture which values not breaking their users' code.
Under language goals on their readme,
> We also have explicit non-goals for Carbon, notably including:
> * A stable application binary interface (ABI) for the entire language and library
> * Perfect backwards or forwards compatibility
There's also this blurb
> Our goals are focused on migration from one version of Carbon to the next rather than compatibility between them. This is rooted in our experience with evolving software over time more generally and a live-at-head model. Any transition, whether based on backward compatibility or a migration plan, will require some manual intervention despite our best efforts, due to Hyrum's Law, and so we should acknowledge that upgrades require active migrations.
https://github.com/carbon-language/carbon-lang/blob/trunk/do...
It sounds like the tool might make the specific case of flutter version upgrades easier though, if the tool works well, so that's nice. Most Google software doesn't come with similar tools.
V0.6 made your issues much less tricky.
Also there is a Material Design theme. And also light and dark.
Here is an excellent example of a gioui app that has light / dark with a custom theme.
https://github.com/chapar-rest/chapar
If you’re on MAC or Windows it’s just “ go run .”.
If you wanted text keening or text sweeping around an arc or RTL / LRT it can do it thanks to github.com/go-text/typesetting
Complex widgets for Calendar spinners or diagrams are out there on GitHub. An effort to bring these together is lacking, and I reckon that if all these things weee brought together it would definitely encourage more developers to dive in.
I checked it last a couple of weeks ago on my Android phone, and the result was so poor, I didn't check with other platforms.
You need to "opt in" to accessibility as a user, so in the beginning you have one button visible to screen readers, then your screen reader sees that single button, then you need to "double tap to activate" to make the rest of the document screen readable.
Then, somehow they made a funky screen reader voice read stuff out loud, not my system's voice, with all the awkwardness and issues that come with it.
On lists, it only recognized the items that were on screen, if you had to "scroll", you are out of luck.
Now, I'm sure someone will come and tell me how I'm holding it wrong, but somehow, every website and even Flutter on mobile got the accessibility mainly right by default, on Flutter web the accessibility situation is very poor.
Pax renders vector graphics to canvas layers and composites + clips + occludes native elements (web DOM nodes, iOS SwiftUI Text elements, etc.) in the same space to make a cohesive scene.
This solves accessibility and SEO, as well as dramatically reducing runtime size (~100KB WASM network footprint) because text rendering and atomic native UI elements don't need to be reinvented.
To my knowledge we're the only ones taking this particular approach so far (I'm on the team behind Pax) but it works quite well. Yes it was a lot of work.
[0] see pax.dev or check out a simple example: https://static.pax.dev/space-game/ ; source code for space game in examples on github https://www.github.com/paxengine/pax
Edit: unless you're doing something like flutter where your goal is to escape the DOM/JS paradigm in your code entirely, the fallback DOM nodes don't tend to be implemented too often.
The biggest reason is that canvas is for things with interactive/consumptive patterns that don't work well without vision.
Cute animations, 3d explorers and configurators, that sort of thing.
For users who rely on screen readers, they are better served by a DOM and interactive patterns that are not strictly recreating the canvas.
As an example, canvas based animations might have a fallback equivalent to an alt attribute on an image. A 3d explorer or configurator might be represented in DOM as a multi step form with verbose descriptions- no need to recreate drag and drop, camera controls, or anything else.
So, if you're not automating it like flutter is (and even if you are) what you really need to do is effectively an entirely alternate UX for screen reading users... Trying to get your product team to budget that is difficult despite being a Good Thing to do.
like gomobile, rust, typescript, etc ?
At some point i thought about using typescript for all business logic, as it seemed the most portable tech, but i realized there's no good way to have decent performance running javascript on iOS.
My company has built quite a few complex capacitor + Angular + Ionic apps and the performance on iOS is fine. Some even got featured by Apple, so that should be some indication that quality and performance was alright.
If you don't mind writing the UI native, sharing only business logic code, Kotlin is an option: https://kotlinlang.org/docs/multiplatform.html#kotlin-multip...
Kotlin also can do the UI if you use Compose: https://www.jetbrains.com/lp/compose-multiplatform/ ... however, iOS support is still in alpha, and Web is "experimental". If you're not willing to cope with that (expect to have to change your code as they develop the framework) then Flutter is the way as it's pretty stable already on all platforms.
If you already know Typescript and React, you may also consider React Native (but I can't attest to its performance on iOS or anywhere else): https://reactnative.dev/
https://sunrisetv.ch/de/vod/1/details/vod/crid:~~2F~~2Fog.li...
If you are going to create simple static homepage or interactive website, I don’t think Flutter is the right tool. Full fledged cross-platform applications is what Flutter is meant for.
Websites should still support accessability, I would even go as far to say it's a must. I just don't want people to mix up web-applications with websites. I've seen some people create a simple "websites" with Flutter which is totally pointless because it could just been created with html + css.
There is also a section in the Flutter docs that talks about accessability and how you can create accessable apps but I don't know how well it translates to Flutter web.
https://docs.flutter.dev/ui/accessibility-and-internationali...
I think all cross platform frameworks come with some kind of drawbacks and in most cases, learning about the underlying native platforms is unavoidable (like widgets, visionOS support etc).
So I wouldn’t worry too much or get into a FOMO decision block - I’d pick the one that feels most fun and fits your use case.
Look at Bluesky's client. The app performs decent on all supporting platforms and has single codebase.
It takes some careful usage of requestIdleCallback and avoiding doing work in the UI thread but it is not rocket science. You do have to avoid a lot of the going advice and bloated libraries / frameworks though.
Maybe better than the native you had before the rewrite lol
You had an app written natively in Objective-C or Swift, switched to single-threaded javascript React Native, and then your app ran faster?
The largest bottleneck historically was the bridge between obj-c and js (now superseded by JSI), and not the speed of the engine itself.
As for beginner - there's quite a bit of learning curve in terms of writing GUI applications themselves. Avalonia is kind of like WPF* but good(tm) and cross-platform. Uno platform is more like Xamarin and MAUI except in a better shape as of today. From technical perspective, Avalonia focuses on consistent rendering on all platforms with Skia while Uno tries to use native controls instead. I generally would not advise using Avalonia for mobile over Uno. In mobile case you might actually want to also give MAUI a try as, despite harsh criticism it has been receiving, it has improved significantly.
* either platform supports various declarative UI libraries (that are either part of their ecosystem or community-maintained), so you are not married to XAML thankfully.
Yes this would necessitate using Windows, an OS which I abhor, but as of now . In time I hope that will change with improvements in both JetBrains Ryder and Avalonia.
EDIT: never mind, I found it https://wails.io/docs/reference/cli#platforms
It doesn't seem to support Android, IOS or web.
Edit: looks like it’s for the server machine
Core logic can be shared. I use gomobile - mostly love it but 3mb overhead for runtime (so not for web). Kotlin multi platform looked good, but I found it missing pretty basic libraries (which exist for Kotlin Android, so no one is building multi platform equivalents). Rust with Mozilla’s language binding layer looks good but I haven’t tried it.
Canonical the company behind Ubuntu have said for some time now that all future app UIs of theirs would be done in Flutter starting with the very first thing people see the installation process.
Google just rewrote Google Earth’s UI in Flutter.
BMW are doing all of their in-car entertainment systems in Flutter.
I suppose if the alternative is “this only exists on one platform” or “this doesn’t exist at all” then maybe, but I find it a bit sad how keen we are as a profession to accept compromise and mediocrity to save developer time and expense.
I get it, but I don’t like it.
I’m saying the “gap” between Flutter and native in this case is much much much smaller than you seem to think it is. Quite a bit has changed there in the past year or two.
That isn’t my opinion but that was the result of many major and very competent teams coming to that conclusion and putting their money where their mouth is in order to substantiate the claim.
Google earth is fine, but doesn’t feel native. I think that’s fine for some apps (games mostly).
“Signed in as” toast wasn’t designed for iOS rounded bottom and “swipe up” bar leaving ugly stripe.
Sheet easing is off, and FPS stutter.
No swipe from left to go back support.
Non standard animations all over. Visible FPS issues (on newest HW)
I could go on and on. Feels like an android app running in an emulator.
And the frame drops and lack of system gestures, and lack of standard nav stack are major infractions on usability outside of design.
All the other alternatives are young frameworks that have not been battle tested and have weird quirks once you want anything relatively complicated.
Uno (https://platform.uno):
* open-source
* targets Android, iOS, Windows, Mac, and Linux (https://platform.uno/platforms/)
* C#
* automatically implements views and controls using each platform's native UI frameworks
* good IDE support: Visual Studio, VS Code, Rider (though you are not limited to those)
* Figma plugin for design collaboration
You get an amazing runtime with near native performance, access to value types, no type erasure, access to low-level operations when utmost performance is needed, very good cross platform and architecture SIMD support, three different great IDEs with two of them being cross-platform, AoT compilation that keeps getting better, WASM target with a good framework (Blazor), and so on.
Modern .NET is truly amazing and highly undervalued IMO.
An older article but puts it in context with Electron and how they differ https://www.codemag.com/Article/2111092/Blazor-Hybrid-Web-Ap...
Transpiles SwiftUI over to Kotlin/JetPack Compose. Native all the way down. Haven't used it in production, but the demos work and are native after going through XCode and Android Studio.
this made me lol. As somebody who does mobile dev professionally, the worst thing about iOS dev is how terrible XCode is compared to Android Studio.
There's no way I would want this.
I'm curious to know what others think about it.
When I need a web gui I use a gioui plugin system here: https://github.com/gioui-plugins/gio-plugins
WebViews work on web, desktop and mobile. It’s amazing. Deep linking also so you can send out a link to an email or Monike notification and the users app will open the gio gui in the right part of the gui.
It also has notifications and share extensions for all OS’s. It’s really a complete system. I can’t believe how much devs complain about things not being easy , when it’s all there. I definitely share the feelings that’s it’s so hard to support so many OS’s, but it’s the world we live in - plurality.
I like how I can do all of this with only golang , instead of switching between bits and bobs.
I always write my golang backend to work with gio and html.
So if I need SEO or video playback I can still do it in a web view and also keep googles seo happy for the gio web. I just use Hugo and feed it markdown. This is what Google SEO sees.
""" You might be thinking that it would be more usual to have an ops.Add(ColorOp{Color: red}) method instead of using op.ColorOp{Color: red}.Add(ops). It’s like this so that the Add method doesn’t have to take an interface-typed argument, which would often require an allocation to call. This is a key aspect of Gio’s “zero allocation” design. """
Why would there be an allocation? Of what? How are we saving it?
(The sibling comment written at the same time has a more detailed explanation of the 'fat' part.)
v := interfaceType(concreteTypeValue)
in Go, what you're actually doing on a lower level is: dataPtr := &concreteTypeValue
typePtr := typeData[concreteType]()
v := interfaceData{
data: dataPtr,
typ: typePtr,
}
The first line here is the allocation, since (at least, the way I recall the rule) in Go pointers never point to values on the stack, so concreteTypeValue must be allocated on the heap. The rule about pointers not pointing to the stack is there to make it easier for goroutine stacks to grow dynamically.This is only the case for pointers that the compiler can't prove not to escape:
>In the current compilers, if a variable has its address taken, that variable is a candidate for allocation on the heap. However, a basic escape analysis recognizes some cases when such variables will not live past the return from the function and can reside on the stack.
https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(filename...
https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(filename...
https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(filename...
When a function takes an interface type as an argument, and you pass a pure struct to it, it creates a wrapper around it (this is the allocation referred to in your quote) which is simply a pair of pointers, one being a type/vtable pointer and the other being a pointer to the struct's data.
Doing it this way allows code to do run-time type inference as well as allow interface extension implicitly (that is, implementing an interface is done by implementing its methods, and you don't have to explicitly type it like ByteReader extends Reader"). Since you only pay this cost if you use it, a lot of fast-path code will use structs exclusively if it can.
That's not exactly true. You get one concrete function for all generic pointer types for instance. Generic functions take a hidden paramater called a dictionnary, which is fairly similar to vtable, since it contains type information and pointers for all methods of the generic type that the function calls.
So methods on generic types are still performed through an indirection.
https://github.com/golang/proposal/blob/master/design/generi...
Others answered your question, but on a different subject: I find that weird to read. To me
op.ColorOp{Color: red}.Add(ops)
reads like “add ops to the result of op.ColorOp{Color: red}. I would name that function AddTo: op.ColorOp{Color: red}.AddTo(ops)
Still unconventional, but at least it messages that the function argument gets modified.Never again. Both Gio and Fyne lack tons of polish and features Flutter provides.
I built my core logic in Golang, and then decided to wrap it in an Android app. The GUI feels like it came out of 2003 with limited ways to fix it.
It's really hacked together, but it works.
The other ones (gotraceui, sointu) seem like they might be what you're talking about. I can see how they might be scary to someone used to "modern" UIs because their information density is much higher.
Personally I'd prefer if more people treated computers like a tool, and not some kind of art installation.
The cryptocurrency app looks good.
The gotraceui and sointu are actually decent. They are niche tools and look fine to me.
Gio is pure Go, Fyne is cgo.
Gio is immediate-mode, Fyne is retained-mode.
Flamewar starts here: I don't know why anyone would seriously choose an immediate mode GUI toolkit. When you receive an event, in immediate mode you have to recreate the whole world, then the toolkit has to then either rerender the world from scratch, or else diff the world against a clone on the heap, and rerender dirty regions. These kind of toolkits "advertise" hitting 60fps rerender speed as if that was impressive. In retained mode you merely don't respond to events you don't need to, the framebuffer doesn't need modification at all, and getting only 60fps updates would be embarassingly slow.
I understand the appeal of integrating e.g. Dear Imgui into your game's render loop if you're already re-rendering the whole viewport. But for desktop/mobile applications where the platform has a retained framebuffer, it seems like a deliberate deoptimisation.
I don't think the immediate-mode code is really more straightforward. Retained mode looks just as simple and only has that one same function with the same 5 basic SLoC, only
- (A) the ui library has to do less work at runtime, resulting in a more responsive app (as per parent comment);
- (B) i was forced to reorder the component instantiation. In immediate mode, the counter's label depends on the button's state, whereas in retained mode, the button update callback depends on the counter instead; and
- (C) immediate mode only needed one sprintf instead of two since the initialization and update logic is unified. You can combine them in retained mode with a helper function or closure (and in real-world apps you would); in this trivial case this increases the SLoC but in real-world cases it reduces it.
7% C is not pure Go.
Somehow, they managed to avoid requiring CGO on Windows so it's 100% Go there.
AFAIK it's not supporting Linux as a target platform. Correct me if I'm wrong.
* Android 5.0 (API 21) or higher is required.
* iOS 11 or higher is required
* macOS 10.15 or higher, using Mac Catalyst
* Windows 11 and Windows 10 version 1809 or higher
While Linux isn't supported, that is also not a typically significant market for GUI apps.
https://learn.microsoft.com/en-us/dotnet/maui/supported-plat...
The point of MAUI is to be a successor to Xamarin, for which the primary target are mobile devices, by far the largest market niche.
We have far better compilers and even languages for cross-platform work, so it should be easier by now.
* Hardware acceleration (in particular abstracting over metal, opengl, direct x, vulkan, etc)
* More complex layouts
* Animations
* Much hateful text rendering (e.g., right-to-left language support): https://faultlore.com/blah/text-hates-you/
* Accessibility
And if you want to emulate a native experience, getting keyboard shortcuts, focus behavior, and a billion other details correct is nearly impossible.
And by the way, today we have web engines that take care of most of these things for free and a huge pool of people who know how to build for them, so it’s often easier to leverage that capability than to essentially reinvent them.
And if people can contain their knee-jerk anti-electron reaction for a moment, I think that’s a pretty reasonable approach. I would really like to see something like an unlocked ChromeOS where the browser is the native interface, but unlike electron, we don’t ship a web browser with each app. Especially as webasm takes form, I could see this as a more compelling solution for cross-platform graphics with multi-language support than cross-language bindings or by trying to maintain a toolkit per language while trying to maintain some illusion of a consistent experience.
This has been a major blocker for my adoption of newer toolkits. I understand that it’s a result of mobile UI trends dominating the space, but that’s not helpful when I’m looking to build useful desktop apps, for which these widgets are crucial. Relying on third party libraries to fill these gaps (if they even exist) isn’t appealing either, adding yet another dependency to the pile and making a major part of my app subject to likely eventual abandonment by the library’s dev.
Try a Treeview. For Go UI kits you can count the treeviews on one hand.
We now have GPUs, mobile OSs with their own separate UI abstractions, the web, WebAssembly, etc. And people want write-once deploy anywhere.
You didn’t have any of that 30 years ago.
Now you can just choose 1 compiler such as GCC and it works the same on all platforms; even cross-compiling to a different CPU architecture works well and is far easier to setup (before it was a black art).
Java ?
My point was that making GUIs today should not be any easier than 30 years ago because we have a lot more complexity across different OSs, phones, and the web.
Update: I think I understand your comment. I think you latched on the very last past of my comment about "write-once deploy anywhere". I meant that as in people want to create their GUIs with one language and one toolkit and have them deployable across different stacks without having a custom version for each and every final target platform.
Yes, technically Java did deliver on that as a programming language and runtime. And I guess swing also deliver on that as a cross-platform UI toolkit. However, we don't see a lot of Java UI applications around, with the exception of a few big ones, so it's fair to say that in the end it didn't work out. Of course, Java as general purpose language and runtime is alive and doing well today.
"Ubiquitous Applications: Embedded Systems to Mainframe"
"To support a variety of platforms, Smalltalk uses a virtual machine approach to portability that allows applications to be easily migrated between platforms with little or no change to the code."
https://dl.acm.org/doi/pdf/10.1145/226239.226264
~
"… Smalltalk developers can work and deploy on Linux, Windows, AIX®, Sun Solaris, HP-UX, and OS/2®. You can also create applications to be deployed on the mainframe with VisualAge Smalltalk Server for OS/390 and z/OS."
https://www.ibm.com/docs/en/announcements/archive/ENUS202-10...
The HTML / CSS stack comes with significant ergonomic hurdles (layout / positioning / "creative fidelity") and performance hurdles (inescapable GC pauses, constant overhead of constraint-solving layout system, DOM overhead in general.)
Can we do better? IMO the future GUI lingua franca is deeply inspired by HTML and will stand on the shoulders of that giant, but will:
- Build to LLVM native apps as well as websites
- Include React-style templating as a foundational concern
- Be fundamentally designed for visual building (think of the ergonomic differences between Webflow & Figma — if "an HTML" existed that "a Figma" could read & write natively, building front-ends would take a quantum leap)
WebAssembly makes all of this possible while still extending the Web and browser ecosystems.Check out what do people work on using JavaFX: https://www.jfx-central.com/links
A robust cross-platform ecosystem for the underlying programming language is a necessary but insufficient piece of the puzzle.
Given the proliferation of HTML and WebView/web-tech wrappers (Electron, Tauri, React Native etc.) there are now enough "good enough" solutions that most organizations that would underwrite the in-house R&D of a new Neuron Data or Delphi or Flash simply don't feel the need.
When it does happen — say Google Flutter — it's almost statistically impossible to keep the hydra of VPs and biz folks aligned over the decades of ongoing investment it takes to mature & form community around this kind of technology — see the recent Flutter team layoffs at Google.
[0] speaking from my experience building a cross-platform GUI solution in Rust for several years now; see pax.dev
I wonder why the idea of widgetsets never really went anywhere: https://en.wikipedia.org/wiki/Widgetset
You could take a program that says you need a button in the window and it would make one for you on Qt, GTK or whatever else was supported, on any platform that you were compiling for.
We might as well have gone a step further: your programming language --> UI toolkit bindings for your language --> programming language independent GUI toolkit --> widgetsets for GTK, Qt, WPF, Win32 or whatever else --> executable for that specific platform
With that many layers of abstraction it's likely that we'd get the lowest common denominators for all functionality across platforms, but at least it would run better than web technology does, with the accessibility and platform look & feel you'd expect.
Of course, for whatever reason, seems like nobody wanted to undertake the borderline crazy effort of creating something like that, something foundational for others to build upon, so that's why we get super fragmented frameworks that are bound to specific languages nowadays and each of them have to reinvent a wheel a lot.
Either way, FreePascal/Lazarus were pretty amazing for RAD: https://en.wikipedia.org/wiki/Lazarus_(software) except that the community and ecosystem isn't exactly getting bigger.
[1]: https://forum.sublimetext.com/t/is-qt-framwork-is-use-used-i...
Horrible as it is, web browsers have managed to provide a common platform for user interface delivery. The browser wars have been fierce, and getting this foot between the door has cost several companies a lot of money. This technological monopoly is far from optimal, but I fear that we are stuck with it, as we are with QWERTY keyboards, USB thumb drives that must be unmounted manually, and the hopeless misery that is Bluetooth.
It is slightly more nuanced, IMHO. For enterprise software it actually makes sense to use browser based software, because it saves the IT department a lot of hassle with local installations and version management.
Ghidra is an example of a large, cross-platform application I use regularly that does use Swing. While it's a terrific reverse engineering tool, its UI leaves at least a little to be desired. Examples, not necessarily Swing's fault:
• Non-native text controls. This is particularly painful on macOS, where Ghidra text controls don't provide access to useful system services (Web search, dictionary look-up, translation, spell check, text-to-speech, sharing, … [it's an extensible list]) and don't honor custom key bindings defined in ~/Library/KeyBindings/DefaultKeyBinding.dict.
• Non-native file open/save and print/page setup dialog boxes.
• Non-native font selection with very limited options: can't choose weights other than standard or bold, can't choose widths (condensed, expanded, etc.), can't choose OpenType stylistic alternatives, etc.
• Per-window menu bars even on platforms where global menu bars are an option (KDE) or standard (macOS). As a consequence, no menu bar search or ability to remap menu item keyboard shortcuts on macOS.
• Other fiddly control differences. For example, on macOS, Option+Right/Left Arrow on a tree view node typically expands/collapses all descendants. On Ghidra, Option+Left Arrow collapses all, but Option+Right Arrow expands only children, with an additional level expanded for each subsequent Option+Right Arrow keypress. While Ghidra's behavior is arguably more useful, it's still inconsistent with platform norms.
• High DPI scaling inconsistent across platforms (works fine by default on macOS, requires fiddling on Windows and Linux).
• No integration with system-provided dark modes.
• No out-of-box support for installation as a native GUI application (macOS app, Windows Start Menu shortcut, Linux .desktop file). To be fair, with the arguable exception of Mac apps, this is out of scope for a GUI toolkit, and I suspect most people in Ghidra's target audience have no problem hand rolling a solution.
May I ask how are the apps made with Electron, Tauri, Wails or other WebView-based GUI toolkits performing on macOS in these regards?
I'm really enjoying the conversation around UI toolkits here. But I think we're all missing the elephant in the room and it's that we already had (have?) a superb toolkit and development model in the Visual Basic (later Windows Forms) era and nothing really manages to replicate that experience nowadays.
Microsoft destroyed it when they tried to push WPF and later shenanigans, only to realize it was a mistake years later.
I can't understand why we all, as a sector, agree about standards being a good thing, even if some are only de facto, and never agreed to maintain that really simple model.
I remember Mono tried to bring that to all platforms and everybody ignored/smashed them because "it's Microsoft".
Edit: typo
> Microsoft destroyed it when they tried to[…] only to realize it was a mistake years later.
Answer is right there.