Almost a year ago, I wrote about I was really trying hard to get into Desktop GUI apps development here, and someone suggested me to try Fyne.
Which I did, and wrote some first impressions about it in here.
I said there that the docs weren't really clear and some stuff felt a bit hacky to do, which it is totally still the case, but since then many things have changed.
I tested Avalonia and loved it.
I even wrote an article here on how I wrote in just under 2 months a multiplatform password manager application, which builds on github action and works on Mac/Win and Linux.
Muscurd-i is the app, and the codebase is quite huge to be such a simple application.
Few weeks ago instead I got my coding mojo back and hacked a bit around with rust, vlang and went back to study go once again (I wrote about it in here also).
This is the way I learn programming languages, I do side projects and since then I felt like I had to give fyne another go.
I finally completed the todo application which I was mentioning on the post before, and I was quite silly not to see why it didn't work before codebase is here.
Now it all works like a charm, and since I was on a roll I decided to give a try to move my password manager to Fyne and golang.
I did.
It took me only 2 days to reach feature parity and a couple of hours to setup a set of github action to build the binaries.
This is the repo.
This is what the latest release page looks like here.
The only thing I needed in here which was not supported out of the box with Fyne, was a routing system, which I was shocked to find so easy to create myself using their binding apis and a bit of creativity.
In this project basically the app creates a context to pass around the views, and maps the string representation of the route to a view which it is "mounted" in the main window content:
views: map[c.AppRoute]func() *fyne.Container{
c.Setup: func() *fyne.Container { return ui.GetSetupView(&ctx) },
c.Login: func() *fyne.Container { return ui.GetLoginView(&ctx) },
c.List: func() *fyne.Container { return ui.GetPasswordListView(&ctx) },
c.Details: func() *fyne.Container { return ui.GetPasswordDetailsView(&ctx) },
c.AddUpdate: func() *fyne.Container { return ui.GetPasswordAddUpdateView(&ctx) },
c.About: func() *fyne.Container { return ui.GetAboutView(&ctx) },
},
}
// Getting the view given a route
func (a *App) getView() *fyne.Container {
key := a.ctx.CurrentRoute()
if content, ok := a.views[key]; ok {
return content()
}
return a.views[c.Login]()
}
// reacting to the view change
a.ctx.OnRouteChange(func() {
val := a.ctx.CurrentRoute()
a.log(fmt.Sprintf("route state changed %s", val))
if val == c.Quit {
a.application.Quit()
}
a.setView()
})
// changing a route from any view then looks like this
aboutPageBtn := widget.NewButtonWithIcon("", theme.InfoIcon(), func() {
ctx.NavigateTo(c.About)
})
I was really proud of that, I even did a small change so I could pass a parameter around, for routes with ids for example.
I know it feels a bit web app
like but every other ui framework has routing systems, that is just the way the UX world goes I guess.
I also managed, as mentioned before, to setup a whole set of github actions to build the app binary for Win, Linux and Mac, and it took me literally 2 hours without almost no trial and error that usually you end up doing when you work with CI/CD tools.
The app looks like this on linux after you install it:
Everything just worked and I felt productive like I probably never have on Desktop GUI app development.
What is next? Maybe I will finally finish one of the projects I tried to do many times before, a Football Manager clone focused on the Players Transfer Market.
Let's see if this mojo streak lasts.
And what about you? Have you tried fyne? GO AND DO IT NOW!
Top comments (0)