use eframe::NativeOptions;
use egui::{CentralPanel, Color32, Frame, Ui};
use egui_inbox::UiInbox;
use egui_router::{EguiRouter, Route, TransitionConfig};
struct AppState {
inbox: UiInbox<RouterMessage>,
}
enum RouterMessage {
Navigate(String),
Back,
}
#[tokio::main]
async fn main() -> eframe::Result<()> {
let mut router: Option<(EguiRouter<AppState>, AppState)> = None;
eframe::run_ui_native(
"Route Lifecycle Example",
NativeOptions::default(),
move |ui, _frame| {
let (router, state) = router.get_or_insert_with(|| {
let mut state = AppState {
inbox: UiInbox::new(),
};
let router = EguiRouter::builder()
.transition(TransitionConfig::slide().with_duration(0.5))
.swipe_back_gesture(true)
.route("/", HomePage::new)
.route("/detail", DetailPage::new)
.route("/no-swipe", NoSwipePage::new)
.default_path("/")
.build(&mut state);
(router, state)
});
state.inbox.read(ui).for_each(|msg| match msg {
RouterMessage::Navigate(route) => {
router.navigate(state, route).ok();
}
RouterMessage::Back => {
router.back().ok();
}
});
CentralPanel::default().show_inside(ui, |ui| {
router.ui(ui, state);
});
},
)
}
struct HomePage {
events: Vec<&'static str>,
}
impl HomePage {
fn new() -> Self {
Self {
events: vec!["created"],
}
}
}
impl Route<AppState> for HomePage {
fn ui(&mut self, ui: &mut Ui, state: &mut AppState) {
background(ui, ui.style().visuals.faint_bg_color, |ui| {
ui.heading("Home Page");
if ui.link("Go to Detail").clicked() {
state
.inbox
.sender()
.send(RouterMessage::Navigate("/detail".to_string()))
.ok();
}
if ui.link("Go to No Swipe Page").clicked() {
state
.inbox
.sender()
.send(RouterMessage::Navigate("/no-swipe".to_string()))
.ok();
}
ui.add_space(16.0);
ui.separator();
ui.add_space(8.0);
ui.strong("Lifecycle events:");
for event in &self.events {
ui.label(format!("• {event}"));
}
});
}
fn on_showing(&mut self) {
self.events.push("on_showing (transition back started)");
}
fn on_shown(&mut self) {
self.events.push("on_shown (fully visible)");
}
fn on_hiding(&mut self) {
self.events.push("on_hiding (transition away started)");
}
fn on_hide(&mut self) {
self.events.push("on_hide (fully hidden)");
}
}
struct DetailPage {
events: Vec<&'static str>,
}
impl DetailPage {
fn new() -> Self {
Self {
events: vec!["created"],
}
}
}
impl Route<AppState> for DetailPage {
fn ui(&mut self, ui: &mut Ui, state: &mut AppState) {
background(ui, ui.style().visuals.extreme_bg_color, |ui| {
ui.heading("Detail Page");
ui.label("(swipe back enabled — uses router default)");
if ui.button("Back").clicked() {
state.inbox.sender().send(RouterMessage::Back).ok();
}
ui.add_space(16.0);
ui.separator();
ui.add_space(8.0);
ui.strong("Lifecycle events:");
for event in &self.events {
ui.label(format!("• {event}"));
}
});
}
fn on_showing(&mut self) {
self.events.push("on_showing (transition back started)");
}
fn on_shown(&mut self) {
self.events.push("on_shown (fully visible)");
}
fn on_hiding(&mut self) {
self.events.push("on_hiding (transition away started)");
}
fn on_hide(&mut self) {
self.events.push("on_hide (fully hidden)");
}
}
struct NoSwipePage {
events: Vec<&'static str>,
}
impl NoSwipePage {
fn new() -> Self {
Self {
events: vec!["created"],
}
}
}
impl Route<AppState> for NoSwipePage {
fn ui(&mut self, ui: &mut Ui, state: &mut AppState) {
background(ui, ui.style().visuals.window_fill, |ui| {
ui.heading("No Swipe Page");
ui.label("Swipe-back gesture is disabled on this page.");
ui.label("You must use the button to go back.");
if ui.button("Back").clicked() {
state.inbox.sender().send(RouterMessage::Back).ok();
}
ui.add_space(16.0);
ui.separator();
ui.add_space(8.0);
ui.strong("Lifecycle events:");
for event in &self.events {
ui.label(format!("• {event}"));
}
});
}
fn enable_swipe(&self) -> Option<bool> {
Some(false)
}
fn on_showing(&mut self) {
self.events.push("on_showing (transition back started)");
}
fn on_shown(&mut self) {
self.events.push("on_shown (fully visible)");
}
fn on_hiding(&mut self) {
self.events.push("on_hiding (transition away started)");
}
fn on_hide(&mut self) {
self.events.push("on_hide (fully hidden)");
}
}
fn background(ui: &mut Ui, color: Color32, content: impl FnOnce(&mut Ui)) {
Frame::NONE.fill(color).inner_margin(16.0).show(ui, |ui| {
ui.set_width(ui.available_width());
ui.set_height(ui.available_height());
content(ui);
});
}