🧬

Dioxus v0.5 でデスクトップアプリを作る

2024/04/02に公開1

概要

Dioxus v0.5.0 がリリースされたので簡単な電卓アプリ(デスクトップアプリ)を作ってみます。

Dioxus とは

Rust で React ライクにマルチプラットフォームのアプリを作成することができます。
アプリ開発に必要な状態管理, ルーターが備わっています。
また、Tailwind を使用することもできます。

公式情報

環境

  • rustc 1.76.0 (07dca489a 2024-02-04)
  • cargo 1.76.0 (c84b36747 2024-01-18)
  • Dioxus v0.5.0
    • dioxus-cli v0.5.0
  • Visual Studio Code 1.87.2
    • Dioxus VSCode Extension v0.5.0

セットアップ

Getting Started を参考に Dioxus をセットアップします。

  1. Visual Studio Code に Dioxus VSCode Extension を追加します。

  2. Rust をインストールします。

  3. デスクトップアプリの依存関係をインストールします。

  4. Dioxus CLI をインストールします。

    cargo install dioxus-cli@0.5.0
    

プロジェクトの作成

  1. プロジェクトの作成を開始します。

    dx new
    
  2. テンプレートは Desktop を選択します。

    ? 🤷   Which sub-template should be expanded? ›
      Web
      Liveview
      Fullstack
    ❯ Desktop
      TUI
    
  3. プロジェクト名は calculator にします。

    🤷   Project Name: calculator
    
  4. Tailwind は使用しないので Vanilla を選択します。

    ? 🤷   How do you want to create CSS? ›
      Tailwind
    ❯ Vanilla
    
  5. ルーターは使用しないので false を選択します。

    ? 🤷   Should the application use the Dioxus router? ›
    ❯ false
      true
    
  6. 作成したプロジェクトを実行します。

    cd calculator
    dx serve --hot-reload --platform desktop
    

State

実装の前に State の使い分けについて簡単に説明します。

  • 1 つのコンポーネントでしか State を使わない場合は、use_signal を使用します。(Component State)
  • 複数のコンポーネントで State を使う場合は、use_context_provider を使用します。(Sharing State)

画面の実装

  1. App コンポーネントを修正して電卓アプリの画面を実装します。

    calculator/src/main.rs
    #[component]
    fn App() -> Element {
       rsx! {
          link { rel: "stylesheet", href: "main.css" }
          div { id: "calculator",
                div { id: "result", "0" }
                div {
                   button { "1" }
                   button { "2" }
                   button { "3" }
                   button { "+" }
                }
                div {
                   button { "4" }
                   button { "5" }
                   button { "6" }
                   button { "-" }
                }
                div {
                   button { "7" }
                   button { "8" }
                   button { "9" }
                   button { "*" }
                }
                div {
                   button { "0" }
                   button { "C" }
                   button { "=" }
                   button { "/" }
                }
          }
       }
    }
    
  2. 電卓アプリの画面デザインを実装します。

    calculator/assets/main.css
    body {
    font-family: Arial, sans-serif;
    text-align: center;
    }
    #calculator {
    width: 300px;
    margin: 50px auto;
    border: 1px solid #ccc;
    padding: 20px;
    border-radius: 10px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    }
    button {
    width: 50px;
    height: 50px;
    font-size: 20px;
    margin: 5px;
    border: none;
    cursor: pointer;
    border-radius: 5px;
    background-color: #f0f0f0;
    }
    button:hover {
    background-color: #e0e0e0;
    }
    #result {
    margin-top: 20px;
    font-size: 24px;
    font-weight: bold;
    }
    
  3. プロジェクトを実行すると次の画面が表示されます。

ロジックの実装

  1. 式を評価するために使用する evalexpr を Cargo.toml > dependencies に追加します。

    calculator/Cargo.toml
    [dependencies]
    evalexpr = "11.3.0"
    
  2. 引数を State に追加する関数を実装します。

    calculator/src/main.rs
    fn appendToResult(ch: char)  {
       let mut result_state = consume_context::<Signal<String>>();
       let result = result_state();
    
       if result == "0" || result == "Error" {
          *result_state.write() = ch.to_string();
       } else {
          result_state.write().push(ch);
       }
    }
    
  3. State をクリアする関数を実装します。

    calculator/src/main.rs
    fn clearResult() {
       let mut result_state = consume_context::<Signal<String>>();
       *result_state.write() = "0".to_string();
    }
    
  4. 式を評価して State に書き込む関数を実装します。

    calculator/src/main.rs
    fn calculate() {
       let mut result_state = consume_context::<Signal<String>>();
       let evaluated = eval(result_state().as_str());
    
       match evaluated {
          Ok(v) => {
                log::info!("Evaluated: {:?}", v);
                *result_state.write() = v.to_string();
          },
          Err(e) => {
                log::error!("Error: {:?}", e);
                *result_state.write() = "Error".to_string();
          }
       }
    }
    
  5. ボタンを押下したら対応する関数をコールするように App コンポーネントを修正します。

    calculator/src/main.rs
    #[component]
    fn App() -> Element {
       use_context_provider(|| Signal::new("0".to_string()));
       let result_state = consume_context::<Signal<String>>();
    
       rsx! {
          link { rel: "stylesheet", href: "main.css" }
          div { id: "calculator",
                div { id: "result", "{result_state()}" }
                div {
                   button {
                      onclick: move |_| {
                            appendToResult('1');
                      },
                      "1"
                   }
                   button {
                      onclick: move |_| {
                            appendToResult('2');
                      },
                      "2"
                   }
                   button {
                      onclick: move |_| {
                            appendToResult('3');
                      },
                      "3"
                   }
                   button {
                      onclick: move |_| {
                            appendToResult('+');
                      },
                      "+"
                   }
                }
                div {
                   button {
                      onclick: move |_| {
                            appendToResult('4');
                      },
                      "4"
                   }
                   button {
                      onclick: move |_| {
                            appendToResult('5');
                      },
                      "5"
                   }
                   button {
                      onclick: move |_| {
                            appendToResult('6');
                      },
                      "6"
                   }
                   button {
                      onclick: move |_| {
                            appendToResult('-');
                      },
                      "-"
                   }
                }
                div {
                   button {
                      onclick: move |_| {
                            appendToResult('7');
                      },
                      "7"
                   }
                   button {
                      onclick: move |_| {
                            appendToResult('8');
                      },
                      "8"
                   }
                   button {
                      onclick: move |_| {
                            appendToResult('9');
                      },
                      "9"
                   }
                   button {
                      onclick: move |_| {
                            appendToResult('*');
                      },
                      "X"
                   }
                }
                div {
                   button {
                      onclick: move |_| {
                            appendToResult('0');
                      },
                      "0"
                   }
                   button {
                      onclick: move |_| {
                            clearResult();
                      },
                      "C"
                   }
                   button {
                      onclick: move |_| {
                            log::info!("= Button clicked");
                            calculate();
                      },
                      "="
                   }
                   button {
                      onclick: move |_| {
                            appendToResult('/');
                      },
                      "/"
                   }
                }
          }
       }
    }
    
  6. プロジェクトを実行すると次の画面が表示されます。ボタンを押下して計算することができます。

Discussion

gachagacha

大変参考になりました。
ちなみに、dioxus cli のインストールは以下で良くなったみたいです。。
cargo install dioxus-cli
dx --version
dioxus 0.5.4