Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: monisuo-dev
|
||||
description: Monisuo 项目结构化开发流程。需求分析 → 架构设计 → 开发 → 测试构建。
|
||||
description: Monisuo 项目结构化开发流程。需求分析 → 架构设计 → 模块化开发 → 精简测试 → 功能验证 → 构建。
|
||||
---
|
||||
|
||||
# Monisuo 开发工作流
|
||||
@@ -8,18 +8,25 @@ description: Monisuo 项目结构化开发流程。需求分析 → 架构设计
|
||||
## 流程概览
|
||||
|
||||
```
|
||||
Phase 1: 需求定义 → Phase 1.5: 架构设计 → Phase 2: 模块化开发
|
||||
Phase 1: 需求定义 → Phase 1.5: 架构设计 → Phase 2: 模块化开发(子任务逐步推进)
|
||||
↑ │
|
||||
│ ↓
|
||||
│ [Agent A] Phase 3: 精简 + 测试 + 构建
|
||||
│ 主对话:同步更新 Spec
|
||||
│ │
|
||||
│ ↓
|
||||
│ [Agent A] Phase 3: 精简 + 测试 → 输出审查报告
|
||||
│ │
|
||||
│ ↓ (通过)
|
||||
│ [Agent B] Phase 4: 功能验证(用例驱动)
|
||||
│ [Agent B] Phase 4: 功能验证(接收审查报告)
|
||||
│ │
|
||||
│ ↓ (通过)
|
||||
│ [Agent C] Phase 5: 构建
|
||||
│ │
|
||||
└──────────────── Bug 修复 ←──────────────────┘
|
||||
```
|
||||
|
||||
**Phase 1-1.5-2 在主对话完成,Phase 3 和 Phase 4 分别启动独立 Agent。**
|
||||
**Phase 1/1.5/2 在主对话完成,Phase 3/4/5 分别启动独立 Agent。**
|
||||
**所有 Agent prompt 中的 `[feature-name]` 由主对话替换为实际值后再传入。**
|
||||
|
||||
---
|
||||
|
||||
@@ -86,28 +93,38 @@ Phase 1: 需求定义 → Phase 1.5: 架构设计 → Phase 2: 模块化开发
|
||||
|
||||
## Phase 2: 模块化开发
|
||||
|
||||
依据 Spec 中的 API 契约和数据模型,自底向上开发:
|
||||
依据 Spec 中的 API 契约和数据模型,**按子模块逐步推进**,自底向上开发:
|
||||
|
||||
```
|
||||
1. 数据层 → 2. 后端 API → 3. 前端实现
|
||||
DDL/Entity Controller/Service Model/Provider/UI
|
||||
2a. 数据层 → 2b. 后端 API → 2c. Flutter 前端 → 2d. Admin 前端(如有)
|
||||
DDL/Entity Controller/Service Model/Provider/UI Vue Pages
|
||||
```
|
||||
|
||||
### 子模块执行方式
|
||||
- 每个子模块独立完成,不与其他子模块混在一起
|
||||
- 每完成一个子模块,确认无编译错误再进入下一个
|
||||
- 后端子模块完成后可先验证 API,前端再对接
|
||||
|
||||
### 代码规范
|
||||
- Flutter: `flutter analyze` 无错,用 AppSpacing/AppRadius/AppColorScheme,禁止硬编码颜色
|
||||
- Java: Lombok 简化,资金变动方法加 `@Transactional(rollbackFor = Exception.class)`,RESTful 设计
|
||||
|
||||
### Spec 同步
|
||||
Phase 2 全部完成后、启动 Agent 前,**主对话必须更新 Spec**:
|
||||
- 实际实现与设计有差异时,以实现为准更新 Spec
|
||||
- 确保 Phase 3/4 的 Agent 读到的是最新状态
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: 精简 + 测试 + 构建(Agent A)
|
||||
## Phase 3: 精简 + 测试(Agent A)
|
||||
|
||||
Phase 2 完成后,启动独立 Agent 执行代码精简、自动化测试和构建。
|
||||
Phase 2 完成且 Spec 已同步后,启动独立 Agent 执行代码精简和自动化测试。
|
||||
|
||||
### 启动 Agent A
|
||||
|
||||
```
|
||||
Agent(
|
||||
description: "精简+测试验证与构建",
|
||||
description: "精简+测试验证",
|
||||
prompt: |
|
||||
你是 Monisuo 项目质量 Agent,独立完成以下任务,不要询问用户。
|
||||
|
||||
@@ -129,19 +146,19 @@ Agent(
|
||||
- Flutter: cd flutter_monisuo && flutter test
|
||||
- API: ./tests/api/test-[feature].sh(如存在)
|
||||
|
||||
## 5. 构建
|
||||
- 后端: mvn clean package -DskipTests → target/monisuo-*.jar
|
||||
- Flutter: cd flutter_monisuo && flutter build web --release --dart-define=ENV=prod → build/web/
|
||||
## 5. 输出审查报告(供 Agent B 使用)
|
||||
### 代码审查摘要
|
||||
- 变更文件列表及每个文件的核心改动
|
||||
- 代码规范检查结果
|
||||
- 精简改动摘要(改了什么、为什么改)
|
||||
|
||||
## 6. 输出报告
|
||||
### 测试结果
|
||||
| 项目 | 状态 | 备注 |
|
||||
|------|------|------|
|
||||
| 代码审查 | ✅/❌ | |
|
||||
| 代码精简 | ✅/❌ | 改动摘要 |
|
||||
| 后端测试 | ✅/❌ | |
|
||||
| Flutter 测试 | ✅/❌ | |
|
||||
| 后端构建 | ✅/❌ | |
|
||||
| Flutter 构建 | ✅/❌ | |
|
||||
|
||||
如有 Bug,列出:文件、原因、修复建议。返回主对话修复后重新验证。
|
||||
)
|
||||
@@ -151,7 +168,7 @@ Agent(
|
||||
|
||||
## Phase 4: 功能验证(Agent B)
|
||||
|
||||
**Agent A 全部通过后**,启动第二个独立 Agent,基于 Spec 中的测试用例验证功能正确性。
|
||||
**Agent A 全部通过后**,启动独立 Agent。Agent B 接收 Agent A 的审查报告,避免重复读代码。
|
||||
|
||||
### 启动 Agent B
|
||||
|
||||
@@ -165,9 +182,9 @@ Agent(
|
||||
- 路径: docs/features/[feature-name].md
|
||||
- 提取「测试用例」和「验收标准」
|
||||
|
||||
## 2. 读取变更代码
|
||||
- git diff 查看本次变更的文件列表
|
||||
- 阅读变更的 Controller、Service、Provider、UI 代码
|
||||
## 2. 利用审查报告
|
||||
- Agent A 已完成代码审查,变更文件列表和核心改动见报告
|
||||
- 仅对报告未覆盖的逻辑路径做补充阅读
|
||||
- 对照 Spec 中的 API 设计,确认实现与契约一致
|
||||
|
||||
## 3. 逐条验证测试用例
|
||||
@@ -199,8 +216,37 @@ Agent(
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: 构建(Agent C)
|
||||
|
||||
**Agent A 和 Agent B 全部通过后**,启动独立 Agent 执行构建。
|
||||
|
||||
### 启动 Agent C
|
||||
|
||||
```
|
||||
Agent(
|
||||
description: "项目构建",
|
||||
prompt: |
|
||||
你是 Monisuo 项目构建 Agent,独立完成构建任务,不要询问用户。
|
||||
|
||||
## 1. 构建
|
||||
- 后端: mvn clean package -DskipTests → target/monisuo-*.jar
|
||||
- Flutter: cd flutter_monisuo && flutter build web --release --dart-define=ENV=prod → build/web/
|
||||
|
||||
## 2. 输出报告
|
||||
| 项目 | 状态 | 备注 |
|
||||
|------|------|------|
|
||||
| 后端构建 | ✅/❌ | 产物路径 |
|
||||
| Flutter 构建 | ✅/❌ | 产物路径 |
|
||||
|
||||
如有构建失败,列出:模块、错误信息、修复建议。返回主对话修复后重新构建。
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Bug 修复循环
|
||||
|
||||
- **Agent A 失败**(测试/构建不通过)→ 主对话修复 Bug → 重新启动 Agent A
|
||||
- **Agent A 失败**(测试不通过)→ 主对话修复 Bug → 重新启动 Agent A
|
||||
- **Agent B 失败**(功能验证不通过)→ 主对话修复逻辑 → 重新启动 Agent B
|
||||
- **Agent C 失败**(构建不通过)→ 主对话修复构建问题 → 重新启动 Agent C
|
||||
- **全部通过** → 开发完成
|
||||
|
||||
@@ -1,621 +0,0 @@
|
||||
---
|
||||
name: superdesign-flutter
|
||||
description: Flutter Design Skill - SuperDesign adapted for Flutter/Dart development. Optimized for Material Design 3 and Flutter widgets.
|
||||
---
|
||||
|
||||
# Flutter Design Skill
|
||||
|
||||
Use this skill when creating Flutter UI components, screens, or any Flutter UI design work.
|
||||
|
||||
This is an adapted version of SuperDesign for Flutter, converting web/CSS patterns to Flutter/Dart equivalents.
|
||||
|
||||
## Design Workflow
|
||||
|
||||
Follow this structured approach for Flutter UI design:
|
||||
|
||||
- **Layout Design** — Think through widget structure, create ASCII wireframes
|
||||
- **Theme Design** — Define colors, text styles, spacing, shadows
|
||||
- **Animation Design** — Plan micro-interactions and transitions
|
||||
- **Implementation** — Generate Flutter/Dart code
|
||||
|
||||
### 1. Layout Design
|
||||
|
||||
Before coding, sketch the widget tree in ASCII format:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ Scaffold / AppBar │
|
||||
├─────────────────────────────────────┤
|
||||
│ │
|
||||
│ Hero Section (Column) │
|
||||
│ - Title Text │
|
||||
│ - CTA ElevatedButton │
|
||||
│ │
|
||||
├─────────────────────────────────────┤
|
||||
│ Row (3x Feature Cards) │
|
||||
│ ┌─────┐ ┌─────┐ ┌─────┐ │
|
||||
│ │Card │ │Card │ │Card │ │
|
||||
│ └─────┘ └─────┘ └─────┘ │
|
||||
├─────────────────────────────────────┤
|
||||
│ BottomNavigationBar │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2. Theme Guidelines
|
||||
|
||||
#### Color Rules:
|
||||
|
||||
- NEVER use generic blue (Colors.blue) — it looks dated
|
||||
- Prefer Material Design 3 color system (ColorScheme)
|
||||
- Use Theme.of(context).colorScheme for semantic colors
|
||||
- Support both light and dark mode with ColorScheme
|
||||
|
||||
#### OKLCH to Flutter Color Conversion:
|
||||
|
||||
Convert OKLCH values to Flutter Color using this helper:
|
||||
|
||||
```dart
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Convert OKLCH to Flutter Color
|
||||
/// Example: oklch(0.205 0 0) → Color
|
||||
Color oklchToColor(double l, double c, double h) {
|
||||
// Simplified conversion - for accurate conversion use a color library
|
||||
// This is a placeholder implementation
|
||||
final hue = h;
|
||||
final chroma = c;
|
||||
final lightness = l;
|
||||
|
||||
// Convert to HSL then to RGB
|
||||
// For production, use package:flutter_color or similar
|
||||
return HSLColor.fromAHSL(1.0, hue, chroma, lightness).toColor();
|
||||
}
|
||||
|
||||
// Modern Dark Mode Colors (Vercel/Linear style)
|
||||
class ModernDarkColors {
|
||||
static const background = Color(0xFFFFFFFF); // oklch(1 0 0)
|
||||
static const foreground = Color(0xFF252525); // oklch(0.145 0 0)
|
||||
static const primary = Color(0xFF343434); // oklch(0.205 0 0)
|
||||
static const secondary = Color(0xFFF7F7F7); // oklch(0.970 0 0)
|
||||
static const muted = Color(0xFFF7F7F7); // oklch(0.970 0 0)
|
||||
static const border = Color(0xFFEBEBEB); // oklch(0.922 0 0)
|
||||
}
|
||||
```
|
||||
|
||||
#### Flutter Theme Setup:
|
||||
|
||||
```dart
|
||||
MaterialApp(
|
||||
theme: ThemeData(
|
||||
useMaterial3: true,
|
||||
colorScheme: ColorScheme.fromSeed(
|
||||
seedColor: Color(0xFF343434), // Modern neutral
|
||||
brightness: Brightness.light,
|
||||
),
|
||||
fontFamily: 'Inter',
|
||||
),
|
||||
darkTheme: ThemeData(
|
||||
useMaterial3: true,
|
||||
colorScheme: ColorScheme.fromSeed(
|
||||
seedColor: Color(0xFF343434),
|
||||
brightness: Brightness.dark,
|
||||
),
|
||||
fontFamily: 'Inter',
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
#### Font Selection (Google Fonts):
|
||||
|
||||
Use `google_fonts` package:
|
||||
|
||||
```yaml
|
||||
# pubspec.yaml
|
||||
dependencies:
|
||||
google_fonts: ^6.1.0
|
||||
```
|
||||
|
||||
```dart
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
// Sans-serif fonts
|
||||
final inter = GoogleFonts.inter();
|
||||
final roboto = GoogleFonts.roboto();
|
||||
final poppins = GoogleFonts.poppins();
|
||||
final montserrat = GoogleFonts.montserrat();
|
||||
final outfit = GoogleFonts.outfit();
|
||||
final dmSans = GoogleFonts.dmSans();
|
||||
|
||||
// Monospace fonts
|
||||
final jetBrainsMono = GoogleFonts.jetBrainsMono();
|
||||
final firaCode = GoogleFonts.firaCode();
|
||||
final sourceCodePro = GoogleFonts.sourceCodePro();
|
||||
```
|
||||
|
||||
#### Spacing System:
|
||||
|
||||
```dart
|
||||
/// Spacing scale based on 4.0 (instead of 0.25rem)
|
||||
class Spacing {
|
||||
static const double xs = 4.0;
|
||||
static const double sm = 8.0;
|
||||
static const double md = 16.0;
|
||||
static const double lg = 24.0;
|
||||
static const double xl = 32.0;
|
||||
static const double xxl = 48.0;
|
||||
}
|
||||
|
||||
// Usage
|
||||
Padding(
|
||||
padding: EdgeInsets.all(Spacing.md), // 16.0
|
||||
child: Text('Content'),
|
||||
)
|
||||
```
|
||||
|
||||
### 3. Theme Patterns
|
||||
|
||||
#### Modern Dark Mode (Vercel/Linear style):
|
||||
|
||||
```dart
|
||||
ThemeData(
|
||||
useMaterial3: true,
|
||||
colorScheme: ColorScheme(
|
||||
brightness: Brightness.light,
|
||||
background: Color(0xFFFFFFFF),
|
||||
onBackground: Color(0xFF252525),
|
||||
primary: Color(0xFF343434),
|
||||
onPrimary: Color(0xFFFBFBFB),
|
||||
secondary: Color(0xFFF7F7F7),
|
||||
onSecondary: Color(0xFF252525),
|
||||
surface: Color(0xFFFFFFFF),
|
||||
onSurface: Color(0xFF252525),
|
||||
outline: Color(0xFFEBEBEB),
|
||||
),
|
||||
cardTheme: CardTheme(
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10.0), // 0.625rem
|
||||
),
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
#### Neo-Brutalism (90s revival):
|
||||
|
||||
```dart
|
||||
ThemeData(
|
||||
useMaterial3: true,
|
||||
colorScheme: ColorScheme(
|
||||
brightness: Brightness.light,
|
||||
background: Color(0xFFFFFFFF),
|
||||
onBackground: Color(0xFF000000),
|
||||
primary: Color(0xFFFF6B35), // Vibrant orange
|
||||
secondary: Color(0xFF4ECDC4), // Teal
|
||||
tertiary: Color(0xFF5B5F97), // Purple
|
||||
outline: Color(0xFF000000),
|
||||
),
|
||||
cardTheme: CardTheme(
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(0), // No radius
|
||||
side: BorderSide(color: Colors.black, width: 2),
|
||||
),
|
||||
),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
elevation: 0,
|
||||
shadowColor: Colors.transparent,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(0),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
#### Glassmorphism:
|
||||
|
||||
```dart
|
||||
class GlassCard extends StatelessWidget {
|
||||
final Widget child;
|
||||
|
||||
const GlassCard({required this.child});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(16.0),
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.1),
|
||||
border: Border.all(
|
||||
color: Colors.white.withOpacity(0.2),
|
||||
width: 1,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(16.0),
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Animation Guidelines
|
||||
|
||||
#### Flutter Animation Equivalents:
|
||||
|
||||
```dart
|
||||
// Button press animation (150ms)
|
||||
class AnimatedButton extends StatefulWidget {
|
||||
@override
|
||||
_AnimatedButtonState createState() => _AnimatedButtonState();
|
||||
}
|
||||
|
||||
class _AnimatedButtonState extends State<AnimatedButton>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _controller;
|
||||
late Animation<double> _scaleAnimation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = AnimationController(
|
||||
duration: Duration(milliseconds: 150),
|
||||
vsync: this,
|
||||
);
|
||||
_scaleAnimation = Tween<double>(begin: 1.0, end: 0.95).animate(
|
||||
CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTapDown: (_) => _controller.forward(),
|
||||
onTapUp: (_) => _controller.reverse(),
|
||||
onTapCancel: () => _controller.reverse(),
|
||||
child: ScaleTransition(
|
||||
scale: _scaleAnimation,
|
||||
child: ElevatedButton(...),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Fade in animation (400ms)
|
||||
class FadeInWidget extends StatefulWidget {
|
||||
final Widget child;
|
||||
const FadeInWidget({required this.child});
|
||||
@override
|
||||
_FadeInWidgetState createState() => _FadeInWidgetState();
|
||||
}
|
||||
|
||||
class _FadeInWidgetState extends State<FadeInWidget>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _controller;
|
||||
late Animation<double> _opacity;
|
||||
late Animation<Offset> _slide;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = AnimationController(
|
||||
duration: Duration(milliseconds: 400),
|
||||
vsync: this,
|
||||
);
|
||||
_opacity = Tween<double>(begin: 0.0, end: 1.0).animate(_controller);
|
||||
_slide = Tween<Offset>(begin: Offset(0, 0.05), end: Offset.zero)
|
||||
.animate(CurvedAnimation(parent: _controller, curve: Curves.easeOut));
|
||||
_controller.forward();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SlideTransition(
|
||||
position: _slide,
|
||||
child: FadeTransition(opacity: _opacity, child: widget.child),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Common Animation Durations:
|
||||
|
||||
```dart
|
||||
class AnimationDurations {
|
||||
static const Duration buttonPress = Duration(milliseconds: 150);
|
||||
static const Duration hover = Duration(milliseconds: 200);
|
||||
static const Duration fadeIn = Duration(milliseconds: 400);
|
||||
static const Duration slideIn = Duration(milliseconds: 350);
|
||||
static const Duration bounce = Duration(milliseconds: 600);
|
||||
static const Duration pageTransition = Duration(milliseconds: 300);
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Widget Patterns
|
||||
|
||||
#### Responsive Layout:
|
||||
|
||||
```dart
|
||||
class ResponsiveLayout extends StatelessWidget {
|
||||
final Widget mobile;
|
||||
final Widget? tablet;
|
||||
final Widget? desktop;
|
||||
|
||||
const ResponsiveLayout({
|
||||
required this.mobile,
|
||||
this.tablet,
|
||||
this.desktop,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
if (constraints.maxWidth >= 1024) {
|
||||
return desktop ?? tablet ?? mobile;
|
||||
} else if (constraints.maxWidth >= 768) {
|
||||
return tablet ?? mobile;
|
||||
} else {
|
||||
return mobile;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
ResponsiveLayout(
|
||||
mobile: MobileLayout(),
|
||||
tablet: TabletLayout(),
|
||||
desktop: DesktopLayout(),
|
||||
)
|
||||
```
|
||||
|
||||
#### Icons:
|
||||
|
||||
```dart
|
||||
// Use Material Icons (built-in)
|
||||
Icon(Icons.home)
|
||||
Icon(Icons.settings)
|
||||
|
||||
// Or use Cupertino Icons
|
||||
Icon(CupertinoIcons.house)
|
||||
Icon(CupertinoIcons.settings)
|
||||
|
||||
// For custom icons, use flutter_svg
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
SvgPicture.asset('assets/icons/custom_icon.svg')
|
||||
```
|
||||
|
||||
#### Images:
|
||||
|
||||
```dart
|
||||
// Network images with error handling
|
||||
Image.network(
|
||||
'https://images.unsplash.com/photo-xxx?w=800&h=600',
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return Icon(Icons.error);
|
||||
},
|
||||
loadingBuilder: (context, child, loadingProgress) {
|
||||
if (loadingProgress == null) return child;
|
||||
return CircularProgressIndicator();
|
||||
},
|
||||
)
|
||||
|
||||
// Asset images
|
||||
Image.asset('assets/images/placeholder.png')
|
||||
|
||||
// Use cached_network_image for better performance
|
||||
CachedNetworkImage(
|
||||
imageUrl: 'https://images.unsplash.com/photo-xxx?w=800&h=600',
|
||||
placeholder: (context, url) => CircularProgressIndicator(),
|
||||
errorWidget: (context, url, error) => Icon(Icons.error),
|
||||
)
|
||||
```
|
||||
|
||||
### 6. Accessibility
|
||||
|
||||
```dart
|
||||
// Semantic labels for screen readers
|
||||
Semantics(
|
||||
label: 'Submit button',
|
||||
button: true,
|
||||
enabled: true,
|
||||
child: ElevatedButton(...),
|
||||
)
|
||||
|
||||
// Minimum touch target (44x44)
|
||||
MaterialButton(
|
||||
minWidth: 44,
|
||||
height: 44,
|
||||
child: Text('Button'),
|
||||
onPressed: () {},
|
||||
)
|
||||
|
||||
// Sufficient color contrast (4.5:1 minimum)
|
||||
// Use tools like https://webaim.org/resources/contrastchecker/
|
||||
```
|
||||
|
||||
### 7. Component Design Tips
|
||||
|
||||
#### Cards:
|
||||
|
||||
```dart
|
||||
Card(
|
||||
elevation: 2, // Subtle shadow
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.0), // p-4
|
||||
child: Column(...),
|
||||
),
|
||||
)
|
||||
|
||||
// Hover effect (web only)
|
||||
InkWell(
|
||||
onTap: () {},
|
||||
hoverColor: Colors.transparent,
|
||||
splashColor: Colors.transparent,
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: AnimatedContainer(
|
||||
duration: Duration(milliseconds: 200),
|
||||
transform: Matrix4.identity()..translate(0.0, _isHovered ? -2.0 : 0.0),
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(_isHovered ? 0.15 : 0.1),
|
||||
blurRadius: _isHovered ? 12 : 8,
|
||||
offset: Offset(0, _isHovered ? 4 : 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Card(...),
|
||||
),
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
#### Buttons:
|
||||
|
||||
```dart
|
||||
// Primary button
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: Size(44, 44), // Touch target
|
||||
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
),
|
||||
onPressed: () {},
|
||||
child: Text('Primary'),
|
||||
)
|
||||
|
||||
// Secondary button
|
||||
OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
minimumSize: Size(44, 44),
|
||||
),
|
||||
onPressed: () {},
|
||||
child: Text('Secondary'),
|
||||
)
|
||||
|
||||
// Ghost button
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
minimumSize: Size(44, 44),
|
||||
),
|
||||
onPressed: () {},
|
||||
child: Text('Ghost'),
|
||||
)
|
||||
```
|
||||
|
||||
#### Forms:
|
||||
|
||||
```dart
|
||||
TextFormField(
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Email', // Clear label above input
|
||||
border: OutlineInputBorder(),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Theme.of(context).colorScheme.primary, width: 2),
|
||||
),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Theme.of(context).colorScheme.error),
|
||||
),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Please enter email';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
)
|
||||
|
||||
// Spacing between fields
|
||||
SizedBox(height: 16.0)
|
||||
```
|
||||
|
||||
#### Navigation:
|
||||
|
||||
```dart
|
||||
// Bottom navigation bar
|
||||
BottomNavigationBar(
|
||||
currentIndex: _currentIndex,
|
||||
onTap: (index) => setState(() => _currentIndex = index),
|
||||
items: [
|
||||
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
|
||||
BottomNavigationBarItem(icon: Icon(Icons.settings), label: 'Settings'),
|
||||
],
|
||||
)
|
||||
|
||||
// Navigation Rail (for tablets)
|
||||
NavigationRail(
|
||||
selectedIndex: _selectedIndex,
|
||||
onDestinationSelected: (index) => setState(() => _selectedIndex = index),
|
||||
destinations: [
|
||||
NavigationRailDestination(icon: Icon(Icons.home), label: Text('Home')),
|
||||
NavigationRailDestination(icon: Icon(Icons.settings), label: Text('Settings')),
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
### 8. Flutter-Specific Best Practices
|
||||
|
||||
#### Performance:
|
||||
|
||||
```dart
|
||||
// Use const constructors when possible
|
||||
const Text('Static text')
|
||||
const SizedBox(height: 16)
|
||||
|
||||
// Use ListView.builder for long lists
|
||||
ListView.builder(
|
||||
itemCount: items.length,
|
||||
itemBuilder: (context, index) {
|
||||
return ListTile(title: Text(items[index]));
|
||||
},
|
||||
)
|
||||
|
||||
// Avoid rebuilding widgets with const
|
||||
const MyStaticWidget()
|
||||
```
|
||||
|
||||
#### State Management:
|
||||
|
||||
```dart
|
||||
// Simple state with StatefulWidget
|
||||
setState(() {
|
||||
_counter++;
|
||||
});
|
||||
|
||||
// For complex state, use provider, riverpod, or bloc
|
||||
// Example with provider:
|
||||
Consumer<MyModel>(
|
||||
builder: (context, model, child) {
|
||||
return Text('${model.value}');
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Element | Flutter Recommendation |
|
||||
|---------|------------------------|
|
||||
| Primary font | Inter, Outfit, DM Sans (google_fonts) |
|
||||
| Code font | JetBrains Mono, Fira Code (google_fonts) |
|
||||
| Border radius | 8.0 - 16.0 (modern), 0 (brutalist) |
|
||||
| Shadow | BoxShadow with elevation 2-4 |
|
||||
| Spacing | 4.0 base unit (Spacing class) |
|
||||
| Animation | 150-400ms, Curves.easeOut |
|
||||
| Colors | ColorScheme.fromSeed (Material 3) |
|
||||
| Touch targets | Minimum 44x44 |
|
||||
|
||||
---
|
||||
|
||||
Based on SuperDesign patterns — https://superdesign.dev
|
||||
Adapted for Flutter/Dart — Material Design 3
|
||||
Reference in New Issue
Block a user