user-interfacerust

Pass value without cloning it to another view in Iced?


I have IpFetcher::Unregistered view where I fill form and at submit button press calling message Message::Register that will change my view from IpFetcher::Unregistered to IpFetcher::Registered. But I want to be able to see and change form's field even at this view change (want it to look same). So even if Registered I'm still able to update the values and click submit button again. I'm doing it by cloning ref mut register_form from one view to another at update (message call). If it is once I would be able to clone it, but in next stages I want to do some loop stuffs, and cloning it whole time is not a good approach I think....

fn update(&mut self, message: Message) -> Command<Message> {
        match message {

........
            Message::Register =>  {
                if let IpFetcher::Unregistered { ref mut register_form, .. } = self {
                    *self = IpFetcher::Registered { register_form_registered: register_form.clone() };


                }

                Command::none()

            }

}


    fn view(&self) -> Element<Message> {
        let content = match self {

            IpFetcher::Unregistered { register_form } => 
                column![register_form.view()] // <<<<< HERE I have submit button button("Register").on_press(Message::Register)
                    .max_width(800)
                    .spacing(20)
                    .align_items(Alignment::End),
            
        

            IpFetcher::Registered { register_form_registered } => {
                column![
                    register_form_registered.view(),
                    button("Start Fetching!").on_press(Message::Search)]
                    .max_width(800)
                    .spacing(20)
                    .align_items(Alignment::End)
            }

I tried to change it to Option<RegisterForm> and pass the value, but this didn't work well..


Solution

  • This is not the right way to handle this kind of cases, the pure enum is enough and you redirect the rendering for each variant you want to control, now, in these cases I prefer to use the builders instead of using the macros, because they give me more flexibility.

    ref: https://github.com/SergioRibera/Simplemoji/blob/main/src/layouts/mod.rs

        let mut col = Column::new().push(tab);
        if settings.show_search {
            col = col.push(show_search_row(search, tone));
        }
        if settings.show_preview {
            col = col.push(show_preview(emoji_hovered));
        }
    

    Here is another example I have that uses enumerators and constructors at the same time to have “dynamic” views https://github.com/SergioRibera/super_clipboard/blob/main/src/ui/mod.rs#L192-L252

    Basically you do something like this

    enum AppView {
      Home,
      Settings,
      Other
    }
    
    pub struct MainApp {
      pub current_view: AppView,
    }
    
    impl Application for MainApp {
      fn view(&self) -> Element<MainMessage> {
        // We use the match to validate which view is the current one,
        // this can be later modified from the update function in any situation.
        match self.curren_view {
          // This is just an example,
          // I like to have the screens and layouts separated in modules,
          // so for each case I call a function that builds the necessary UI.
          AppView::Home => view_mod::home(),
          AppView::Settings => view_mod::settings(),
          _ => view_mod::error_view(),
        }
      }
    }