Iced: How can I implement a music list ?

Created on 15 Aug 2020  路  2Comments  路  Source: hecrj/iced

Like Spotify does.
Sorry if it's a dumb question, I'm a total beginner.

question

Most helpful comment

As H茅ctor said the question is a little bit broad. ;)

Accordingly, I would recommend that when you are faced with a big problem, you divide it into smaller problems.

Smaller problems can usually be solved much faster.

Let's assume we only want to display the title and the duration (in seconds) of a song. We could use a simple tuple for this: (String, u32). Since we are working with more than one song in a playlist we would need a collection like a Vector.

With this, our data would be as follows:

struct MusicPlayer {
    playlist: Vec<(String, u32)>,
}

As mentioned earlyer we could use a Row to show the title and duration grouped in one line like this:

Row::new()
    .push(Text::new(title))
    .push(Text::new(format_duration(duration)))

Since we are storing the duration in seconds, we would need a way to format it to mm:ss. This is up to you! :)

Now that we have a way to display one song, we would need a way to display all songs from the playlist. We could use a Column for this. To add all songs from the Vector to a new Column we could use the fold(...) function of the Iterator. It could look like this:

playlist.iter_mut()
    .fold(Column::new(), | column, (title, duration) | {
        column.push(
            // Row ...
        )
    })

After solving the small problems we can now face the big one and combine everything together:

use iced::{ Element, Sandbox, Settings, Text, Container, Column, Row, Scrollable, scrollable, Length };

struct MusicPlayer {
    playlist: Vec<(String, u32)>,
    scroll: scrollable::State,
}

impl Sandbox for MusicPlayer {
    type Message = ();

    fn new() -> Self {
        MusicPlayer {
            playlist: vec!(
                (String::from("Foo"), 123),
                (String::from("Bar"), 456),
                (String::from("Baz"), 789),
            ),
            scroll: scrollable::State::new(),
        }
    }

    fn title(&self) -> String {
        String::from("Music Player")
    }

    fn update(&mut self, _: Self::Message) {

    }

    fn view(&mut self) -> Element<Self::Message> {
        let playlist: Element<_> = self.playlist.iter_mut()
            .enumerate()
            .fold(Column::new().spacing(10), |column, (i, (title, duration))| {
                column.push(
                    Row::new()
                        .spacing(20)
                        .push(Text::new(format!("{}.", i+1)))
                        .push(Text::new(title.to_owned()))
                        .push(Text::new(format_duration(duration)))
                )
            })
            .into();

        let scrollable: Element<_> = Scrollable::new(&mut self.scroll)
            .push(playlist)
            .into();

        Container::new(scrollable)
            .width(Length::Fill)
            .height(Length::Fill)
            .center_x()
            .center_y()
            .into()
    }
}

fn format_duration(seconds: &u32) -> String {
    // up to you ;)
}

fn main() {
    MusicPlayer::run(Settings::default())
}

This will give us this nice little list:
Bildschirmfoto vom 2020-08-18 19-47-17

Of course this is only the foundation and there would be a lot more to do and maybe you would like to replace the tuple by a struct, but I hope I could help you out. If you have any questions, don't hesitate! :)

All 2 comments

Not a dumb question, but maybe a bit broad! :)

You should be able to use a Scrollable with many instances of Row to implement such a list. The todos example showcases something similar.

If you need more help, I recommend you to share more details about your use case here or join our Zulip server!

As H茅ctor said the question is a little bit broad. ;)

Accordingly, I would recommend that when you are faced with a big problem, you divide it into smaller problems.

Smaller problems can usually be solved much faster.

Let's assume we only want to display the title and the duration (in seconds) of a song. We could use a simple tuple for this: (String, u32). Since we are working with more than one song in a playlist we would need a collection like a Vector.

With this, our data would be as follows:

struct MusicPlayer {
    playlist: Vec<(String, u32)>,
}

As mentioned earlyer we could use a Row to show the title and duration grouped in one line like this:

Row::new()
    .push(Text::new(title))
    .push(Text::new(format_duration(duration)))

Since we are storing the duration in seconds, we would need a way to format it to mm:ss. This is up to you! :)

Now that we have a way to display one song, we would need a way to display all songs from the playlist. We could use a Column for this. To add all songs from the Vector to a new Column we could use the fold(...) function of the Iterator. It could look like this:

playlist.iter_mut()
    .fold(Column::new(), | column, (title, duration) | {
        column.push(
            // Row ...
        )
    })

After solving the small problems we can now face the big one and combine everything together:

use iced::{ Element, Sandbox, Settings, Text, Container, Column, Row, Scrollable, scrollable, Length };

struct MusicPlayer {
    playlist: Vec<(String, u32)>,
    scroll: scrollable::State,
}

impl Sandbox for MusicPlayer {
    type Message = ();

    fn new() -> Self {
        MusicPlayer {
            playlist: vec!(
                (String::from("Foo"), 123),
                (String::from("Bar"), 456),
                (String::from("Baz"), 789),
            ),
            scroll: scrollable::State::new(),
        }
    }

    fn title(&self) -> String {
        String::from("Music Player")
    }

    fn update(&mut self, _: Self::Message) {

    }

    fn view(&mut self) -> Element<Self::Message> {
        let playlist: Element<_> = self.playlist.iter_mut()
            .enumerate()
            .fold(Column::new().spacing(10), |column, (i, (title, duration))| {
                column.push(
                    Row::new()
                        .spacing(20)
                        .push(Text::new(format!("{}.", i+1)))
                        .push(Text::new(title.to_owned()))
                        .push(Text::new(format_duration(duration)))
                )
            })
            .into();

        let scrollable: Element<_> = Scrollable::new(&mut self.scroll)
            .push(playlist)
            .into();

        Container::new(scrollable)
            .width(Length::Fill)
            .height(Length::Fill)
            .center_x()
            .center_y()
            .into()
    }
}

fn format_duration(seconds: &u32) -> String {
    // up to you ;)
}

fn main() {
    MusicPlayer::run(Settings::default())
}

This will give us this nice little list:
Bildschirmfoto vom 2020-08-18 19-47-17

Of course this is only the foundation and there would be a lot more to do and maybe you would like to replace the tuple by a struct, but I hope I could help you out. If you have any questions, don't hesitate! :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Newbytee picture Newbytee  路  4Comments

porglezomp picture porglezomp  路  3Comments

olanod picture olanod  路  4Comments

Charles-Schleich picture Charles-Schleich  路  3Comments

Gohla picture Gohla  路  4Comments