// name: ui-skills-flutter // description: Opinionated constraints for building better interfaces with agents in Dart/Flutter projects.
UI Skills (Flutter/Dart)
When invoked, apply these opinionated constraints to Flutter UI work.
How to use
- •
/ui-skillsApply these constraints to any UI work in this conversation. - •
/ui-skills <file or dir>Review the specified file(s) against all constraints and output:- •violations (quote exact line/snippet)
- •why it matters (1 short sentence)
- •a concrete fix (code-level suggestion)
Stack
- •MUST use Flutter Material 3 defaults (ThemeData(useMaterial3: true)) unless custom tokens already exist or are explicitly requested
- •SHOULD use
ColorSchemeand semantic tokens over hardcoded colors - •MUST use
flutter_animateorImplicitlyAnimatedwidgets (AnimatedOpacity,AnimatedScale, etc.) for micro/entrance animations - •MUST use a
build-time class composition helper for conditional styles (e.g. helper functions instead of stringly classes) - •SHOULD use
riverpodorflutter_riverpodfor state where testability and DI are needed; keepsetStatescoped to simple, local UI - •MUST respect
MediaQueryandViewPadding(safe areas) for fixed elements (SafeArea,SliverSafeArea)
Components
- •MUST use accessible widgets for anything with keyboard/focus behavior:
Focus,FocusTraversalGroup,Shortcuts,Actions - •MUST add
semanticsLabelfor icon-only buttons/icons (Semantics,IconButton(tooltip: ...)) - •MUST prefer existing project component primitives first (design system widgets)
- •NEVER mix primitive systems within the same interaction surface (e.g., two different shortcut/focus systems overlapping)
- •SHOULD use
NavigationBar/BottomAppBar/AppBarwith proper semantics and tooltips - •NEVER rebuild keyboard or focus behavior by hand unless explicitly requested—use
Shortcuts+Actions+Intentpattern orRawKeyboardListeneronly when necessary
Interaction
- •MUST use a
AlertDialogorDialogwith explicit confirmation for destructive/irreversible actions - •SHOULD use skeletons/placeholders for loading states (
Shimmeror simple grey boxes) rather than spinners in dense lists - •NEVER use full-screen fixed heights; prefer
MediaQuery.size.heightwithSafeAreaorLayoutBuilderconstraints - •MUST respect
safe-area(SafeAreaorPaddingfromMediaQuery.viewPadding) for fixed positioned elements - •MUST show errors next to where the action happens (inline
TextorHelperTextnear the control), not only via SnackBar - •NEVER block paste in
TextFieldorTextFormField(do not overrideinputFormattersto block paste)
Animation
- •NEVER add animation unless explicitly requested
- •MUST animate only compositor-friendly props:
opacity,transform(scale,translate,rotate) - •NEVER animate layout properties:
width,height,margin,padding, or expensive relayout triggers - •SHOULD avoid animating paint-heavy props (
BoxShadowblur, largeBackdropFilter) except small, local UI (icons, text) - •SHOULD use ease-out on entrance (
Curves.easeOut,Curves.easeOutCubic) - •NEVER exceed
200msfor interaction feedback; entrances typically120–200ms - •MUST pause or dispose looping animations when off-screen (
TickerMode,Visibility, or lifecycle-aware controllers) - •SHOULD respect
MediaQuery.of(context).disableAnimations/AccessibilityFeatures.disableAnimations(or equivalent) and provideDuration.zerofallbacks - •NEVER introduce custom cubic bezier curves unless explicitly requested
- •SHOULD avoid animating large images or full-screen surfaces
Typography
- •MUST balance headings (
TextwithtextAlign: TextAlign.startand wrapping) and useTextWidthBasis.longestLinewhere appropriate to avoid ragged text - •MUST use tabular numerals for data when using a font that supports it (e.g., set
FontFeature.tabularFigures()viaTextStyle.fontFeatures) - •SHOULD use
TextOverflow.ellipsisormaxLinesfor dense UI; preferLayoutBuilder+RichTextfor complex clamps - •NEVER modify letter-spacing (
TextStyle.letterSpacing) unless explicitly requested
Layout
- •MUST use a fixed
z-index-like layering viaStackorder or a documented elevation scale; avoid arbitraryStackindex juggling - •SHOULD use square sizing via
SizedBox.squareorConstrainedBoxinstead of separately setting width + height - •SHOULD use
Sliversfor scrollable complex layouts; avoid nesting scroll views that fight each other - •MUST use
Padding/SizedBox/Spacerfor spacing; avoid magic numbers—document spacing tokens in Theme
Performance
- •NEVER animate large
BackdropFilterorImageFiltered.blursurfaces - •NEVER apply
RepaintBoundary/willChange-like hints outside an active animation; useRepaintBoundaryonly where profiling shows benefit - •NEVER use
setState/useEffect-equivalents (e.g.,addPostFrameCallback) for logic that can be expressed declaratively inbuildwith state providers/selectors - •SHOULD memoize expensive widgets with
constconstructors andconststyles - •MUST avoid rebuilding heavy lists; prefer
ListView.builder/SliverListwith keyed items
Design
- •NEVER use gradients unless explicitly requested
- •NEVER use purple or multicolor gradients
- •NEVER use glow effects as primary affordances
- •SHOULD use Material default shadow/elevation scale unless explicitly requested
- •MUST give empty states one clear next action (primary button or link)
- •SHOULD limit accent color usage to one per view
- •SHOULD use existing theme or
ColorSchemetokens before introducing new ones
Example fixes (snippets)
- •
Icon-only button missing semantics
// violations: // IconButton(icon: Icon(Icons.delete)) // why it matters: // Screen readers need labels for non-text controls. // fix: IconButton( icon: const Icon(Icons.delete), tooltip: '削除', onPressed: handleDelete, )
- •
Unsafe fixed element without safe-area
// violations: // Positioned(bottom: 0, child: MyBar()) // why it matters: // Overlaps home indicator / notch on modern devices. // fix: SafeArea( minimum: EdgeInsets.zero, child: Align( alignment: Alignment.bottomCenter, child: MyBar(), ), )
- •
Animating layout property (height)
// violations: // AnimatedContainer(duration: Duration(milliseconds: 400), height: expanded ? 300 : 100) // why it matters: // Layout animation is janky; use transform/opacity. // fix: AnimatedOpacity( duration: const Duration(milliseconds: 180), curve: Curves.easeOut, opacity: expanded ? 1 : 0, child: Transform.scale( scale: expanded ? 1.0 : 0.96, child: const MyContent(), ), )
- •
Error placement far from the action
// violations: // ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('エラー'))) // why it matters: // Users may miss context; error should be near the control. // fix: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ TextField( controller: controller, decoration: const InputDecoration(labelText: '金額'), onChanged: validate, ), if (error != null) Padding( padding: const EdgeInsets.only(top: 8), child: Text(error!, style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Theme.of(context).colorScheme.error)), ), ], )
Notes
- •Treat any non-adopted stack items as Not Applicable and map principles to Flutter equivalents.
- •Keep interactions consistent with a single primitives system (Material or your design system), and document exceptions explicitly.