flutter-design について
mobile-app-design-mastery スキルを補完する Flutter・Dart 実装ガイドです。テーマ、タイポグラフィ、余白、ウィジェットのパターンでデザイン方針をコードに落とし込みます。
主な機能
- 01ThemeData と ColorScheme のパターン
- 02TextTheme によるタイポグラフィ指針
- 03再利用可能な余白定数
- 04BoxDecoration のレシピ
- 05ThemeExtension のパターン
活用シーン
- 01Flutter 画面へのデザイン方針の適用
- 02既存プロジェクトのテーマ設定の利用
- 03一貫したカードや入力欄の作成
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ anilcancakir/ my-claude-code --skill flutter-designインストール後、Claude Code で/flutter-designを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/extensions.mdtheming.mdwidgets.md詳細
Flutter Design Patterns
mobile-app-design-mastery スキルを補完する Flutter/Dart 実装ガイドです。Refactoring UI の原則を Flutter コードに反映します。
前提条件: このスキルでは Flutter 固有の構文を説明します。モバイルデザインの理論や意思決定については、
mobile-app-design-masteryスキルを参照してください。
⚠️ 重要:まずプロジェクトのテーマを確認する
新しいスタイルを作成する前に、既存のテーマ設定を必ず確認してください。
プロジェクトに ThemeData、AppColors、AppTextStyles などがある場合は、それらを使用してください。
// Check lib/core/theme/ or lib/config/
// Common patterns:
AppColors.primary
AppTextStyles.headline
AppSpacing.md
context.theme.colorScheme.primary
優先順位:
- プロジェクトで定義されたテーマ(AppColors、AppTextStyles、カスタム ThemeExtension)
- Theme.of(context) によるアクセス
- 最後の手段としてのみハードコード値を使用
ThemeData の設定(Material 3)
MaterialApp(
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blue, // Brand color
brightness: Brightness.light,
),
textTheme: _textTheme,
),
darkTheme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blue,
brightness: Brightness.dark,
),
textTheme: _textTheme,
),
);
色へのアクセスパターン
// ✅ CORRECT: Use ColorScheme
final colors = Theme.of(context).colorScheme;
Container(color: colors.primary)
Text('Hello', style: TextStyle(color: colors.onSurface))
// ✅ With extension
extension BuildContextX on BuildContext {
ColorScheme get colors => Theme.of(this).colorScheme;
}
// Usage: context.colors.primary
// ❌ AVOID: Hardcoded colors
Container(color: Colors.blue) // Ignores theme
ColorScheme の役割:
| 役割 | ライトモード | ダークモード | 用途 |
|---|---|---|---|
primary | ブランドカラー | 明るいブランドカラー | CTA、アクティブ状態 |
onPrimary | 白 | 暗色 | primary 上のテキスト |
surface | 白 | グレー-900 | カード、シート |
onSurface | グレー-900 | 白 | 本文 |
surfaceContainerHighest | グレー-100 | グレー-800 | 高さのあるサーフェス |
outline | グレー-400 | グレー-600 | 境界線 |
error | 赤 | 明るい赤 | エラー状態 |
タイポグラフィ(TextTheme)
Material 3 のタイプスケール:
| スタイル | サイズ(sp) | 用途 |
|---|---|---|
displayLarge | 57 | ヒーローテキスト |
displayMedium | 45 | 大きなディスプレイ |
displaySmall | 36 | ディスプレイ |
headlineLarge | 32 | 大見出し |
headlineMedium | 28 | ページタイトル |
headlineSmall | 24 | セクションタイトル |
titleLarge | 22 | カードタイトル |
titleMedium | 16 | サブタイトル |
titleSmall | 14 | 小さなタイトル |
bodyLarge | 16 | 強調する本文 |
bodyMedium | 14 | 標準の本文 |
bodySmall | 12 | 補足テキスト |
labelLarge | 14 | ボタン |
labelMedium | 12 | ラベル |
labelSmall | 11 | キャプション |
// ✅ CORRECT: Use TextTheme
Text('Title', style: Theme.of(context).textTheme.headlineMedium)
// With extension
extension BuildContextX on BuildContext {
TextTheme get textTheme => Theme.of(this).textTheme;
}
// Usage: context.textTheme.bodyMedium
余白システム(4dp グリッド)
再利用可能な余白定数を作成します。
abstract class AppSpacing {
static const double xs = 4;
static const double sm = 8;
static const double md = 12;
static const double base = 16;
static const double lg = 24;
static const double xl = 32;
static const double xxl = 48;
// SizedBox shortcuts
static const SizedBox gapXs = SizedBox(height: xs, width: xs);
static const SizedBox gapSm = SizedBox(height: sm, width: sm);
static const SizedBox gapMd = SizedBox(height: md, width: md);
static const SizedBox gapBase = SizedBox(height: base, width: base);
static const SizedBox gapLg = SizedBox(height: lg, width: lg);
}
// Usage
Padding(padding: EdgeInsets.all(AppSpacing.base))
Column(children: [widget1, AppSpacing.gapMd, widget2])
BoxDecoration のパターン
カード(フラット)
Container(
decoration: BoxDecoration(
color: colors.surface,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: colors.outline.withOpacity(0.2)),
),
)
カード(立体)
Container(
decoration: BoxDecoration(
color: colors.surface,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.08),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
)
入力フィールド
Container(
decoration: BoxDecoration(
color: colors.surface,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: colors.outline),
),
)
シャドウのスケール
abstract class AppShadows {
static List<BoxShadow> sm = [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 4,
offset: const Offset(0, 1),
),
];
static List<BoxShadow> md = [
BoxShadow(
color: Colors.black.withOpacity(0.08),
blurRadius: 8,
offset: const Offset(0, 2),
),
];
static List<BoxShadow> lg = [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 16,
offset: const Offset(0, 4),
),
];
static List<BoxShadow> xl = [
BoxShadow(
color: Colors.black.withOpacity(0.15),
blurRadius: 24,
offset: const Offset(0, 8),
),
];
}
// Usage
Container(decoration: BoxDecoration(boxShadow: AppShadows.md))
アンチパターン
// ❌ NEVER
Colors.blue // Hardcoded, ignores theme
TextStyle(fontSize: 16) // Not from TextTheme
SizedBox(height: 17) // Off 4dp grid
EdgeInsets.only(top: 20, left: 8) // Asymmetric without reason
// ✅ INSTEAD
context.colors.primary
context.textTheme.bodyLarge
SizedBox(height: 16) // On grid
EdgeInsets.all(16) // Symmetric
参考ファイル
| トピック | ファイル |
|---|---|
| ThemeData と ColorScheme | theming.md |
| ウィジェットのレシピ | widgets.md |
| ThemeExtension のパターン | extensions.md |
コメント
Markdown 対応