Journaling app
Distribute your Fyne journaling app
In part two of this guide, Andrew Williams completes the building of a daily journal app using Fyne and prepares it for distribution.
FYNE
Credit: https://fyne.io
Part Two!
Don’t miss next issue, subscribe on page 16!
OUR EXPERT
Andrew Williams is a software engineer and entrepreneur. He has been a core developer in Enlightenment, EFL and Maven, and founded the Fyne toolkit.
Last month, you met Fyne, a popular toolkit for building graphical applications with Go. It aims to make it easy to build native apps that work everywhere. Apps built with Fyne can be distributed to multiple operating systems, including Linux, BSD, Android, iOS, Mac and Windows. Last time, we stepped through the setup and Go source code required to build a simple journal app and run it on our computer.
In this part, we pick up with an important omission: storing the user data so it’s not lost each time the app closes. We also look at using data binding for simpler code, and some additions to the feature set. Lastly, we explore how to package a Fyne app for each platform and even upload them to app stores and marketplaces.
Storing user data
Fyne comes with some standard storage APIs, and the most useful is Preferences. This is a place to store user data between app runs. Before we can use this, we need to update our app setup so it has a unique identifier (ID). The unique ID is commonly used for mobile applications or software distributed through an app store, so that two apps can’t overwrite each other’s data. The identifier is typically in reverse DNS notation, such as com.example.myapp. If you do not have a domain name for your software, it is common to use the location of the source code – for example com. github.username.myapp. In our code, we will replace app.New() with a new call that passes in the unique ID: app.NewWithID(“com.example.journal”)
With that in place, we can remove the entries map of data from the code that we put together last month and replace the read and write of the data with preference calls. The instance of Preferences for your application is available from App.Preferences(), which returns an implementation of the fyne.Preferences API. This provides getters and setters for many data types, such as String, Int and Bool, as well as lists of these types (such as a Go slice).
STOR AGE AND DATA BINDING
Once we’ve moved to Fyne Preferences for our storage and data binding to communicate changes, the code is even more concise than it was at the end of part one – here it is in its completeness: package main
import ( “time”
)
“fyne.io/fyne/v2” “fyne.io/fyne/v2/app” “fyne.io/fyne/v2/container” “fyne.io/fyne/v2/data/binding” “fyne.io/fyne/v2/theme” “fyne.io/fyne/v2/widget”
const dateFormat = “2 Jan 2006” func main() { a := app.NewWithID(“com.example. journal”) w := a.NewWindow(“My Journal”) var date time.Time entry := widget.NewMultiLineEntry() title := widget.NewLabel(“Today”) title.Alignment = fyne.TextAlignCenter
setDate := func(d time.Time) { date = d dateStr := date.Format(dateFormat) title.SetText(dateStr) entry.Bind(binding.
BindPreferenceString(dateStr, a.Preferences())) entry.Validator = nil
} setDate(time.Now()) prev := widget.NewButtonWithIcon (“”, theme.NavigateBackIcon(), func() { setDate(date.Add(time.Hour * -24))
}) next := widget.NewButtonWithIcon(“”, theme.NavigateNextIcon(), func() { setDate(date.Add(time.Hour * 24))
}) bar := container.NewBorder(nil, nil, prev, next, title)
w.SetContent(container.
NewBorder(bar, nil, nil, nil, entry)) w.Resize(fyne.NewSize(200, 180)) w.ShowAndRun()
}
As you can see, not a lot has changed, but the user data is now saved any time that it changes. In addition, we were able to remove the OnChanged callback to the Entry, because data binding takes care of this for us.