Freya Documentation
repository·main·Indexed 25 days ago
https://github.com/marc2332/freyaA Rust-based native GUI framework featuring components, animations, and state management. It includes specialized tools such as Freya Router (a Dioxus Router fork), the Torin layout library, the Ragnarok events processor, and experimental Android support via the AndroidPlugin and AndroidExt trait. The framework also provides a comprehensive animation system with hooks like use_animation and types such as AnimNum, AnimColor, and AnimSequential.
What's inside Freya
- Ragnarok is a pure Rust UI events processor library. While it was developed for Freya (a native GUI library for Rust), Ragnarok is completely agnostic and can be used independently in other Rust projects.
Overview of Torin layout library
mainTorin 📐 is a pure Rust layout library designed for use with Freya 🟣, a native GUI library for Rust. While optimized for Freya, Torin is decoupled enough to work with other GUI libraries; a demonstration of this interoperability can be found in thedemo.rsexample within the repository.Key differences in Freya Router vs Dioxus Router
mainFreya Router is a fork of Dioxus Router optimized for the Freya ecosystem. The primary architectural differences are:
- Multiple Routers Support: Unlike Dioxus Router, which allows only one router per app, Freya Router allows multiple routers in the same component tree by providing the router context to children rather than injecting it at the root.
- Built-in
ActivableRoute: A helper component (moved fromfreya-components) that informs child components if a specific route is currently active, without requiring those children to depend onfreya-routerdirectly. - Built-in
NativeRouter: Provides Freya-specific integration for mouse-based back and forward navigation (moved fromfreya-components). - MemoryHistory: The router always uses
MemoryHistory, removing the need for renderer-agnostic APIs. - Reduced Dependencies: The
htmlfeature and WASM-splitting features have been removed to avoid pulling indioxus-htmland unnecessary overhead.
Use skia-plotters-backend for plotter-rs
mainTheskia-plotters-backendprovides a backend for theplotterscrate usingrust-skia(skia-safe). This allows you to use theplottersAPI to render charts and plots using the Skia graphics engine.Define the App Root with Function Components or the App Trait
mainThe application root can be a plain function (Function Component) or a struct implementing the
Apptrait.- Function Components: Use these for the app root only. This is where top-level hooks like
use_init_theme,use_init_radio_station, oruse_provide_contextshould be called. - App Trait: Use this to pass data from
maininto the root component.
Note: Reusable UI that uses hooks or props MUST be a
Component(struct +impl Component). Plain functions are only for the app root and stateless helpers.// Function component root fn app() -> impl IntoElement { rect().child("Hello, World!") } // App trait root struct MyApp { number: u8 } impl App for MyApp { fn render(&self) -> impl IntoElement { label().text(self.number.to_string()) } }- Function Components: Use these for the app root only. This is where top-level hooks like
Perform Conditional and Dynamic Rendering
mainUse the following methods to handle conditional elements within the builder chain:
.maybe(bool, |el| ...): Applies the callback only when the boolean condition is true..map(Option<T>, |el, val| ...): Applies the callback when theOptionisSome, passing the inner value to the callback..maybe_child(Option<impl IntoElement>): Appends a child only when the providedOptionisSome.
Best Practice: Prefer one outer
.maybeor.mapto wrap multiple conditional children rather than repeating the same condition across multiple.maybe_childcalls. This keeps the gating logic in one place.// Good: Single .maybe wraps all conditional children rect() .maybe(show, |el| { el.child(Title::new("Hi")) .child(Content::new().child("Hello")) .child(Footer::new()) }) // Bad: Same predicate repeated per child rect() .maybe_child(show.then(|| Title::new("Hi"))) .maybe_child(show.then(|| Content::new().child("Hello"))) .maybe_child(show.then(|| Footer::new()))Use ContextMenu for floating menus
mainThe
ContextMenucomponent enables floating menus.Requirement: You must call
ContextMenuViewer::new()in an ancestor of the component where you intend to open menus (ideally near the app root) to provide the rendering slot.To open a menu from a press event, use
ContextMenu::open_from_event(&event, menu_content).fn context_menu() -> Menu { Menu::new() .child( SubMenu::new() .child(MenuButton::new().child("Option 1")) .child(MenuButton::new().child("Option 2")) .label("Options"), ) .child(MenuButton::new().child("Close").on_press(move |_| ContextMenu::close())) } // Open from any press event Button::new().on_press(move |e: Event<PressEventData>| { ContextMenu::open_from_event(&e, context_menu()) })Setup experimental Hot Reloading
mainFreya 0.4 supports hot reloading via the
subsecondcrate and the DioxusdxCLI. This requires thehotreloadfeature flag.- Enable the feature in your
Cargo.toml:
[features] hotreload = ["freya/hotreload"]- Install the Dioxus CLI:
cargo install dioxus-cli- Run the application:
dx serve --hot-patch --features hotreloadLimitations:
- Hook state is reset and spawned tasks are cancelled on every patch.
- Only function bodies can be patched; structural changes (new types or changed signatures) require a full restart.
# In your Cargo.toml [features] hotreload = ["freya/hotreload"]cargo install dioxus-cli dx serve --hot-patch --features hotreload- Enable the feature in your
Enable the Performance Overlay plugin
mainThe
PerformanceOverlayPluginrenders a live metrics panel. It is automatically registered on debug builds and can be shown/hidden usingCtrl+Shift+P(orCmd+Shift+Pon macOS). To manually register it in your launch configuration:launch( LaunchConfig::new() .with_plugin(PerformanceOverlayPlugin::default()) .with_window(WindowConfig::new(app)) )Enable Developer Tools
mainEnable thedevtoolsfeature in yourCargo.tomlto add a real-time component tree inspector. To use it, run the devtools app alongside your application to examine layout, props, and state.Implement a System Tray with Menus
mainFreya supports system tray icons with menus. You can define a tray icon using
TrayIconBuilderand provide a handler function to respond toTrayEvent::Menuevents. This allows you to run apps entirely from the tray or use the tray to control window visibility and lifecycle.fn main() { let tray_icon = || { let tray_menu = Menu::new(); let _ = tray_menu.append(&MenuItem::with_id("open", "Open", true, None)); let _ = tray_menu.append(&MenuItem::with_id( "toggle-visibility", "Toggle Visibility", true, None, )); let _ = tray_menu.append(&MenuItem::with_id("exit", "Exit", true, None)); TrayIconBuilder::new() .with_menu(Box::new(tray_menu)) .with_tooltip("Freya Tray") .with_icon(LaunchConfig::tray_icon(ICON)) .build() .unwrap() }; let tray_handler = |ev, mut ctx: RendererContext| match ev { TrayEvent::Menu(MenuEvent { id }) if id == "open" => { ctx.launch_window(WindowConfig::new(app).with_size(500., 450.)); } // ... handle the other menu items (toggle visibility, exit, ...) _ => {} }; launch(LaunchConfig::new().with_tray(tray_icon, tray_handler)) }Embed a WebView with freya-webview
mainThe experimental
freya-webviewcrate allows embedding native web views. To use it, you must register theWebViewPluginin yourLaunchConfig. You can then use theWebViewcomponent to render a URL. Web views are identified byWebViewId, allowing for multi-tab setups and explicit disposal viaWebViewManager::close(id). This feature requires thewebviewfeature flag.fn main() { launch( LaunchConfig::new() .with_plugin(WebViewPlugin::new()) .with_window(WindowConfig::new(app)), ) } fn app() -> impl IntoElement { WebView::new("https://duckduckgo.com").expanded() }