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()
}
}
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()
}
}
➡️ Next: Passing Parameters Across Pages
📘 Back: Table of contents