1. Основы Flet и архитектура Page: жизненный цикл и структура приложения
Основы Flet и архитектура Page: жизненный цикл и структура приложения
Представьте, что вы создаете интерфейс, который должен одинаково безупречно работать как в браузере Safari на iPhone, так и в нативном окне Windows 11, не требуя при этом переписывания ни единой строчки кода, отвечающего за логику. В мире Python-разработки долгое время существовал разрыв между мощными бэкенд-инструментами и фронтенд-фреймворками. Flet устраняет этот барьер, предлагая архитектуру, в которой Python управляет Flutter-движком через протокол обмена данными, превращая разработку UI в манипуляцию иерархическим деревом объектов.
Философия Flet: почему это не просто обертка над Flutter
Большинство кроссплатформенных фреймворков заставляют разработчика выбирать между нативностью и скоростью разработки. Flet занимает уникальную нишу. Технически он представляет собой сервер (написанный на Go), который управляет клиентом (написанным на Dart/Flutter). Когда вы запускаете приложение Flet, вы запускаете Python-скрипт, который общается с отрисовщиком через TCP-сокеты или веб-сокеты.
Это фундаментальное отличие от подходов вроде Kivy или PySide. В PySide вы работаете с биндингами к C++ библиотекам, что требует компиляции под каждую платформу. В Flet ваш Python-код остается неизменным, а «тяжелую» работу по рендерингу берет на себя Flutter-клиент.
Главная ментальная модель, которую нужно принять: Page (страница) — это не просто окно, это сессия. Для десктопного приложения это одно окно, но для веб-приложения — это конкретное подключение пользователя. Если десять человек откроют ваш сайт на Flet одновременно, сервер создаст десять независимых объектов Page, каждый со своим состоянием и жизненным циклом.
Анатомия приложения и точка входа
Любое профессиональное приложение на Flet начинается с функции main. Это не просто соглашение, это обязательная сигнатура для инициализации событийного цикла.
Функция ft.app() — это диспетчер. Она принимает аргумент target, который указывает на вашу основную логику. Важно понимать разницу в способах запуска:
ft.app(target=main) открывает нативное окно.ft.app(target=main, view=ft.AppView.WEB_BROWSER) запускает локальный веб-сервер и открывает вкладку в браузере.Объект page является корневым контейнером. Все, что вы видите на экране, находится внутри page.controls. Это список, и манипуляция этим списком — основной способ изменения интерфейса.
Жизненный цикл страницы и событийная модель
Понимание жизненного цикла Page критично для управления ресурсами, например, для закрытия соединений с базой данных или остановки фоновых задач при закрытии окна.
Когда пользователь запускает приложение, происходит следующая последовательность:
Page.main.page.add() или page.controls.append().page.update().Важнейшим аспектом является обработка закрытия страницы. В Flet есть событие on_close, но оно специфично для веб-сессий. Для десктопа чаще используется on_window_event, который позволяет перехватить попытку закрытия окна:
Событийная модель Flet построена на колбэках. Каждое событие (клик, ввод текста, изменение размера окна) порождает объект ControlEvent, содержащий:
target: Имя (ID) контрола, вызвавшего событие.name: Тип события (например, "click").data: Дополнительные данные (например, индекс выбранной вкладки).Иерархия контролов: Дерево против Списка
Во Flet интерфейс строится как дерево. Page — это корень. Внутри него могут быть View (для навигации), внутри View — AppBar, FloatingActionButton и основной контент в controls.
Рассмотрим структуру сложного интерфейса:
Ключевой нюанс: Flet минимизирует трафик между Python и отрисовщиком. Когда вы меняете свойство контрола, например, text_field.value = "New Value", визуально ничего не меняется до вызова page.update(). Этот метод собирает все изменения («диффы») и отправляет их одним пакетом. В высоконагруженных интерфейсах вызов update() после каждого изменения — это антипаттерн. Правильнее изменить 10 свойств и вызвать update() один раз.
Глубокое погружение в объект Page
Объект ft.Page обладает огромным набором свойств, определяющих поведение всего приложения.
Геометрия и параметры окна
Для десктопных приложений управление окном — первоочередная задача.page.window_width и page.window_height: Установка размеров.page.window_resizable: Запрет или разрешение изменения размера.page.window_always_on_top: Поверх всех окон.page.window_top, page.window_left: Позиционирование на экране.Интересной особенностью является работа с адаптивностью на уровне страницы. Свойство page.platform позволяет узнать, где запущено приложение (ios, android, macos, windows, linux, web). Это позволяет динамически менять интерфейс:
Выравнивание и отступы
Многие начинающие разработчики пытаются центрировать элементы, оборачивая их в бесконечные контейнеры. ОднакоPage сам по себе является мощным контейнером:
page.vertical_alignment: Выравнивание по вертикали (MainAxisAlignment).page.horizontal_alignment: Выравнивание по горизонтали (CrossAxisAlignment).page.padding: Внутренние отступы всей страницы.Пример идеально центрированного контента:
Управление состоянием на уровне страницы
Flet предоставляет встроенные механизмы для хранения данных сессии. Это избавляет от необходимости использовать глобальные переменные, которые могут «протекать» между разными пользователями в веб-версии.
page.session
Это словарь, который живет ровно столько, сколько открыта страница.
page.client_storage
Еслиsession очищается после обновления страницы в браузере, то client_storage использует локальное хранилище браузера (LocalStorage) или файл на диске для десктопа. Это идеальное место для хранения токенов авторизации или настроек темы.Важно помнить: данные в client_storage сериализуются в JSON, поэтому туда нельзя сохранять сложные Python-объекты (например, экземпляры классов с методами).
Асинхронность в архитектуре Page
Поскольку Flet часто используется для приложений, взаимодействующих с API или базами данных, блокировка основного потока недопустима. Если вы выполните time.sleep(10) внутри main, интерфейс «зависнет» для пользователя.
Flet поддерживает asyncio из коробки. Для этого достаточно объявить main как async:
При использовании асинхронного main, Flet автоматически запускает событийный цикл. Это позволяет легко интегрировать библиотеки вроде httpx или motor (MongoDB) для создания реактивных интерфейсов.
Паттерны структурирования кода
Для небольших скриптов достаточно одной функции main. Но профессиональная разработка требует разделения ответственности. Существует два основных подхода к организации кода во Flet:
Функциональный подход (Composition)
Вы разбиваете интерфейс на функции, возвращающие контролы.
Классовый подход (Encapsulation)
Создание кастомных контролов путем наследования отft.UserControl (в новых версиях рекомендуется наследоваться от конкретных контейнеров, например, ft.Container или ft.Column). Это позволяет инкапсулировать логику внутри компонента.Использование классов делает код тестируемым и повторно используемым. Вы можете создать библиотеку собственных компонентов и использовать их в разных проектах.
Оптимизация обновлений: метод update() и его нюансы
Одной из самых частых ошибок является избыточный вызов page.update(). Когда ваше приложение растет, дерево контролов становится тяжелым. Вызов page.update() заставляет Flet проверять всё дерево на наличие изменений.
Чтобы оптимизировать работу:
Container, вызывайте container.update(), а не page.update(). Это обновит только эту ветку дерева.page.controls.clear(): При полной смене экрана (например, переход из логина в личный кабинет) очищайте список контролов перед добавлением новых, чтобы старые объекты удалялись из памяти.Особенности работы с Overlay
Иногда элементы должны отображаться «над» основным контентом, но не быть частью стандартного потока верстки (например, диалоговые окна, снекбары или контекстные меню). Для этого у Page есть свойство overlay.
Элементы в overlay не занимают места в макете, что критично для создания современных UI-эффектов.
Граничные случаи: когда Flet ведет себя иначе
Необходимо учитывать различия между Web и Desktop версиями в контексте Page:
page.fonts, иначе браузер заменит их на стандартные.C:\Users\... работает, в вебе — нет. Всегда используйте относительные пути и папку assets.page.on_keyboard_event) работают везде, но системные сочетания (типа Alt+F4) перехватываются операционной системой до того, как попадут во Flet.Завершая разбор основ, важно подчеркнуть: Page — это ваш холст и одновременно дирижер. Понимание того, как данные текут от Python к Flutter-клиенту через дерево контролов и как update() синхронизирует эти состояния, является фундаментом для освоения более сложных тем — адаптивной верстки и навигации.