Skip to content

Latest commit

 

History

History
191 lines (157 loc) · 5.43 KB

File metadata and controls

191 lines (157 loc) · 5.43 KB

Loading Images Asynchronously

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_handle is None and show_container is false;
  • loading: image_handle is None and show_container is true;
  • loaded: image_handle is Some(...) and show_container is 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:

Loading Images Asynchronously 1

State of loading:

Loading Images Asynchronously 2

State of loaded:

Loading Images Asynchronously 3

➡️ Next: The end

📘 Back: Table of contents