---
title: "Знакомство с Fyne: GUI на Go"
description: "Что такое Fyne: GUI-тулкит на чистом Go — архитектура, CanvasObject и виджеты, OpenGL-рендер, платформы от Windows до Android и сравнение с Walk и Gio."
course: "Fyne: десктопные и мобильные GUI на Go"
url: "https://golangmaster.ru/courses/fyne/lessons/01-znakomstvo/"
published: "2026-09-19"
updated: "2026-09-21"
minutes: 15
---

# Знакомство с Fyne: GUI на Go

*Курс «Fyne: десктопные и мобильные GUI на Go», урок 1 из 25. ~15 минут. Обновлено 21.09.2026. Источник: https://golangmaster.ru/courses/fyne/lessons/01-znakomstvo/*

Что такое Fyne и почему именно он, если в Go захотелось окна с кнопками? Fyne — самый заметный GUI-тулкит языка: виджеты, компоновка и тема написаны на чистом Go, рендер идёт через OpenGL, а одна кодовая база собирается под Windows, macOS, Linux, iOS, Android и даже браузер. Дебютный урок курса: архитектура тулкита, модель CanvasObject и виджетов, платформы сборки и честное сравнение с Walk и Gio.

Начнём с ситуации, в которой рано или поздно оказывается каждый Go-разработчик. Вы написали утилиту: логика аккуратная, логи красивые, конфиг читается из файла — и всё хорошо, пока заказчик не говорит: а можно не в терминале, а окошком с кнопкой? В экосистеме Python или C# на этот вопрос есть ответ по умолчанию. В Go — нет: GUI считается известным слабым местом языка, стандартная библиотека не содержит ничего интерфейсного, и дальше начинается выбор из доброго десятка библиотек. Вот здесь и стоит разобраться, что такое Fyne и почему он стал первым ответом на запрос «GUI на Go». Первая мажорная версия 1.0 вышла в марте 2019 года, и с тех пор проект вырос в самый заметный тулкит ниши: крупная команда, каталог приложений на fyne.io, собственная конференция. Курс, который вы начали, — про него: 25 уроков от первого окна до менеджера задач с темами, привязкой данных и упаковкой в сторы.

### Что такое Fyne: GUI-тулкит на чистом Go

Если вычесть маркетинг, что такое Fyne технически? Это тулкит и app-API, написанные на Go: сами виджеты, контейнеры, компоновщики и тема — обычный Go-код, который можно открыть и читать, а не обёртка над Qt или GTK, как в предыдущих поколениях Go-GUI. Слово «чистый» требует одной честной оговорки: для сборки нужен C-компилятор и системные инструменты разработки, потому что вывод на экран идёт через cgo-привязки к OpenGL и библиотеке GLFW. Но весь код, с которым вы работаете как автор приложения, — Go. README формулирует назначение одной строкой: «easy-to-use UI toolkit and app API» для десктопа и мобильных из единой кодовой базы. Вторая мажорная версия с обновлённым API вышла в январе 2021-го, актуальная ветка — v2.8. Визуальный язык вдохновлён Material Design, с оговоркой самой документации: базовая плотность у Fyne другая, иконка высотой 18 соответствует 24 в материальном Android-приложении. Важное следствие подхода «рисуем всё сами»: на Windows, macOS и Linux приложение выглядит одинаково — и это не нативный вид каждой системы, а единая тема тулкита. Кому-то это минус, кому-то наоборот: дизайн перестаёт зависеть от версии ОС у пользователя.

### Первая программа — hello world из README

```go
package main

import (
        "fyne.io/fyne/v2/app"
        "fyne.io/fyne/v2/container"
        "fyne.io/fyne/v2/widget"
)

func main() {
        // приложение: под ним сидит драйвер платформы
        myApp := app.New()

        // окно с заголовком
        window := myApp.NewWindow("Привет")

        // метка и кнопка - обычные виджеты
        hello := widget.NewLabel("Привет, Fyne!")
        window.SetContent(container.NewVBox(
                hello,
                widget.NewButton("Нажми меня", func() {
                        hello.SetText("Заработало!")
                }),
        ))

        // показать окно и запустить цикл событий
        window.ShowAndRun()
}
```

Импорт-пути fyne.io/fyne/v2/... — первое, к чему привыкаешь: корневой пакет задаёт типы, подпакеты app, container и widget — всё остальное. Программа из шести содержательных строк открывает окно с меткой и кнопкой, а клик меняет текст метки. Пока просто прочитайте её как текст; во втором уроке установим тулкит и запустим это локально.

### Каркас программы: приложение, окно и цикл событий

Разберём каркас по слоям. app.New() создаёт приложение: за ним стоит драйвер платформы, который умеет открывать окна, собирать ввод и рисовать; для мобильных и настроек есть вариант app.NewWithID с идентификатором. myApp.NewWindow("Заголовок") — окно, а window.SetContent(...) задаёт корневой объект содержимого. Самая важная строка — последняя: ShowAndRun() — сокращение для window.Show() и myApp.Run() подряд. Run запускает цикл событий, run loop: он принимает клики и нажатия клавиш, перерисовывает экран и не даёт программе завершиться. Документация формулирует два жёстких правила. Первое: Run вызывается ровно один раз — цикл в приложении один, повторный вызов приводит к ошибкам. Второе: управление после Run вы получите только когда приложение закроется, поэтому функции, написанные после него, выполнятся в самом конце жизни программы. App.Quit() прерывает работу, но только в десктопных средах — на мобильных такой возможности нет. А если Run никто не вызвал, setup-код доходит до конца main, и процесс завершается — об этом отдельное задание ниже.

### Что напечатается и когда?

```go
package main

import (
        "fmt"

        "fyne.io/fyne/v2/app"
        "fyne.io/fyne/v2/widget"
)

func main() {
        myApp := app.New()
        window := myApp.NewWindow("Завершение")
        window.SetContent(widget.NewLabel("Работаем..."))
        window.ShowAndRun()
        tidyUp()
}

func tidyUp() {
        fmt.Println("Приложение закрыто")
}
```

**Что выведет программа?**

- «Приложение закрыто» напечатается сразу после ShowAndRun, ещё до открытия окна
- ✅ «Приложение закрыто» напечатается после того, как пользователь закроет окно
- Программа зависнет навсегда и ничего не напечатает
- Программа завершится аварийно: после Run нельзя вызывать свои функции

Run блокирует main на всё время работы приложения, поэтому tidyUp дожидается выхода. В документации Fyne это ровно тот паттерн, которым показывают поведение цикла: функция после Run выполняется при завершении. Вариант «никогда» близок по ощущению, но неверен: управление возвращается — просто поздно.

### CanvasObject и виджеты: анатомия интерфейса

У каждого окна есть canvas — область рисования, доступ даёт window.Canvas(). Всё, что можно нарисовать, — от прямоугольника до кнопки — является реализацией интерфейса fyne.CanvasObject: у объекта есть размер, позиция, видимость и метод Refresh() для перерисовки. Примитивы — canvas.Rectangle, canvas.Circle, canvas.Line, canvas.Text, canvas.Image — живут в пакете canvas, и из них, по формулировке документации, собран весь тулкит. Виджеты — особый вид CanvasObject: fyne.Widget добавляет интерактивное поведение, а внешний вид вынесен в отдельный объект — WidgetRenderer. Разделение сознательное. API виджета описывает поведение и состояние (SetText, SetEnabled), рендер его не касается; когда состояние меняется, вызывается Refresh, и рендерер перерисовывает элемент по новому состоянию. Из этого следует неожиданно приятное свойство: приложение можно прогонять юнит-тестами в памяти, вообще не отрисовывая его на экран — для этого в пакете test есть SoftwarePainter и безоконный canvas. Widget и WidgetRenderer — интерфейсы, и на них держится вся система кастомных виджетов: как такие «контракты» проектируются на чистом Go, подробно разбирается в уроке 10-interfejsy продвинутого курса, а свой виджет мы соберём в восьмом уроке этого курса.

### Компоновка: MinSize и контейнеры

Виджеты в Fyne не решают сами, где им стоять. Они сообщают минимальный размер — MinSize — а расстановкой занимаются контейнеры с компоновками. container.NewVBox из hello world складывает элементы в колонку: каждый получает ширину самого широкого соседа и высоту не меньше своей минимальной; горизонтальный HBox работает так же, но в строку и с общей высотой. Дальше по списку пакета layout: Border с зонами сверху, слева, снизу и справа, где всё остальное занимает центр; Center, ставящий каждый элемент на его минимум; Form парами «подпись — поле»; Grid и GridWrap для плиток; Padded и Stack. Отступы берутся из темы: стандартный padding равен 4, внутренний отступ виджета — 8, и это не случайные пиксели, а константы темы, которые можно переопределить. Компоновки вкладываются друг в друга: заголовок в HBox, боковая панель в VBox, содержимое в Grid — и всё это внутри Border. Полный разбор Box-контейнеров и их ловушек — четвёртый урок курса.

**Вопрос:** Что в Fyne является общим предком и примитивов рисования, и кнопок?

- fyne.Widget
- ✅ fyne.CanvasObject
- fyne.Driver
- fyne.Theme

Всё нарисованное — реализация fyne.CanvasObject. Виджет — особый CanvasObject с поведением и WidgetRenderer, примитивы пакета canvas — простые CanvasObject. Driver и Theme к иерархии рисования не относятся.

### Запуск без цикла событий

```go
package main

import (
        "fyne.io/fyne/v2/app"
        "fyne.io/fyne/v2/widget"
)

func main() {
        myApp := app.New()
        window := myApp.NewWindow("Мигнёт?")
        window.SetContent(widget.NewLabel("Видите меня?"))
        window.Show()
        // ShowAndRun забыли вызвать
}
```

**Что выведет программа?**

- Окно останется висеть, но приложение перестанет отвечать на клики
- Окно покажется и всё будет работать — Show достаточно
- ✅ main завершится, процесс закончится — устойчивого окна не будет
- Компилятор откажется собирать программу без вызова Run

Show лишь помечает окно видимым; цикл событий никто не запустил, main дошёл до конца — и процесс закрылся. Компилятор молчит: это логическая ошибка, а не синтаксическая. Отсюда железное правило — ShowAndRun или Run в конце main.

### Рендеринг: OpenGL, программная растеризация и главный поток

Кто именно рисует пиксели? На десктопе Fyne открывает окна через GLFW и рисует через OpenGL: в go.mod тулкита прописаны привязки go-gl/gl и go-gl/glfw, а в internal/painter/gl живут шейдеры, которыми прямоугольники, скругления и текст превращаются в графику видеокарты. В сборках под WebAssembly те же вызовы уходят в gl-js — то есть в браузерный WebGL. Второй путь — программная растеризация: software-пейтер из internal/painter/software рисует canvas в обычный image.Image в памяти, без участия видеокарты. Медленнее, но именно на нём построены тесты и headless-среды: пакет test подсовывает приложению SoftwarePainter, а драйвер software умеет рисовать в WindowlessCanvas — рендер без окна вообще. Третье, что полезно знать заранее: GUI живёт в главном потоке операционной системы. Драйвер закрепляет за собой главный поток через runtime.LockOSThread и проводит все операции рисования через очередь; из собственной горутины менять интерфейс напрямую небезопасно — с версии 2.6.0 для этого есть fyne.Do, который ставит вашу функцию в очередь главного потока. Обновление GUI из горутин — тема двадцатого урока.

### Платформы: одна кодовая база, пять сред исполнения

Главный аргумент за Fyne — кроссплатформенность. Десктоп: Windows, macOS, Linux — обычные go run и go build; нужен установленный C-компилятор, а первая сборка на Windows может занять до десяти минут, дальше кеш. Мобильные: Android и iOS собираются командой fyne package -os android -appID my.domain.app или fyne package -os ios; сборка под iOS ведётся с macOS, а -os iossimulator даёт пакет для эмулятора. Готовый пакет ставится утилитой fyne install -os android или платформенными средствами. Посмотреть, как приложение будет выглядеть на телефоне, можно и без пакования: go run -tags mobile запускает мобильную симуляцию. Пятая среда — браузер: с версии 2.5.0 web-драйвер полностью поддерживается, fyne serve поднимает локальный сервер с WebAssembly-сборкой, fyne package -os web готовит файлы для хостинга; ограничения описаны честно — файловые диалоги и хранение документов пока не работают. Требования к среде разработки скромные: Go 1.22 или новее, C-компилятор, системные инструменты — всё перечислено в README.

### Как собирается одна и та же программа

| Среда | Команда | Примечание |
| --- | --- | --- |
| Windows, macOS, Linux | go run . или go build | нужен C-компилятор; первая сборка до 10 минут |
| Android | fyne package -os android -appID … | установка на устройство: fyne install -os android |
| iOS | fyne package -os ios -appID … | сборка с macOS; iossimulator — для эмулятора |
| Мобильная симуляция | go run -tags mobile | вид телефона без пакования |
| Web-браузер | fyne serve / fyne package -os web | с v2.5.0; без файловых диалогов и хранилища документов |

**Вопрос:** Сколько раз в приложении Fyne можно запускать цикл событий (Run)?

- ✅ Ровно один — повторный вызов приводит к ошибкам
- Два — по циклу на каждое окно
- Сколько угодно: каждый Run открывает свой поток событий
- Ни разу — Run вызывается только внутри ShowAndRun

Цикл событий один на приложение: документация прямо просит вызывать Run единожды, иначе — ошибки. Несколько окон живут в одном цикле, отдельного цикла на окно нет.

### Сравнение с Walk: одноплатформенный ветеран

Перебирая gui go фреймворки, вы почти наверняка наткнётесь на Walk — «Windows Application Library Kit», один из старожилов Go-ниши. Его характер противоположен Fyne: Walk — обёртка над Win32 API и стандартными элементами управления Windows. Отсюда плюс: интерфейс нативный для этой системы, кнопки и списки — настоящие контролы ОС, а не нарисованные поверх. Отсюда и жёсткий потолок: macOS, Linux, мобильные — никак; приложение, собранное с Walk, навсегда привязано к Windows и её API. Стиль кода другой: в Walk принято декларативное описание окна структурами — MainWindow, VBox, TextEdit с AssignTo — что по духу ближе к описаниям интерфейса в других экосистемах, чем к Go-идиомам. Если утилита точно всегда будет под Windows и нужен нативный вид — Walk до сих пор рабочий выбор. Но как только появляется «а теперь пусть работает на маке», начинается переписывание с нуля: Fyne собирает ту же программу на все три десктопные системы из одного кода, и в этом вся цена вопроса.

### Сравнение с Gio: ниже уровень, больше контроля

Gio — второй серьёзный сосед, и сравнивать его честнее всего по уровню абстракции. Gio — immediate mode GUI: программа на каждом кадре заново описывает интерфейс, как это делают игровые движки; нет дерева объектов, живущего между кадрами, есть операция «нарисуй состояние прямо сейчас». README определяет проект одной строкой: «Immediate mode GUI programs in Go» — для Android, iOS, macOS, Linux, FreeBSD, OpenBSD, Windows и WebAssembly, причём веб помечен как экспериментальный. Последствия: Gio даёт предсказуемость и полный контроль отрисовки — ценой ручной работы. Готовых виджетов мало, тема — ваша забота, путь от идеи до формы с полями заметно длиннее, чем в Fyne. Показательна и версия: Gio до сих пор живёт в нулевой мажорной — API ещё может меняться. Fyne — retained-подход: дерево CanvasObject живёт между кадрами, Refresh перерисовывает изменившееся, виджеты и тема идут в комплекте. Для стандартных форм и панелей управления Fyne быстрее в разработке; Gio выбирают, когда интерфейс нестандартный и нужен контроль каждого пикселя. Никто не «лучше» — они про разную цену контроля.

### Fyne, Walk и Gio рядом

|  | Fyne | Walk | Gio |
| --- | --- | --- | --- |
| Платформы | Windows, macOS, Linux, iOS, Android, web | только Windows | десктопы, iOS, Android, WASM (экспериментально) |
| Модель UI | retained: дерево CanvasObject и Refresh | обёртка над Win32-контролами | immediate mode: кадр собирается каждый раз |
| Виджеты и тема | большой набор, единая тема в комплекте | нативные контролы Windows | минимум готового, тема — ваша забота |
| Порог входа | самый низкий из трёх | низкий, если знаком с Win32 | высокий: ниже уровень абстракции |
| Выбор, если | кроссплатформенное GUI из одной кодовой базы | утилита навсегда под Windows | нужен полный контроль отрисовки |

**Вопрос:** Приложение должно работать на Windows, macOS и Android из одной кодовой базы. Какой тулкит отпадает сразу?

- Gio
- ✅ Walk
- Fyne
- Подходит любой из трёх

Walk работает только под Windows. Gio и Fyne собираются под все три среды — разница между ними в уровне абстракции и наборе готовых виджетов, а не в платформах.

### Когда Fyne уместен, а когда нет

Fyne — правильный выбор для внутренних инструментов, настольных утилит, панелей управления, клиентов к собственному API и учебных проектов: везде, где нужна форма с полями, кнопками и списками на трёх десктопных системах, а язык команды — Go. README подчёркивает переносимость: приложения работают без предустановленных библиотек — пользователю не нужны ни рантайм, ни пакеты зависимостей, только ваш бинарник. Честный список, где Fyne не лучший вариант. Игры: тулкит не для этого, в Go для игр есть отдельные инструменты вроде Ebiten. Тяжёлая 3D-графика и нестандартные медиаплееры — возможны, но вы окажетесь на низком уровне canvas раньше, чем рассчитывали. Пиксель-в-пиксель нативный вид каждой ОС — не про Fyne: тема единая, «кнопка как в Windows 11» из коробки не получится. Кросс-компиляция с машины на машину усложняется cgo: собирать под чужую платформу удобнее утилитой fyne-cross с контейнерами — это тема двадцать третьего урока. И веб: работает, но ограничения web-драйвера заметны — для сайта берите обычный фронтенд.

> **Почему код этого курса не запускается в браузере.** GUI в браузерной песочнице курса не исполняется: графическому тулкиту нужна локальная графическая среда, а не онлайн-редактор. Поэтому в уроках курса нет runner-ов и кнопок запуска: код — рабочий образец с построчным разбором, а проверяют вас quiz, predict, fill, order и флешкарты. Установите Go и Fyne по второму уроку — и каждый пример запустится локально.

### Соберите каркас формы

Восстановите четыре пропущенных вызова каркаса: приложение, окно, содержимое, запуск.

```go
package main

import (
        "fyne.io/fyne/v2/app"
        "fyne.io/fyne/v2/container"
        "fyne.io/fyne/v2/widget"
)

func main() {
        myApp := ___()                // приложение с драйвером платформы
        window := myApp.___("Форма")  // окно с заголовком
        window.___(container.NewVBox( // содержимое окна
                widget.NewLabel("Имя:"),
                widget.NewEntry(),
        ))
        ___() // показать окно и запустить цикл событий
}
```

Подсказка: Все четыре глагола из hello world: создание приложения, окно, контент, запуск.

Правильные ответы в пропусках: `app.New`, `NewWindow`, `SetContent`, `window.ShowAndRun`

### Каркас программы по строкам

Соберите минимальную Fyne-программу: от объявления пакета до запуска цикла событий.

Правильный порядок строк:

1. `package main`
2. `import "fyne.io/fyne/v2/app"`
3. `import "fyne.io/fyne/v2/widget"`
4. `func main() {`
5. `myApp := app.New()`
6. `window := myApp.NewWindow("Заметки")`
7. `window.SetContent(widget.NewLabel("Новая заметка"))`
8. `window.ShowAndRun()`
9. `}`

### Маршрут курса и что дальше

Дальше по курсу так. Второй урок ставит тулкит и запускает первое окно: app.New, SetContent и run loop под микроскопом (02-pervoe-okno). Третий — базовые виджеты Label, Button, Entry, Check, Select; четвёртый — Box-контейнеры и компоновки, тизер которых был выше. Седьмой урок уходит в canvas и примитивы рисования, восьмой собирает собственный виджет на WidgetRenderer — интерфейсная модель из этого урока там окупится полностью. Дальше списки и коллекции, таблицы, вкладки, диалоги, темы, привязка данных, многопоточность и сборка под мобильные и десктоп; полная программа из 25 уроков — на странице [курса Fyne](/courses/fyne/). Финальный проект — менеджер задач: списки, темы, сохранение состояния. Если вы прошли базовый курс этого самоучителя, опыт цельного приложения у вас уже есть: 25-pervoe-prilozhenie собирал консольный менеджер заметок. Здесь вы соберёте такой же по смыслу, но с окном, кнопками и мышью — и он будет работать там же, где вы: на вашей системе.

### Термины урока

- **Fyne** — GUI-тулкит и app-API на Go: виджеты, компоновка и тема написаны на Go, рендер через OpenGL; одна кодовая база для Windows, macOS, Linux, iOS, Android и web.
- **CanvasObject** — Интерфейс всего, что рисуется: размер, позиция, видимость и Refresh(). Примитивы пакета canvas и виджеты — всё это CanvasObject.
- **Widget и WidgetRenderer** — Виджет — CanvasObject с поведением; внешний вид вынесен в WidgetRenderer. Состояние меняется — вызывается Refresh, рендерер перерисовывает.
- **Run loop** — Цикл событий: принимает ввод и перерисовывает экран. Один на приложение; запускается через App.Run() или Window.ShowAndRun() в конце main.
- **MinSize** — Минимальный размер виджета, от которого отталкиваются компоновки: VBox ставит элементы на их минимальную высоту, HBox — на минимальную ширину.
- **Walk и Gio** — Соседи Fyne по нише: Walk — обёртка над Win32, только Windows; Gio — immediate mode GUI ниже уровнем, с нулевой мажорной версией.

## Коротко: что запомнить

- Fyne — GUI-тулкит на Go: виджеты, layout и тема — Go-код; для сборки нужен C-компилятор из-за cgo-привязок к OpenGL и GLFW.
- Всё нарисованное — fyne.CanvasObject; виджет добавляет поведение, внешний вид живёт в WidgetRenderer, обновление — через Refresh().
- Цикл событий один: ShowAndRun в конце main, повторный Run приводит к ошибкам, код после Run выполняется при выходе.
- Одна кодовая база — Windows, macOS, Linux, Android, iOS и web (fyne package -os …); web-драйвер полностью поддерживается с v2.5.0.
- Рендер двойной: OpenGL на десктопе и мобильных, программная растеризация в память — для тестов и headless-сред.
- Walk — только Windows и Win32-контролы; Gio — immediate mode ниже уровнем; Fyne — самый короткий путь к кроссплатформенной форме.

## Домашнее задание

Поставьте тулкит локально: go get fyne.io/fyne/v2@latest, затем go mod tidy после первого импорта. Запустите hello world из урока, поменяйте заголовок окна и текст кнопки, затем выполните go run -tags mobile и сравните вид с десктопным. Прочитайте раздел Architecture в docs.fyne.io про организацию пакетов и выпишите, какие пакеты вам уже знакомы из этого урока, а какие встретятся позже.

## Вопросы и ответы по уроку

### Нужны ли Android SDK и Xcode для разработки под мобильные?

Писать и отлаживать логику можно на десктопе и в мобильной симуляции (go run -tags mobile). Для сборки пакета под Android нужен Android SDK, под iOS — Xcode на macOS: fyne package сам вызывает платформенные инструменты.

### Fyne выглядит нативно на каждой системе?

Нет, и это честная особенность: тема одна, вдохновлена Material Design, поэтому на Windows, macOS и Linux приложение выглядит одинаково. Нативные контролы из коробки даёт обёртка Walk — но только под Windows.

### Можно ли собрать Fyne-приложение для браузера?

Да: fyne serve для локальной проверки и fyne package -os web для хостинга. С версии 2.5.0 web-драйвер полностью поддерживается, но не завершён на сто процентов: файловые диалоги и хранение документов пока недоступны.
