
关于
Robius 状态管理参考,用于 Rust 跨平台 UI 框架中的状态管理模式。
name: robius-state-management description: | 关键: 用于 Robius 状态管理模式。触发条件: AppState, persistence, theme switch, 状态管理, Scope::with_data, save state, load state, serde, 状态持久化, 主题切换 risk: unknown source: community
Robius 状态管理技能
基于 Robrix 和 Moly 代码库的 Makepad 应用状态管理和持久化最佳实践。
源代码库:
- Robrix: Matrix 聊天客户端 - AppState、SelectedRoom、通过 serde 持久化
- Moly: AI 聊天应用 - 中央 Store 模式、异步初始化、Preferences
适用场景
在以下情况下使用此技能:
- 设计应用状态结构
- 实现状态持久化
- 通过 widget 树传递状态
- 跨会话管理 UI 状态
- 关键词: app state, makepad state, persistence, Scope::with_data, save state, load state
生产级模式
生产就绪的状态管理模式,参见 _base/ 目录:
| 模式 | 描述 | |------|------| | 06-global-registry | 使用 Cx::set_global 的全局 widget 注册表 | | 07-radio-navigation | 使用单选按钮的标签式导航 | | 10-state-machine | 基于枚举的状态机 widget | | 11-theme-switching | 使用 apply_over 的多主题支持 | | 12-local-persistence | 保存/加载用户偏好设置 |
AppState 结构
核心状态定义
use serde::{Serialize, Deserialize};
use std::collections::HashMap;
use matrix_sdk::ruma::OwnedRoomId;
/// App-wide state that is stored persistently across multiple app runs
/// and shared/updated across various parts of the app.
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
pub struct AppState {
/// The currently-selected room
pub selected_room: Option<SelectedRoom>,
/// Saved UI layout state for main view
pub saved_layout_state: SavedLayoutState,
/// Per-item saved states (e.g., per-space dock layouts)
pub saved_state_per_item: HashMap<OwnedRoomId, SavedLayoutState>,
/// Whether a user is currently logged in
#[serde(skip)] // Don't persist login state
pub logged_in: bool,
}
/// Represents a currently selected item
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum SelectedRoom {
JoinedRoom { room_name_id: RoomNameId },
InvitedRoom { room_name_id: RoomNameId },
Space { space_name_id: RoomNameId },
}
impl SelectedRoom {
pub fn room_id(&self) -> &OwnedRoomId {
match self {
Self::JoinedRoom { room_name_id } => room_name_id.room_id(),
Self::InvitedRoom { room_name_id } => room_name_id.room_id(),
Self::Space { space_name_id } => space_name_id.room_id(),
}
}
/// Upgrade from invited to joined state
pub fn upgrade_invite_to_joined(&mut self, room_id: &RoomId) -> bool {
match self {
Self::InvitedRoom { room_name_id } if room_name_id.room_id() == room_id => {
let name = room_name_id.clone();
*self = Self::JoinedRoom { room_name_id: name };
true
}
_ => false,
}
}
}
// Equality based on room_id only
impl PartialEq for SelectedRoom {
fn eq(&self, other: &Self) -> bool {
self.room_id() == other.room_id()
}
}
impl Eq for SelectedRoom {}
布局/Dock 状态持久化
/// A snapshot of UI layout state for restoration
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
pub struct SavedLayoutState {
/// All items contained in the layout, keyed by ID
pub layout_items: HashMap<LiveIdSerde, LayoutItemSerde>,
/// Items currently open, keyed by ID
pub open_items: HashMap<LiveIdSerde, SelectedRoom>,
/// Order items were opened (chronological)
pub item_order: Vec<SelectedRoom>,
/// Currently selected item when state was saved
pub selected_item: Option<SelectedRoom>,
}
/// Serializable wrapper for LiveId
#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct LiveIdSerde(pub u64);
impl From<LiveId> for LiveIdSerde {
fn from(id: LiveId) -> Self {
Self(id.0)
}
}
impl From<LiveIdSerde> for LiveId {
fn from(s: LiveIdSerde) -> Self {
LiveId(s.0)
}
}
通过 Scope 传播状态
通过 Widget 树传递状态
impl AppMain for App {
fn handle_event(&mut self, cx: &mut Cx, event: &Event) {
// Forward to MatchEvent
self.match_event(cx, event);
// Create Scope with AppState data
let scope = &mut Scope::with_data(&mut self.app_state);
// Pass to widget tree - all children can access AppState
self.ui.handle_event(cx, event, scope);
}
}
在子 Widget 中访问状态
impl Widget for RoomScreen {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
// Access AppState from scope
if let Some(app_state) = scope.data.get::<AppState>() {
if let Some(select
兼容工具
Claude CodeCursor
标签
前端开发