Skip to content

Latest commit

 

History

History
160 lines (131 loc) · 4.41 KB

memoryless_pages.md

File metadata and controls

160 lines (131 loc) · 4.41 KB

Memoryless Pages

The previous tutorial has a problem that the fields can still be accessed when we navigate to other pages. This brings potential security problems, e.g., the input password could be accessed in other pages.

To fix this problem, we can use trait objects. The Page trait below is responsible for update and view for a single page. In the main struct MyApp, we dispatch update and view to the corresponding page that is indicated by page field in MyApp. The update method in MyApp is also responsible for switching pages.

In addition, we explicitly distinguish messages from different pages in MyAppMessage.

use iced::Element;

fn main() -> iced::Result {
    iced::application("My App", MyApp::update, MyApp::view).run()
}

struct MyApp {
    page: Box<dyn Page>
}

impl Default for MyApp {
    fn default() -> Self {
        MyApp::new()
    }
}

#[derive(Debug, Clone)]
enum Message {
    PageA(PageAMessage),
    PageB(PageBMessage),
}

trait Page {
    fn update(&mut self, message: Message) -> Option<Box<dyn Page>>;
    fn view(&self) -> iced::Element<'_, Message>;
}

impl MyApp {
    fn new() -> Self {
        Self {
            page: Box::new(PageA::new())
        }
    }
  
    fn update(&mut self, message: Message) {
        let page = self.page.update(message);
        if let Some(p) = page {
            self.page = p;
        }
    }

    fn view(&self) -> Element<Message> {
        self.page.view()
    }
}

In this tutorial, we have two pages, PageA and PageB. PageA is a simple login form and PageB is a simple hello page. Let's start with PageB. In its update method, we only care about messages of PageBMessage.

#[derive(Debug, Clone)]
enum PageBMessage {
    ButtonPressed,
}

type Mb = PageBMessage;
  
struct PageB;

impl PageB {
    fn new() -> Self{
        Self
    }
}

impl Page for PageB {
    fn update(&mut self, message: Message) -> Option<Box<dyn Page>> {
        if let Message::PageB(msg) = message {
            match msg {
                PageBMessage::ButtonPressed => return Some(Box::new(PageA::new())),
            }
        }
        None
    }

    fn view(&self) -> iced::Element<Message> {
        column![
            text("Hello!"),
            button("Log out").on_press(Message::PageB(Mb::ButtonPressed)),
        ]
        .into()
    }
}

Page B

In PageA, we check the password when the login button is pressed. If it is a valid password, we switch to PageB. Note that PageA (and its password field) is dropped after we switch to PageB. This ensures the password is protected.

#[derive(Debug, Clone)]
enum PageAMessage {
    TextChanged(String),
    ButtonPressed,
}
type Ma = PageAMessage;

struct PageA {
    password: String,
}

impl PageA {
    fn new() -> Self {
        Self {
            password: String::new(),
        }
    }
}

impl Page for PageA {
    fn update(&mut self, message: MyAppMessage) -> Option<Box<dyn Page>> {
        if let MyAppMessage::PageA(msg) = message {
            match msg {
                PageAMessage::TextChanged(s) => self.password = s,
                PageAMessage::ButtonPressed => {
                    if self.password == "abc" {
                        return Some(Box::new(PageB::new()));
                    }
                }
            }
        }
        None
    }

    fn view(&self) -> iced::Element<MyAppMessage> {
        column![
            text_input("Password", &self.password)
                .secure(true)
                .on_input(|s| MyAppMessage::PageA(Ma::TextChanged(s))),
            button("Log in").on_press(MyAppMessage::PageA(Ma::ButtonPressed)),
        ]
        .into()
    }
}

Page A

➡️ Next: Passing Parameters Across Pages

📘 Back: Table of contents