WIP: feat(ui): Migrate to new UI implementation

This commit is contained in:
2026-06-22 06:47:16 +04:00
parent 873aa8d7b5
commit bb4334f68d
109 changed files with 13499 additions and 1257 deletions
+330
View File
@@ -0,0 +1,330 @@
# Widgetbook Use-Case Generation Instructions
## Workspace And Asset Contract
The catalog is a standalone Flutter workspace under `widgetbook/` with a path
dependency on the launcher package. Production components live under
`lib/app/design_system`; stories, fixtures, and generated directories stay in
`widgetbook/lib`.
```powershell
cd widgetbook
fvm flutter pub get
fvm dart run build_runner build
fvm flutter run -d windows
```
The catalog provides forest and temple themes, 1024×640, 1280×720, and
1600×900 Windows viewports, and text scaling from 100% through 200%. The
production launcher composition targets a fixed minimum window of 1280×720 to
preserve the mockup layout.
The launcher compositions must follow `mockup.html`: a 240-pixel game rail,
brand and account header, hero copy over the key art, a 360-pixel floating news
panel, and the realm/progress/play strip above the status bar. The login shell
uses the mockup's split artwork and 480-pixel form panel. Widgetbook stories
exercise these production compositions; they are not alternate app layouts.
Temporary launcher artwork uses a 1536×1024 source cropped with `BoxFit.cover`.
Final artwork should be at least 2560×1440, keep essential content out of the
outer 12% on every edge, and leave the central/right content areas readable
under a dark gradient. The MoonWell brand mark is composed from Flutter vector
icons and text and must remain legible between 96 and 360 logical pixels.
Generate comprehensive Widgetbook use cases for Flutter components that showcase different variants and configurations effectively.
## Core Requirements
### UseCase Annotation Structure
Each use case must have a properly configured `@UseCase` annotation:
```dart
@UseCase(
name: 'variantName', // Required: Unique identifier for this variant
type: ComponentType, // Required: The Flutter widget class being showcased
)
```
**Naming Rules:**
- Single use case: `name: 'default'`
- Multiple use cases: Use descriptive names like `'with_label'`, `'disabled'`, `'loading'`
- Names must be unique within the same component type
### Method Signature Requirements
**Exact signature pattern:**
```dart
Widget build[ComponentName][VariantName]UseCase(BuildContext context) {
// Implementation
}
```
**Naming conventions:**
- Single use case: `buildProgressIndicatorUseCase`
- Multiple use cases: `buildProgressIndicatorWithLabelUseCase`, `buildProgressIndicatorDisabledUseCase`
- Always return `Widget`
- Always accept exactly one parameter: `BuildContext context`
## Parameter Configuration Strategy
### Priority System
1. **Critical parameters** (required, affects core functionality): Always use knobs
2. **Visual parameters** (colors, sizes, styles): Use knobs when they demonstrate component flexibility
3. **Behavioral parameters** (enabled/disabled, loading states): Use knobs for interactive demonstration
4. **Callback parameters**: Implement with descriptive print statements
5. **Complex objects**: Hardcode with meaningful defaults, add TODO comments
### Knob Selection Logic
- **Bounded numeric values**: Use sliders (opacity: 0.0-1.0, progress: 0-100)
- **Unbounded numeric values**: Use input fields (dimensions, counts)
- **Enums/predefined options**: Use list knobs
- **Text content**: Use string inputs
- **Feature toggles**: Use boolean checkboxes
## Comprehensive Knobs API
### Basic Types
```dart
// Strings
context.knobs.string(label: 'text', initialValue: 'Hello World')
context.knobs.stringOrNull(label: 'optionalText', initialValue: null)
// Booleans
context.knobs.boolean(label: 'enabled', initialValue: true)
context.knobs.booleanOrNull(label: 'optionalFlag', initialValue: null)
// Integers
context.knobs.int.input(label: 'count', initialValue: 5)
context.knobs.int.slider(label: 'progress', initialValue: 50, min: 0, max: 100, divisions: 10)
context.knobs.intOrNull.input(label: 'optionalCount', initialValue: null)
context.knobs.intOrNull.slider(label: 'progress', initialValue: null, min: 0, max: 100, divisions: 10)
// Doubles
context.knobs.double.input(label: 'value', initialValue: 1.5)
context.knobs.double.slider(label: 'opacity', initialValue: 0.8, min: 0.0, max: 1.0, divisions: 20)
context.knobs.doubleOrNull.input(label: 'value', initialValue: null)
context.knobs.doubleOrNull.slider(label: 'optionalOpacity', initialValue: null, min: 0.0, max: 1.0)
```
### Advanced Types
```dart
// Dropdown lists
context.knobs.object.dropdown<TextAlign>(
label: 'textAlign',
initialOption: TextAlign.center,
options: [TextAlign.left, TextAlign.center, TextAlign.right],
labelBuilder: (value) => value.name,
)
// Colors
context.knobs.color(label: 'primaryColor', initialValue: Colors.blue)
context.knobs.colorOrNull(label: 'optionalColor', initialValue: null)
// DateTime
context.knobs.dateTime(
label: 'selectedDate',
initialValue: DateTime.now(),
start: DateTime.now().subtract(const Duration(days: 365)),
end: DateTime.now().add(const Duration(days: 365)),
)
// Duration
context.knobs.duration(label: 'animationDuration', initialValue: const Duration(milliseconds: 300))
```
## Advanced Component Handling
### State Management Integration
For components requiring external state:
```dart
// Example: Component requiring a provider
@UseCase(name: 'with_data', type: DataWidget)
Widget buildDataWidgetWithDataUseCase(BuildContext context) {
return MockDataProvider(
data: _generateMockData(),
child: DataWidget(
onItemSelected: (item) => print('Selected item: ${item.id}'),
showLoading: context.knobs.boolean(label: 'showLoading', initialValue: false),
),
);
}
```
### Complex Parameter Handling
```dart
// For unmappable parameters
final customObject = CustomConfiguration(
// Hardcoded meaningful defaults
apiEndpoint: 'https://api.example.com',
timeout: const Duration(seconds: 30),
); // TODO: User should configure CustomConfiguration manually
// For asset references
final iconPath = 'assets/icons/star.svg'; // Verify asset exists in pubspec.yaml
```
### Callback Implementation Patterns
```dart
// Simple callbacks
onPressed: () => print('Button pressed'),
// Callbacks with data
onChanged: (value) => print('Value changed to: $value'),
// Complex callbacks
onFormSubmitted: (formData) {
print('Form submitted with data:');
print(' - Name: ${formData.name}');
print(' - Email: ${formData.email}');
},
// Async callbacks
onSave: () async {
print('Save operation started');
await Future.delayed(const Duration(seconds: 1));
print('Save operation completed');
},
```
### Theme handling
Themes are globally injected and must not be provided.
## Use Case Variant Strategies
### Single Component, Multiple Variants
Create variants that showcase different states and configurations:
```dart
// Default state
@UseCase(name: 'default', type: CustomButton)
Widget buildCustomButtonUseCase(BuildContext context) { /* ... */ }
// Loading state
@UseCase(name: 'loading', type: CustomButton)
Widget buildCustomButtonLoadingUseCase(BuildContext context) { /* ... */ }
// Disabled state
@UseCase(name: 'disabled', type: CustomButton)
Widget buildCustomButtonDisabledUseCase(BuildContext context) { /* ... */ }
// With icon
@UseCase(name: 'with icon', type: CustomButton)
Widget buildCustomButtonWithIconUseCase(BuildContext context) { /* ... */ }
```
### Responsive and Theme Variants
```dart
// Different sizes
@UseCase(name: 'small', type: ProfileCard)
@UseCase(name: 'medium', type: ProfileCard)
@UseCase(name: 'large', type: ProfileCard)
```
## Quality Standards
### Code Quality
- **No descriptive comments**: Code should be self-documenting
- **Consistent formatting**: Follow Dart style guidelines
- **Meaningful defaults**: Initial values should represent realistic usage
- **Error handling**: Wrap potentially failing operations in try-catch where appropriate
### Use Case Coverage
Ensure use cases demonstrate:
- [ ] Default/primary functionality
- [ ] Edge cases (empty states, maximum values)
- [ ] Interactive behaviors (hover, focus, disabled states)
- [ ] Visual variants (different styles, sizes, colors)
- [ ] Error states when applicable
### Testing Considerations
```dart
// Include realistic data volumes
final items = List.generate(50, (index) => 'Item ${index + 1}');
// Test boundary conditions
final progress = context.knobs.double.slider(
label: 'progress',
initialValue: 0.7,
min: 0.0,
max: 1.0,
divisions: 100,
); // Allows testing 0%, 100%, and intermediate values
```
## Complete Example Template
```dart
import 'package:flutter/material.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart';
@UseCase(
name: 'interactive',
type: CustomSlider,
)
Widget buildCustomSliderInteractiveUseCase(BuildContext context) {
return CustomSlider(
value: context.knobs.double.slider(
label: 'value',
initialValue: 0.5,
min: 0.0,
max: 1.0,
divisions: 20,
),
min: context.knobs.double.input(
label: 'min',
initialValue: 0.0,
),
max: context.knobs.double.input(
label: 'max',
initialValue: 1.0,
),
enabled: context.knobs.boolean(
label: 'enabled',
initialValue: true,
),
showLabels: context.knobs.boolean(
label: 'showLabels',
initialValue: true,
),
activeColor: context.knobs.color(
label: 'activeColor',
initialValue: Colors.blue,
),
onChanged: (value) => print('Slider value changed to: $value'),
onChangeStart: (value) => print('Slider interaction started at: $value'),
onChangeEnd: (value) => print('Slider interaction ended at: $value'),
);
}
```
## Pre-Generation Checklist
Before generating use cases, verify:
- [ ] Component class name and import path
- [ ] Required vs optional parameters
- [ ] Parameter types and constraints
- [ ] Available enum values for list knobs
- [ ] Asset dependencies and paths
- [ ] State management requirements
- [ ] Theme/localization dependencies
- [ ] Callback signatures and expected behavior