How to create a counter with button click?

main
2396fdffeat(torin): `Content::Wrap` (#1408)
Avatar of Marc EspinMarc Espinposted

how would a hello world look like? I want a counter that increase upon button click

Avatar of Claude Haiku 4.5Claude Haiku 4.5commented

I'll help you understand how a "Hello World" with a counter would look like in the Freya framework. Let me first explore the repository structure to find relevant examples.

Perfect! I can see there's a counter.rs example. Let me read that:

Perfect! Here's what a Hello World counter application would look like in Freya. This is the actual example from the repository:

##Counter Example

use freya::prelude::*;

fn main() {
    launch(LaunchConfig::new().with_window(WindowConfig::new(app).with_size(500., 450.)))
}

fn app() -> impl IntoElement {
    let mut count = use_state(|| 4);

    let counter = rect()
        .width(Size::fill())
        .height(Size::percent(50.))
        .center()
        .color((255, 255, 255))
        .background((15, 163, 242))
        .font_weight(FontWeight::BOLD)
        .font_size(75.)
        .shadow((0., 4., 20., 4., (0, 0, 0, 80)))
        .child(count.read().to_string());

    let actions = rect()
        .horizontal()
        .width(Size::fill())
        .height(Size::percent(50.))
        .center()
        .spacing(8.0)
        .child(
            Button::new()
                .on_press(move |_| {
                    *count.write() += 1;
                })
                .child("Increase"),
        )
        .child(
            Button::new()
                .on_press(move |_| {
                    *count.write() -= 1;
                })
                .child("Decrease"),
        );

    rect().child(counter).child(actions)
}

##Key Concepts:

  1. State Management: use_state(|| 4) creates a reactive state initialized to 4

    • count.read() - reads the value
    • count.write() - writes/mutates the value
  2. UI Building: Using a declarative builder pattern with chainable methods

    • rect() - creates rectangular containers
    • .horizontal() - layout direction
    • .child() - adds children elements
  3. Event Handling: Button::new().on_press(move |_| { ... }) handles button clicks

    • Closure captures count with move
    • Updates state inside the closure
  4. Styling:

    • Colors: (R, G, B) or (R, G, B, A)
    • Size: Size::fill(), Size::percent(50.)
    • Typography: font size, weight
    • Effects: shadows, spacing

The app displays the counter in the upper half (50% height) with large text, and two buttons in the lower half that increment/decrement the counter!

Avatar of Marc EspinMarc Espincommented

how could I also render a progress bar alongside the buttons

Avatar of Claude Haiku 4.5Claude Haiku 4.5commented

Let me look at the progress bar component example to see how it's used:

Great! Now I can show you how to integrate a progress bar with the counter. Here's the modified code:

use freya::prelude::*;

fn main() {
    launch(LaunchConfig::new().with_window(WindowConfig::new(app).with_size(500., 450.)))
}

fn app() -> impl IntoElement {
    let mut count = use_state(|| 4);

    let counter = rect()
        .width(Size::fill())
        .height(Size::percent(50.))
        .center()
        .color((255, 255, 255))
        .background((15, 163, 242))
        .font_weight(FontWeight::BOLD)
        .font_size(75.)
        .shadow((0., 4., 20., 4., (0, 0, 0, 80)))
        .child(count.read().to_string());

    let progress_value = (*count.read() as f32 / 100.0 * 100.0).min(100.0).max(0.0);

    let actions = rect()
        .vertical()
        .width(Size::fill())
        .height(Size::percent(50.))
        .padding(16.0)
        .spacing(8.0)
        .child(
            rect()
                .horizontal()
                .width(Size::fill())
                .spacing(8.0)
                .child(
                    Button::new()
                        .on_press(move |_| {
                            *count.write() += 1;
                        })
                        .child("Increase"),
                )
                .child(
                    Button::new()
                        .on_press(move |_| {
                            *count.write() -= 1;
                        })
                        .child("Decrease"),
                ),
        )
        .child(
            ProgressBar::new(progress_value)
                .width(Size::fill())
        );

    rect().child(counter).child(actions)
}

##Key Changes:

  1. Progress Bar Value: Calculate progress based on the counter

    let progress_value = (*count.read() as f32 / 100.0 * 100.0).min(100.0).max(0.0);
    

    This maps the counter value to a 0-100% range (clamped)

  2. Layout: Changed actions to use .vertical() instead of .horizontal() to stack buttons and progress bar

  3. Added padding and spacing for better visual separation

  4. Progress bar child: Added alongside buttons

    ProgressBar::new(progress_value)
        .width(Size::fill())
    

Now the progress bar will update as you click the buttons! You can adjust the progress calculation to match whatever range you want. For example, if you want it to go from 0-100 on the counter directly, you could simplify it to just *count.read() as f32 and adjust your button logic accordingly.


END OF POST

Log in to add a comment
Claude Haiku 4.5
How to create a counter with button click? — marc2332/freya