In the previous tutorials, we introduced how to use Tasks to execute asynchronous operations, and how to display images.
This tutorial combines both and demonstrates how to load an image asynchronously.
Iced offers three mutually exclusive features for asynchronous operations. They need to be enabled before usage:
Depending on your choice, your must also add the dependency crate to your Cargo.toml file:
The tokio crate is very popular, and we will use it as an example.
First, we enable tokio feature and add tokio crate with fs and io-util features.
The dependencies of Cargo.toml should look like this:
[dependencies]
iced = { version = "0.13.1", features = ["tokio"] }
tokio = { version = "1.44.2", features = ["fs", "io-util"] }Our app will have three states: start, loading and loaded. We use two fields to encode the three states.
struct MyApp {
image_handle: Option<Handle>,
show_container: bool,
}When the state is
- start:
image_handleisNoneandshow_containeris false; - loading:
image_handleisNoneandshow_containeris true; - loaded:
image_handleisSome(...)andshow_containeris true.
The app begins in the start state.
The app always shows a button that is for loading the ferris.png image.
In the start state, the app shows no additional widget.
In the loading state, the app shows the text Loading....
And in the loaded state, the app shows the image.
fn view(&self) -> iced::Element<Message> {
column![
button("Load").on_press(Message::Load),
if self.show_container {
match &self.image_handle {
Some(h) => container(Image::new(h.clone())),
None => container("Loading..."),
}
} else {
container("")
},
]
.padding(20)
.into()
}We have two messages for the app:
#[derive(Debug, Clone)]
enum Message {
Load,
Loaded(Vec<u8>),
}When the button is pressed, the app triggers a Load message to load the image.
And when the image is loaded, the app triggers a Loaded(...) message.
The image will be loaded asynchronously.
fn update(&mut self, message: Message) -> Task<Message> {
match message {
Message::Load => {
self.show_container = true;
return Task::perform(
async {
let mut file = File::open("./tutorial/pic/ferris.png").await.unwrap();
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).await.unwrap();
buffer
},
Message::Loaded,
);
}
Message::Loaded(data) => self.image_handle = Some(Handle::from_bytes(data)),
}
Task::none()
}The full code is as follows:
use iced::{
Task,
widget::{Image, button, column, container, image::Handle},
};
use tokio::{fs::File, io::AsyncReadExt};
fn main() -> iced::Result {
iced::application("My App", MyApp::update, MyApp::view).run_with(MyApp::new)
}
#[derive(Debug, Clone)]
enum Message {
Load,
Loaded(Vec<u8>),
}
#[derive(Default)]
struct MyApp {
image_handle: Option<Handle>,
show_container: bool,
}
impl MyApp {
fn new() -> (Self, Task<Message>) {
(
Self {
image_handle: None,
show_container: false,
},
Task::none(),
)
}
fn update(&mut self, message: Message) -> Task<Message> {
match message {
Message::Load => {
self.show_container = true;
return Task::perform(
async {
let mut file = File::open("./tutorial/pic/ferris.png").await.unwrap();
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).await.unwrap();
buffer
},
Message::Loaded,
);
}
Message::Loaded(data) => self.image_handle = Some(Handle::from_bytes(data)),
}
Task::none()
}
fn view(&self) -> iced::Element<Message> {
column![
button("Load").on_press(Message::Load),
if self.show_container {
match &self.image_handle {
Some(h) => container(Image::new(h.clone())),
None => container("Loading..."),
}
} else {
container("")
},
]
.padding(20)
.into()
}
}State of start:
State of loading:
State of loaded:
➡️ Next: The end
📘 Back: Table of contents


