They both owner-draw every widget themselves (not using platform native widgets).
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 recently wrote a little document previewer in Fyne. It's a great experience and super easy to build simple GUIs, however Fyne struggled with a few pages of text (slow scrolling and resizing). I'll have to try Gio.
One positive side of immediate mode is that the code becomes a lot more straight forward. You only see that loop function, so it's very nice if you need a POC, or if it's going to be ran on embedded.
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.