How to Create a Simple Desktop App with Golang and Fyne

We’ll create a Golang desktop app for adding, updating, deleting, and viewing text items in a list using the Fyne GUI toolkit

In this article, I will go over a little project that I made to explore some of the possibilities of the Fyne GUI toolkit. This collection of packages implements desktop GUI functionality for Golang on multiple platforms.

I chose to create a data-entry app for this project since this seems to be very useful for many people. Currently, it is very simple, but you can easily expand it on.

First, I will discuss why I chose the Fyne toolkit to create a desktop app. Afterward, I will add a quick note on the installation process.

Then, I will discuss various parts of the program’s code that I wrote for data entry. Here you’ll find some helpful notes of things I discovered along the way when creating the project.

To close, I will show you how to run the program and what it looks like and discuss how we can improve it.

My reason for using Fyne is pretty banal. Over the years, I have played around with many software libraries/packages for various programming languages. These languages ​​include C++, Python, JavaScript, etc.

Therefore, I can tell you that if something works straight out of the box on a Windows PC after installing it, then I’m pleased. This was the case with Fyne. I installed it (and GCC, but more on that later), and I ran a few of the official examples. It worked like a charm.

In addition to the apparent ease of use and stability, it seems — but I haven’t tested it yet — that the Fyne GUI toolkit allows being compiled for most popular platforms. These platforms include Windows, macOS X, Linux, Raspberry Pi, BSD, Android, and iOS. That is a very decent coverage.

Finally, the Fyne packages contain all the typical widgets you’d expect to find in a desktop GUI toolkit.

Apart from installing the Fyne toolkit, you’ll need to install a C Compiler (GCC) to compile code that uses it. Given that I’m using a Windows PC for programming in Golang, I chose to install MSYS2 as is recommended by the official documentation. GCC is included in MSYS2. Overall, the installation process is pretty straightforward, so I don’t need to explain that here.

There is a tiny thing I’d like to add, though. You may want to add the location of the gcc.exe binary to your Windows PATH. Like this, you can just run the Golang code from your Windows Terminal as you are used to. Otherwise, you’d have to run the Go compiler from the MSYS2 terminal. The latter is also possible but not particularly convenient.

Below you can find the complete code of the data-entry desktop app.

In what follows, I will discuss the code in small snippets.

Importing the required packages

In lines 3–16, we import all the necessary libraries. These include:

  • encoding/json for encoding and decoding JSON strings.
  • fmt for printing messages to the terminal. I also used this for debugging my app while building it.
  • io/ioutil for reading and writing files.
  • Various Fyne packages

Loading data from a JSON file

In line 4, we read the file called data.json with ioutil.ReadFile() . This function returns a slice of bytes ([]byte) and an error . In this example, we skip the error and just put an underscore.

Should the file data.jsonnot exist, then input is an empty slice.

In line 5, we declare a slice of string ([]string) called datato hold each of the strings that we have added using our app.

In line 6, we decode the JSON byte slice from input and put the contents into data as a slice of strings.

In line 9, the loadJsonData() function returns either an empty slice or a slice containing the strings saved by our app before.

Saving data to a JSON file

In line 1, the function saveJsonData() receives a variable called data which is of the type binding.StringList . This type is a particular type from the Fyne package, used to keep and update list items. You can read more about it here.

In line 3, we get the slice of strings from the data StringList. Again we skip the errors and put an underscore.

In line 4, we encode the slice of strings as JSON data.

In line 5, we use ioutil.WriteFile() to the JSON data to the file called data.json . Note that the third argument of WriteFile() is a number. This number corresponds to the numeric notice of the Unix file permission code — in this case 0644.

Setting up the app

The above code appears at the beginning of the main() function.

In line 1, we create a new Fyne app.

In line 2, we then create a new window within the Fyne app.

In line 4, we load the JSON data into the variable called loadedDatausing the function described above.

In lines 6 and 7, we create a variable data of the type binding.StringList and set its contents to loadedData.

In line 9, we defer saving the JSON data, using the function previously described, with the defer keyword. By deferring this function, we ensure that it will be called at the end of the main() function even if some errors should happen during execution.

Listing the data

We’ll use a widget called ListData to list the strings from the StringList called data.

This widget can be created using the function widget.NewListWithData() . The way this widget works is that it is made to be bound to the StringList called data . When data is updated, then the list of strings in the widget is also automatically updated.

Updating and deleting the data

The items in the list of strings can be selected. In our case, this boils down to a click, as the selected item is immediately unselected (in line 2).

In line 3, we get the value of the item in the data given its id and using the function getValue() .

In line 4, a new window is created for holding the following widgets:

  • In lines 6 and 7, a widget for text string entry. The text of the widget is set to the item’s current value.
  • In lines 9–12, a button widget that, when clicked, will update the selected list item. Updating the item in the list is easy with the SetValue() function.
  • In lines 14–16, a button widget that, when clicked, will cancel the action and close the window.
  • In lines 18–31, a button widget that, when clicked, will delete the selected item from the string list. Deleting an item is a bit more involved than updating it. The idea is that we copy all data to newDataexcept the deleted item onto a new list and then set the data to the new data.

All the widgets are added to the window in line 33.

In lines 34–36, we give the window a size of 400×200 pixels and center the window on the screen to avoid it opening in the upper left corner. Finally, we make sure to show the window.

Adding the data

We can add data items to the list by clicking the “Add” button at the bottom of the app. Adding the item is implemented in the function that is called when the button is clicked.

In line 1, the button widget is created.

Then in line 2, a window is created. This window will, in its turn, contain three widgets:

  • In line 4, a widget for text string entry.
  • In lines 6–9, a button widget that, when clicked, will add a new list item. This is as simple as applying the Append() function to the data list. When the item has been added, the window is closed.
  • In lines 11–13, a button widget that, when clicked, will cancel the action and close the window.

In line 15, all the above widgets are added to the window using a VBoxLayout. This means that the widgets are stacked vertically.

In lines 16–18, the window size is set to 400×200 pixels, the window is centered on the screen, and then it is shown.

Set the contents of the main window

In line 6, we add the add and exit buttons to a vertical layout, which in its turn is added to the bottom of a border layout. The list widget is added to the center.

In line 7, we resize the window to 400×600 pixels.

In line 8, we set the main window as the “master” window. When we close the main window, we close all windows.

In line 9, the window is centered on the screen.

In line 10, the window is shown, and the app is run with ShowAndRun() . This function can only be used once in the program, as we can only run the app one at a time. This is why the other windows are shown using Show() only.

Leave a Comment