使い方
- インストールコマンドをコピーし、自分の環境へ導入する前にソースを確認します。
- 必要なツール、認証情報、対応環境を原文で確認します。
- 入力と成果物を指定し、出力を資料と照合します。
npx skills add https://github.com/karanb05/mobile-flutter-design-skill --skill mobile-flutter-design開始前の確認
- 導入にはモデル契約、API枠、ファイルへのアクセス許可は含まれません。
- スター数や導入数は活動の指標であり、独立した品質・安全性試験ではありません。
見出し、例、コード、表、リンク、参照画像を含む原文を表示しています。
Flutter iOS Design
Adapts Apple's Human Interface Guidelines (HIG) to Flutter. The HIG principles are framework-agnostic; this skill re-expresses them in idiomatic Flutter so a Flutter app can feel native on iOS without leaving Dart. Converted from the SwiftUI-based mobile-ios-design skill.
When to Use This Skill
- Designing/refining Flutter screens that must feel native on iOS
- Choosing Cupertino vs. Material widgets for an iOS-leaning app
- Getting spacing, typography, and hierarchy to HIG standards in Flutter
- Implementing iOS-style navigation (stacks, tabs, bottom sheets) in Flutter
- Supporting Dynamic Type (text scaling), Dark Mode, and accessibility
- Making glanceable, uncluttered dashboards and lists
Cupertino vs. Material — the first decision
Flutter ships two design languages. Pick deliberately:
- Material (`MaterialApp`, `Scaffold`, `NavigationBar`) — the pragmatic
default for cross-platform apps. Looks clean on iOS; not pixel-native but users accept it. If the app already commits to Material, stay in Material and apply HIG principles (spacing, hierarchy, restraint) rather than swapping widgets. Don't mix half-and-half — inconsistency reads as broken.
- Cupertino (`CupertinoApp`, `CupertinoPageScaffold`, `CupertinoNavigationBar`)
— pixel-native iOS look (system fonts, iOS transitions, sliding back gesture). Use when "feels exactly like an Apple app" is a hard requirement, or for a specific surface (a settings screen, an action sheet).
- Hybrid — Material shell + Cupertino for specific interactions
(showCupertinoModalPopup for action sheets, CupertinoContextMenu). Fine when intentional and consistent.
This project (FieldFlowSales mobile) is a Material app (NavigationBar,showModalBottomSheet, custom cards). Apply HIG at the principle level here; reach for Cupertino widgets only for genuinely iOS-idiomatic moments.
Core Concepts
1. HIG Principles (framework-agnostic)
- Clarity — legible text, precise icons, subtle adornment. Prefer content
over chrome. In Flutter: lean on Theme.of(context).textTheme, keep borders and shadows light, avoid decorative gradients competing with content.
- Deference — the UI helps users understand content without competing with
it. Translate to restraint: one accent color per surface, generous whitespace, no card-in-card-in-card nesting.
- Depth — layers and motion convey hierarchy. In Flutter: elevation/shadow
for cards, showModalBottomSheet sliding up for transient tasks, hero/route transitions for drill-down.
2. Layout — SwiftUI → Flutter
| SwiftUI | Flutter |
|---|---|
VStack(alignment:spacing:) | Column(crossAxisAlignment:, children:[…, SizedBox(height:)…]) |
HStack | Row |
ZStack | Stack |
Spacer() | Spacer() or Expanded |
.padding() | Padding(padding: EdgeInsets…) / Container(padding:) |
LazyVStack / List | ListView / ListView.builder |
LazyVGrid(adaptive) | GridView.builder(gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(maxCrossAxisExtent: 200)) |
LazyVGrid(fixed 3) | GridView.count(crossAxisCount: 3) |
// VStack(alignment: .leading, spacing: 12) equivalent
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Title', style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 12),
Text('Subtitle',
style: Theme.of(context).textTheme.bodyMedium
?.copyWith(color: Theme.of(context).colorScheme.onSurfaceVariant)),
],
)
// HStack with Spacer + trailing accent
Row(
children: [
Icon(Icons.star, color: Colors.amber),
const SizedBox(width: 8),
const Text('Featured'),
const Spacer(),
Text('View All', style: TextStyle(color: Theme.of(context).colorScheme.primary)),
],
)Spacing scale: HIG leans on multiples of 4/8. Use a consistent scale (4, 8, 12, 16, 24) rather than arbitrary values. Standard screen edge padding is 16.
3. Navigation
| SwiftUI | Flutter |
|---|---|
NavigationStack + NavigationLink | Navigator + MaterialPageRoute (or go_router) |
.navigationTitle | AppBar(title:) |
TabView | Scaffold(bottomNavigationBar: NavigationBar) + IndexedStack |
.sheet | showModalBottomSheet |
.confirmationDialog | showModalBottomSheet of actions, or showCupertinoModalPopup |
.fullScreenCover | Navigator.push with fullscreenDialog: true |
Full patterns and code in references/flutter-navigation.md. Key HIG rule: push for drill-down (a back stack), sheet for a transient/self-contained task, tabs for peer sections. Keep tab pages alive with IndexedStack so switching doesn't rebuild/refetch (this app learned that the hard way — it fixed a tab flicker).
4. System Integration
- Icons: SF Symbols → Flutter
Icons(Material) orCupertinoIcons. For an
iOS feel use CupertinoIcons; for a Material app, Icons is consistent. Pick one set per app.
- Dynamic Type: SwiftUI scales automatically; Flutter needs care. Never
hardcode font sizes for body copy where you can use textTheme. Respect the user's setting via MediaQuery.textScalerOf(context) — don't clamp it to 1.0 (that breaks accessibility). Test at 200% text size; ensure rows grow, not clip.
- Haptics:
HapticFeedback.selectionClick()/.lightImpact()on
meaningful actions (toggle, pull-to-refresh complete) — iOS users expect it.
5. Visual Design
- Semantic colors (dark-mode safe): SwiftUI's
.primary/.secondary→
Flutter's ColorScheme. Use Theme.of(context).colorScheme.onSurface, onSurfaceVariant, surface, primary instead of literal Colors.black/ Colors.grey.shadeXXX. Literal colors are the #1 cause of broken dark mode. If the app is light-only today, still prefer colorScheme so dark mode is a theme change, not a rewrite.
- Materials/blur: SwiftUI
.ultraThinMaterial→BackdropFilterwith
ImageFilter.blur behind a translucent color (use sparingly; it's costly).
- Elevation: card shadow via `BoxShadow(color: Colors.black.withValues(alpha:
0.06), blurRadius: 8, offset: Offset(0, 4)) or Material(elevation:)`. HIG shadows are subtle — low opacity, soft blur. Avoid heavy drop shadows.
- Corner radius: 12 for cards, 20+ for pills/sheets, matching iOS softness.
Quick Start Component (SwiftUI FeatureCard → Flutter)
class FeatureCard extends StatelessWidget {
final String title;
final String description;
final IconData icon;
final VoidCallback? onTap;
const FeatureCard({
super.key,
required this.title,
required this.description,
required this.icon,
this.onTap,
});
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: scheme.surface,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.05),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: Row(
children: [
Container(
width: 44,
height: 44,
alignment: Alignment.center,
decoration: BoxDecoration(
color: scheme.primary.withValues(alpha: 0.1),
shape: BoxShape.circle,
),
child: Icon(icon, color: scheme.primary),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 4),
Text(
description,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodyMedium
?.copyWith(color: scheme.onSurfaceVariant),
),
],
),
),
Icon(Icons.chevron_right, color: scheme.outline),
],
),
),
);
}
}Best Practices (Flutter-adapted)
- Prefer `ColorScheme`/`textTheme` over literals — automatic dark mode and
consistent typography. Reserve literal colors for brand accents only.
- One icon set per app (
IconsorCupertinoIcons) — don't mix. - Respect text scaling — use
textTheme, don't hardcode body sizes, don't
clamp textScaler. Let rows grow.
- Add semantics — wrap icon-only buttons with
Semantics(label:)or use
IconButton(tooltip:); label meaningful images. Test with TalkBack/VoiceOver.
- Respect safe areas — wrap edge content in
SafeArea; don't hardcode
status-bar/home-indicator padding.
- Keep tab pages alive —
IndexedStack(orAutomaticKeepAliveClientMixin)
so switching tabs doesn't refetch and flicker.
- Lazy lists —
ListView.builder/GridView.builderfor long content, never
a Column of hundreds of children.
- Glanceable first — put what a user needs at a glance at the top; collapse
detail behind taps (e.g. a summary row that expands). Cards should be slim.
- Test on device — haptics, scroll physics, and true colors don't show in a
simulator screenshot.
Common Issues (Flutter)
- Broken dark mode — literal
Colors.black/Colors.grey.shadeXXX. Use
colorScheme.
- Clipped text at large font sizes — fixed heights on rows containing text;
let them size to content, use Flexible/Expanded.
- Jank on long lists — non-lazy
Column/ListView(children:[…]); switch to
.builder.
- Overflowing dialogs/sheets — unbounded content in a
Dialog; give the body
overflow-y via a scroll view and cap tall inner lists (this app hit exactly this in a modal — DialogContent was display:grid, so a flex child didn't scroll; the fix was overflow on the content itself + a height-capped list).
- Tab flicker / lost scroll — recreating tab pages on switch; use
IndexedStack.
- Nested-scroll gesture conflict — a scrollable inside a scrollable fighting
for the drag. Avoid unless the inner region genuinely needs its own scroll; prefer letting the page scroll.
References
references/flutter-navigation.md— stacks, tabs, bottom sheets, dialogs,
deep links, transitions (SwiftUI navigation → Flutter).
references/flutter-components.md— lists, forms/inputs, buttons/menus,
sheets, loading/skeletons, async images, animations, gestures (SwiftUI components → Flutter).

