robius-widget-patterns
CRITICAL: Use for Robius widget patterns. Triggers on: apply_over, TextOrImage, modal, 可复用, 模态, collapsible, drag drop, reusable widget, widget design, pageflip, 组件设计, 组件模式
Install
mkdir -p .claude/skills/robius-widget-patterns && curl -L -o skill.zip "https://mcp.directory/api/skills/download/3874" && unzip -o skill.zip -d .claude/skills/robius-widget-patterns && rm skill.zipInstalls to .claude/skills/robius-widget-patterns
About this skill
Robius Widget Patterns Skill
Best practices for designing reusable Makepad widgets based on Robrix and Moly codebase patterns.
Source codebases:
- Robrix: Matrix chat client - Avatar, RoomsList, RoomScreen widgets
- Moly: AI chat application - Slot, ChatLine, PromptInput, AdaptiveView widgets
Triggers
Use this skill when:
- Creating reusable Makepad widgets
- Designing widget component APIs
- Implementing text/image toggle patterns
- Dynamic styling in Makepad
- Keywords: robrix widget, makepad component, reusable widget, widget design pattern
Production Patterns
For production-ready widget patterns, see the _base/ directory:
| Pattern | Description |
|---|---|
| 01-widget-extension | Add helper methods to widget references |
| 02-modal-overlay | Popups, dialogs using DrawList2d overlay |
| 03-collapsible | Expandable/collapsible sections |
| 04-list-template | Dynamic lists with LivePtr templates |
| 05-lru-view-cache | Memory-efficient view caching |
| 14-callout-tooltip | Tooltips with arrow positioning |
| 20-redraw-optimization | Efficient redraw patterns |
| 15-dock-studio-layout | IDE-style resizable panels |
| 16-hover-effect | Hover effects with instance variables |
| 17-row-based-grid-layout | Dynamic grid layouts |
| 18-drag-drop-reorder | Drag-and-drop widget reordering |
| 19-pageflip-optimization | PageFlip 切换优化,即刻销毁/缓存模式 |
| 21-collapsible-row-portal-list | Auto-grouping consecutive items in portal lists with FoldHeader |
| 22-dropdown-overlay | Dropdown popups using DrawList2d overlay (no layout push) |
Standard Widget Structure
use makepad_widgets::*;
live_design! {
use link::theme::*;
use link::widgets::*;
pub MyWidget = {{MyWidget}} {
width: Fill, height: Fit,
flow: Down,
// Child widgets defined in DSL
inner_view = <View> {
// ...
}
}
}
#[derive(Live, LiveHook, Widget)]
pub struct MyWidget {
#[deref] view: View, // Delegate to inner View
#[live] some_property: f64, // DSL-configurable property
#[live(100.0)] default_val: f64, // With default value
#[rust] internal_state: State, // Rust-only state (not in DSL)
#[animator] animator: Animator, // For animations
}
impl Widget for MyWidget {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
self.view.handle_event(cx, event, scope);
// Custom event handling...
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
self.view.draw_walk(cx, scope, walk)
}
}
Text/Image Toggle Pattern
A common pattern for widgets that show either text or an image (like avatars):
live_design! {
pub Avatar = {{Avatar}} {
width: 36.0, height: 36.0,
align: { x: 0.5, y: 0.5 }
flow: Overlay, // Stack views on top of each other
text_view = <View> {
visible: true, // Default visible
show_bg: true,
draw_bg: {
uniform background_color: #888888
fn pixel(self) -> vec4 {
let sdf = Sdf2d::viewport(self.pos * self.rect_size);
let c = self.rect_size * 0.5;
sdf.circle(c.x, c.x, c.x)
sdf.fill_keep(self.background_color);
return sdf.result
}
}
text = <Label> {
text: "?"
}
}
img_view = <View> {
visible: false, // Hidden by default
img = <Image> {
fit: Stretch,
width: Fill, height: Fill,
}
}
}
}
#[derive(LiveHook, Live, Widget)]
pub struct Avatar {
#[deref] view: View,
#[rust] info: Option<UserInfo>,
}
impl Avatar {
/// Show text content, hiding the image
pub fn show_text<T: AsRef<str>>(
&mut self,
cx: &mut Cx,
bg_color: Option<Vec4>,
info: Option<AvatarTextInfo>,
username: T,
) {
self.info = info.map(|i| i.into());
// Get first character
let first_char = utils::first_letter(username.as_ref())
.unwrap_or("?").to_uppercase();
self.label(ids!(text_view.text)).set_text(cx, &first_char);
// Toggle visibility
self.view(ids!(text_view)).set_visible(cx, true);
self.view(ids!(img_view)).set_visible(cx, false);
// Apply optional background color
if let Some(color) = bg_color {
self.view(ids!(text_view)).apply_over(cx, live! {
draw_bg: { background_color: (color) }
});
}
}
/// Show image content, hiding the text
pub fn show_image<F, E>(
&mut self,
cx: &mut Cx,
info: Option<AvatarImageInfo>,
image_set_fn: F,
) -> Result<(), E>
where
F: FnOnce(&mut Cx, ImageRef) -> Result<(), E>
{
let img_ref = self.image(ids!(img_view.img));
let res = image_set_fn(cx, img_ref);
if res.is_ok() {
self.view(ids!(img_view)).set_visible(cx, true);
self.view(ids!(text_view)).set_visible(cx, false);
self.info = info.map(|i| i.into());
}
res
}
/// Check current display status
pub fn status(&mut self) -> DisplayStatus {
if self.view(ids!(img_view)).visible() {
DisplayStatus::Image
} else {
DisplayStatus::Text
}
}
}
Dynamic Styling with apply_over
Apply dynamic styles at runtime:
// Apply single property
self.view(ids!(content)).apply_over(cx, live! {
draw_bg: { color: #ff0000 }
});
// Apply multiple properties
self.view(ids!(message)).apply_over(cx, live! {
padding: { left: 20, right: 20 }
margin: { top: 10 }
});
// Apply with variables
let highlight_color = if is_selected { vec4(1.0, 0.0, 0.0, 1.0) } else { vec4(0.5, 0.5, 0.5, 1.0) };
self.view(ids!(item)).apply_over(cx, live! {
draw_bg: { color: (highlight_color) }
});
Widget Reference Pattern
Implement *Ref methods for external API:
impl AvatarRef {
/// See [`Avatar::show_text()`].
pub fn show_text<T: AsRef<str>>(
&self,
cx: &mut Cx,
bg_color: Option<Vec4>,
info: Option<AvatarTextInfo>,
username: T,
) {
if let Some(mut inner) = self.borrow_mut() {
inner.show_text(cx, bg_color, info, username);
}
}
/// See [`Avatar::show_image()`].
pub fn show_image<F, E>(
&self,
cx: &mut Cx,
info: Option<AvatarImageInfo>,
image_set_fn: F,
) -> Result<(), E>
where
F: FnOnce(&mut Cx, ImageRef) -> Result<(), E>
{
if let Some(mut inner) = self.borrow_mut() {
inner.show_image(cx, info, image_set_fn)
} else {
Ok(())
}
}
}
Collapsible/Expandable Pattern
live_design! {
pub CollapsibleSection = {{CollapsibleSection}} {
flow: Down,
header = <View> {
cursor: Hand,
icon = <Icon> { }
title = <Label> { text: "Section" }
}
content = <View> {
visible: false,
// Expandable content here
}
}
}
#[derive(Live, LiveHook, Widget)]
pub struct CollapsibleSection {
#[deref] view: View,
#[rust] is_expanded: bool,
}
impl CollapsibleSection {
pub fn toggle(&mut self, cx: &mut Cx) {
self.is_expanded = !self.is_expanded;
self.view(ids!(content)).set_visible(cx, self.is_expanded);
// Rotate icon
let rotation = if self.is_expanded { 90.0 } else { 0.0 };
self.view(ids!(header.icon)).apply_over(cx, live! {
draw_icon: { rotation: (rotation) }
});
self.redraw(cx);
}
}
Loading State Pattern
live_design! {
pub LoadableContent = {{LoadableContent}} {
flow: Overlay,
content = <View> {
visible: true,
// Main content
}
loading_overlay = <View> {
visible: false,
show_bg: true,
draw_bg: { color: #00000088 }
align: { x: 0.5, y: 0.5 }
<BouncingDots> { }
}
error_view = <View> {
visible: false,
error_label = <Label> { }
}
}
}
#[derive(Live, LiveHook, Widget)]
pub struct LoadableContent {
#[deref] view: View,
#[rust] state: LoadingState,
}
pub enum LoadingState {
Idle,
Loading,
Loaded,
Error(String),
}
impl LoadableContent {
pub fn set_state(&mut self, cx: &mut Cx, state: LoadingState) {
self.state = state;
match &self.state {
LoadingState::Idle | LoadingState::Loaded => {
self.view(ids!(content)).set_visible(cx, true);
self.view(ids!(loading_overlay)).set_visible(cx, false);
self.view(ids!(error_view)).set_visible(cx, false);
}
LoadingState::Loading => {
self.view(ids!(content)).set_visible(cx, true);
self.view(ids!(loading_overlay)).set_visible(cx, true);
self.view(ids!(error_view)).set_visible(cx, false);
}
LoadingState::Error(msg) => {
self.view(ids!(content)).set_visible(cx, false);
self.view(ids!(loading_overlay)).set_visible(
---
*Content truncated.*
More by ZhangHanDong
View all skills by ZhangHanDong →You might also like
flutter-development
aj-geddes
Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.
drawio-diagrams-enhanced
jgtolentino
Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.
ui-ux-pro-max
nextlevelbuilder
"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."
godot
bfollington
This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.
nano-banana-pro
garg-aayush
Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.
fastapi-templates
wshobson
Create production-ready FastAPI projects with async patterns, dependency injection, and comprehensive error handling. Use when building new FastAPI applications or setting up backend API projects.
Related MCP Servers
Browse all serversn8n offers conversational workflow automation, enabling seamless software workflow creation and management without platf
Securely join MySQL databases with Read MySQL for read-only query access and in-depth data analysis.
Context Portal: Manage project memory with a database-backed system for decisions, tracking, and semantic search via a k
Dot AI (Kubernetes Deployment) streamlines and automates Kubernetes deployment with intelligent guidance and vector sear
Claude Historian is a free AI search engine offering advanced search, file context, and solution discovery in Claude Cod
Claude Historian: AI-powered search for Claude Code conversations—find files, errors, context, and sessions via JSONL pa
Stay ahead of the MCP ecosystem
Get weekly updates on new skills and servers.