- 新增 cold_wallet 表结构及默认数据 - 补充 order_fund 表字段(wallet_id, wallet_address, pay_time, confirm_time, withdraw_contact) - 创建数据库补丁脚本 sql/patch_cold_wallet.sql - 创建充值功能测试脚本 test_deposit_api.sh - 创建数据库检查脚本 check_database.sh - 更新充值功能检查报告 check_cold_wallet.md 修复问题:充值功能因缺少冷钱包表而无法使用
625 lines
16 KiB
Markdown
625 lines
16 KiB
Markdown
---
|
|
name: superdesign-flutter
|
|
description: Flutter Design Skill - SuperDesign adapted for Flutter/Dart development. Optimized for Material Design 3 and Flutter widgets.
|
|
version: 1.0.0
|
|
original: superdesign
|
|
adapted_for: Flutter/Dart
|
|
---
|
|
|
|
# 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
|