Creating a Custom Theme

Applications are able to load custom themes that can apply small changes to the standard theme or provide a completely unique look. A theme will need to implement the functions of fyne.Theme interface, which is defined as follows:

type Theme interface {
	Color(ThemeColorName, ThemeVariant) color.Color
	Font(TextStyle) Resource
	Icon(ThemeIconName) Resource
	Size(ThemeSizeName) float32
}

To apply our theme changes we will first define a new type that that implements this interface.

Define your theme

We start by defining a new type that will be our theme, a simple empty struct will do:

type myTheme struct {}

It is a good idea to assert that we implement an interface so that compile errors are closer to the defining type.

var _ fyne.Theme = (*myTheme)(nil)

At this point you could see compile errors because we still need to implement the methods, we start with color.

Customising colors

The Color function defined in the Theme interface asks us to define a named color and also provides a hint for the variant that the user desires (for example theme.VariantLight or theme.VariantDark). In our theme we will return a custom background color - using a different value for light and dark.

// The color package has to be imported from "image/color".

func (m myTheme) Color(name fyne.ThemeColorName, variant fyne.ThemeVariant) color.Color {
	if name == theme.ColorNameBackground {
		if variant == theme.VariantLight {
			return color.White
		}
		return color.Black
	}

	return theme.DefaultTheme().Color(name, variant)
}

You will see the last line here references the theme.DefaultTheme() to look up standard values. This allows us to provide custom values and yet fall back to the standard theme when we don’t want to provide our own values.

Of course colors are simpler than resources, we look at that for custom icons.

Overriding default icons

Icons (and Fonts) use fyne.Resource as values instead of simple types like int (for size) or color.Color for colors. We can build our own resource using fyne.NewStaticResource, or you could pass in a value that was created using resource embedding.

func (m myTheme) Icon(name fyne.ThemeIconName) fyne.Resource {
	if name == theme.IconNameHome {
		return fyne.NewStaticResource("myHome", homeBytes)
	}
	
	return theme.DefaultTheme().Icon(name)
}

As above we return the default theme icon if we don’t want to provide a specific override.

Load the theme

Before we can load the theme you will need to implement the Size and Font methods as well. You can just use these empty implementations if you are happy to use the defaults.

func (m myTheme) Font(style fyne.TextStyle) fyne.Resource {
	return theme.DefaultTheme().Font(style)
}

func (m myTheme) Size(name fyne.ThemeSizeName) float32 {
	return theme.DefaultTheme().Size(name)
}

To set the theme for your app you will need to add the following line of code:

app.Settings().SetTheme(&myTheme{})

With these changes you can apply your own style, to make small tweaks or provide a completely custom looking application!