flutter-pro-max-cli 2.3.1 → 2.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/scripts/core.py +1 -1
- package/assets/templates/base/rules/01_skill_usage.md +22 -0
- package/assets/templates/base/{rules.md → rules/02_code_quality.md} +28 -29
- package/assets/templates/base/rules/03_interaction_flow.md +21 -0
- package/assets/templates/base/rules/04_app_consistency.md +58 -0
- package/assets/templates/base/rules/05_error_handling.md +43 -0
- package/assets/templates/base/rules/06_testing.md +48 -0
- package/assets/templates/base/rules/07_performance.md +47 -0
- package/assets/templates/base/rules/08_security.md +41 -0
- package/assets/templates/base/rules/09_state_management.md +54 -0
- package/assets/templates/base/rules/10_naming_conventions.md +70 -0
- package/assets/templates/base/rules/11_accessibility.md +56 -0
- package/assets/templates/base/skill-content-10k.md +40 -206
- package/assets/templates/base/skill-content-4k.md +9 -95
- package/assets/templates/base/skill-content.md +29 -370
- package/assets/templates/platforms/agent.json +2 -2
- package/dist/types/index.js +1 -1
- package/dist/utils/detect.js +2 -2
- package/dist/utils/template.js +30 -13
- package/package.json +1 -1
package/assets/scripts/core.py
CHANGED
|
@@ -23,7 +23,7 @@ def _get_data_dir() -> Path:
|
|
|
23
23
|
script_dir.parent.parent / "data",
|
|
24
24
|
# When running from root/scripts/
|
|
25
25
|
script_dir.parent / ".shared" / "data",
|
|
26
|
-
# When running from .
|
|
26
|
+
# When running from .agents/workflows/scripts/ (nested 3 levels deep)
|
|
27
27
|
script_dir.parent.parent.parent / ".shared" / "data",
|
|
28
28
|
# When running from .claude/skills/flutter-pro-max/scripts/ (reference mode)
|
|
29
29
|
script_dir.parent.parent.parent.parent / ".shared" / "data",
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Rule: Tự động sử dụng Skill
|
|
2
|
+
|
|
3
|
+
> Kích hoạt: Khi làm việc với Flutter/Dart files
|
|
4
|
+
|
|
5
|
+
**TRƯỚC KHI viết code Flutter**, bạn PHẢI tự động sử dụng skill để lấy knowledge phù hợp. Không được bỏ qua bước này.
|
|
6
|
+
|
|
7
|
+
| Tình huống | Domains cần search |
|
|
8
|
+
|------------|-------------------|
|
|
9
|
+
| Tạo UI/Screen mới | `style`, `pattern`, `color`, `typography`, `landing` |
|
|
10
|
+
| Chọn package/thư viện | `package` (kèm `--stack` filter nếu có) |
|
|
11
|
+
| Thiết kế kiến trúc | `architect`, `pattern` |
|
|
12
|
+
| Tối ưu performance | `performance` |
|
|
13
|
+
| Accessibility | `accessibility` |
|
|
14
|
+
| Không chắc UI style | Dùng `--design-system` để generate design system |
|
|
15
|
+
|
|
16
|
+
**Workflow bắt buộc:**
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
User request → Search skill (≥2 domains) → Đọc kết quả → Áp dụng vào code
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
> ⚠️ Viết code Flutter mà không tham khảo skill trước = thiếu context = code chất lượng thấp.
|
|
@@ -1,44 +1,43 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Rule: Code Quality & Hard Constraints
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
> Chúng là project-level rules, TÁCH BIỆT khỏi skill content.
|
|
3
|
+
> Kích hoạt: Khi tạo/chỉnh sửa files `.dart`
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
## Think-Before-Code Protocol
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
**TRƯỚC KHI tạo file mới hoặc viết widget**, bạn PHẢI trả lời 5 câu hỏi:
|
|
9
8
|
|
|
10
|
-
**
|
|
9
|
+
1. **File này có vượt 300 dòng không?** → Nếu có khả năng, TÁCH ngay từ đầu
|
|
10
|
+
2. **Widget/Component này đã tồn tại chưa?** → Search codebase trước, REUSE nếu có
|
|
11
|
+
3. **Widget này có thể reuse cho nơi khác không?** → Nếu có, đặt vào `core/widgets/` hoặc shared folder
|
|
12
|
+
4. **Logic này thuộc layer nào?** → UI / Domain / Data — KHÔNG được trộn layers
|
|
13
|
+
5. **Có widget/hàm nào đang lặp logic tương tự không?** → Nếu có, REFACTOR thành shared component
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
|------------|-------------------|
|
|
14
|
-
| Tạo UI/Screen mới | `style`, `pattern`, `color`, `typography`, `landing` |
|
|
15
|
-
| Chọn package/thư viện | `package` (kèm `--stack` filter nếu có) |
|
|
16
|
-
| Thiết kế kiến trúc | `architect`, `pattern` |
|
|
17
|
-
| Tối ưu performance | `performance` |
|
|
18
|
-
| Accessibility | `accessibility` |
|
|
19
|
-
| Không chắc UI style | Dùng `--design-system` để generate design system |
|
|
15
|
+
## Hard Constraints (Vùng Cấm)
|
|
20
16
|
|
|
21
|
-
|
|
17
|
+
### 🚫 NO GOD CLASSES
|
|
22
18
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
| Indicator | Threshold | Action |
|
|
20
|
+
|-----------|-----------|--------|
|
|
21
|
+
| Public methods | > 10 methods | 🔴 **REFACTOR** |
|
|
22
|
+
| Lines of logic | > 200 lines | 🔴 **REFACTOR** |
|
|
23
|
+
| Mixed concerns | Logic + UI + DB | 🔴 **TÁCH NGAY** |
|
|
26
24
|
|
|
27
|
-
|
|
25
|
+
### 🚫 NO GOD FILES
|
|
28
26
|
|
|
29
|
-
|
|
27
|
+
| Rule | Limit |
|
|
28
|
+
|------|-------|
|
|
29
|
+
| **File size** | ≤ 300 dòng (tối đa 500) |
|
|
30
|
+
| **Classes per file** | 1 Class chính duy nhất |
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
### 🚫 NO LOGIC LEAKAGE
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
4. **Logic này thuộc layer nào?** → UI / Domain / Data — KHÔNG được trộn layers
|
|
39
|
-
5. **Có widget/hàm nào đang lặp logic tương tự không?** → Nếu có, REFACTOR thành shared component
|
|
34
|
+
| Violation | Correct Layer |
|
|
35
|
+
|-----------|---------------|
|
|
36
|
+
| Business Logic trong Widget | ➜ Move to `UseCase` / `Service` |
|
|
37
|
+
| SQL/Query trong Controller | ➜ Move to `Repository` |
|
|
38
|
+
| API calls trong UI | ➜ Move to `DataSource` |
|
|
40
39
|
|
|
41
|
-
|
|
40
|
+
## Nguyên tắc cứng
|
|
42
41
|
|
|
43
42
|
| ❌ Sai | ✅ Đúng |
|
|
44
43
|
|--------|---------|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Rule: Interaction Flow (ABCR)
|
|
2
|
+
|
|
3
|
+
> Kích hoạt: Khi review, refactor, hoặc fix bugs
|
|
4
|
+
|
|
5
|
+
Khi nhận request liên quan đến code hiện tại, luôn tuân thủ quy trình:
|
|
6
|
+
|
|
7
|
+
1. **AUDIT** - Quét code smells, kiểm tra God Class/File
|
|
8
|
+
2. **BLOCK** - Cảnh báo nếu vi phạm, giải thích Technical Debt
|
|
9
|
+
3. **REFACTOR** - Sửa kiến trúc trước khi fix bug
|
|
10
|
+
4. **EXPLAIN** - Giải thích lý do tách/refactor
|
|
11
|
+
|
|
12
|
+
### Khi nào áp dụng ABCR?
|
|
13
|
+
|
|
14
|
+
| Tình huống | Áp dụng? |
|
|
15
|
+
|------------|----------|
|
|
16
|
+
| User yêu cầu fix bug | ✅ AUDIT trước, refactor nếu có code smell |
|
|
17
|
+
| User yêu cầu thêm feature | ✅ AUDIT file đích trước khi thêm code |
|
|
18
|
+
| User yêu cầu tạo file mới | ⚠️ Chỉ AUDIT các file liên quan |
|
|
19
|
+
| User hỏi kiến thức chung | ❌ Không cần ABCR |
|
|
20
|
+
|
|
21
|
+
> 💡 **Mục đích:** Không bao giờ thêm code rác lên code rác. Fix nền tảng trước.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Rule: App Consistency
|
|
2
|
+
|
|
3
|
+
> Kích hoạt: Khi tạo UI, thêm screen, hoặc chỉnh sửa widget
|
|
4
|
+
|
|
5
|
+
## Nguyên tắc: Mọi thứ phải nhất quán
|
|
6
|
+
|
|
7
|
+
**TRƯỚC KHI viết UI code**, bạn PHẢI kiểm tra các pattern hiện có trong project để đảm bảo consistency.
|
|
8
|
+
|
|
9
|
+
## 1. Design Tokens — Dùng chung, không hardcode
|
|
10
|
+
|
|
11
|
+
| ❌ Sai | ✅ Đúng |
|
|
12
|
+
|--------|---------|
|
|
13
|
+
| `Color(0xFF1A73E8)` | `Theme.of(context).colorScheme.primary` |
|
|
14
|
+
| `EdgeInsets.all(16)` | `EdgeInsets.all(AppSpacing.md)` hoặc constant |
|
|
15
|
+
| `TextStyle(fontSize: 14)` | `Theme.of(context).textTheme.bodyMedium` |
|
|
16
|
+
| `BorderRadius.circular(8)` | `BorderRadius.circular(AppRadius.sm)` |
|
|
17
|
+
| `Duration(milliseconds: 300)` | `AppDurations.normal` |
|
|
18
|
+
|
|
19
|
+
## 2. Widget Patterns — Copy style từ existing screens
|
|
20
|
+
|
|
21
|
+
**TRƯỚC KHI tạo screen mới:**
|
|
22
|
+
|
|
23
|
+
1. Tìm screen tương tự trong codebase (list, detail, form, dashboard)
|
|
24
|
+
2. Sao chép cấu trúc, spacing, và layout pattern
|
|
25
|
+
3. Dùng cùng widget wrappers (Scaffold, AppBar style, padding)
|
|
26
|
+
|
|
27
|
+
| Element | Quy tắc |
|
|
28
|
+
|---------|---------|
|
|
29
|
+
| **AppBar** | Dùng chung 1 style/component cho toàn app |
|
|
30
|
+
| **Empty States** | Dùng chung widget, không tạo mới mỗi screen |
|
|
31
|
+
| **Loading States** | Dùng chung shimmer/skeleton, không mỗi chỗ 1 kiểu |
|
|
32
|
+
| **Error States** | Dùng chung error widget với retry action |
|
|
33
|
+
| **List Items** | Cùng padding, divider style, tap behavior |
|
|
34
|
+
| **Forms** | Cùng validation style, field spacing, button placement |
|
|
35
|
+
| **Dialogs** | Cùng shape, padding, button alignment |
|
|
36
|
+
|
|
37
|
+
## 3. Navigation & Transitions
|
|
38
|
+
|
|
39
|
+
- Dùng chung transition animations (không mỗi screen 1 kiểu)
|
|
40
|
+
- Consistent back button behavior
|
|
41
|
+
- Cùng pattern cho bottom sheets, modals, popups
|
|
42
|
+
|
|
43
|
+
## 4. Spacing System
|
|
44
|
+
|
|
45
|
+
Định nghĩa và tuân thủ spacing scale:
|
|
46
|
+
|
|
47
|
+
```dart
|
|
48
|
+
// ✅ Dùng constants
|
|
49
|
+
abstract class AppSpacing {
|
|
50
|
+
static const double xs = 4;
|
|
51
|
+
static const double sm = 8;
|
|
52
|
+
static const double md = 16;
|
|
53
|
+
static const double lg = 24;
|
|
54
|
+
static const double xl = 32;
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
> 🔴 **Khi có nghi ngờ:** Mở screen hiện tại có cùng chức năng → copy exact spacing và layout pattern. **Không sáng tạo riêng.**
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Rule: Error Handling
|
|
2
|
+
|
|
3
|
+
> Kích hoạt: Khi viết logic xử lý dữ liệu, API calls, hoặc async operations
|
|
4
|
+
|
|
5
|
+
## Nguyên tắc: Không bao giờ fail im lặng
|
|
6
|
+
|
|
7
|
+
### Bắt buộc
|
|
8
|
+
|
|
9
|
+
| Tình huống | Cách xử lý |
|
|
10
|
+
|------------|------------|
|
|
11
|
+
| API call | Luôn wrap trong `try-catch`, log lỗi, hiển thị message cho user |
|
|
12
|
+
| Parse JSON/data | Dùng `tryParse` hoặc `try-catch`, KHÔNG để crash |
|
|
13
|
+
| File I/O | Handle `FileSystemException` cụ thể |
|
|
14
|
+
| Navigation args | Validate params trước khi dùng |
|
|
15
|
+
|
|
16
|
+
### Pattern chuẩn
|
|
17
|
+
|
|
18
|
+
```dart
|
|
19
|
+
// ✅ Structured error handling
|
|
20
|
+
Future<Result<User>> fetchUser(String id) async {
|
|
21
|
+
try {
|
|
22
|
+
final response = await api.getUser(id);
|
|
23
|
+
return Result.success(User.fromJson(response));
|
|
24
|
+
} on DioException catch (e) {
|
|
25
|
+
developer.log('API failed', name: 'user.fetch', error: e);
|
|
26
|
+
return Result.failure(e.toAppError());
|
|
27
|
+
} catch (e, s) {
|
|
28
|
+
developer.log('Unexpected', name: 'user.fetch', error: e, stackTrace: s);
|
|
29
|
+
return Result.failure(AppError.unexpected(e));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Cấm
|
|
35
|
+
|
|
36
|
+
| ❌ Sai | Lý do |
|
|
37
|
+
|--------|-------|
|
|
38
|
+
| `catch (e) {}` (empty catch) | Nuốt lỗi, debug nightmare |
|
|
39
|
+
| `print(e)` | Dùng `developer.log()` thay vì print |
|
|
40
|
+
| Throw generic `Exception('Error')` | Tạo custom exceptions có context |
|
|
41
|
+
| Ignore `StackTrace` | Luôn log cả `stackTrace` để debug |
|
|
42
|
+
|
|
43
|
+
> 🔴 **Mỗi `try` phải có `catch` có ý nghĩa.** Log + User message + Recovery action.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Rule: Testing
|
|
2
|
+
|
|
3
|
+
> Kích hoạt: Khi tạo feature mới, fix bug, hoặc refactor logic
|
|
4
|
+
|
|
5
|
+
## Nguyên tắc: Không có test = Không hoàn thành
|
|
6
|
+
|
|
7
|
+
### Khi nào PHẢI viết test
|
|
8
|
+
|
|
9
|
+
| Loại code | Test bắt buộc | Ví dụ |
|
|
10
|
+
|-----------|---------------|-------|
|
|
11
|
+
| Business logic | Unit test | UseCase, Service, Validator |
|
|
12
|
+
| Repository/DataSource | Unit test với mock | API calls, DB queries |
|
|
13
|
+
| Widget có logic | Widget test | Form validation, state changes |
|
|
14
|
+
| User flow quan trọng | Integration test | Login, checkout, onboarding |
|
|
15
|
+
|
|
16
|
+
### Khi nào KHÔNG cần test
|
|
17
|
+
|
|
18
|
+
- Pure UI widget không có logic (chỉ layout/styling)
|
|
19
|
+
- Generated code (`.g.dart`, `.freezed.dart`)
|
|
20
|
+
- Constants, enums đơn giản
|
|
21
|
+
|
|
22
|
+
### Pattern chuẩn
|
|
23
|
+
|
|
24
|
+
```dart
|
|
25
|
+
// ✅ Test structure: Arrange → Act → Assert
|
|
26
|
+
test('should return user when API succeeds', () async {
|
|
27
|
+
// Arrange
|
|
28
|
+
when(mockApi.getUser('123')).thenAnswer(
|
|
29
|
+
(_) async => {'name': 'John'},
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
// Act
|
|
33
|
+
final result = await useCase.execute('123');
|
|
34
|
+
|
|
35
|
+
// Assert
|
|
36
|
+
expect(result, isA<Success<User>>());
|
|
37
|
+
expect(result.data.name, equals('John'));
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Quy tắc
|
|
42
|
+
|
|
43
|
+
- File test đặt cùng tên: `user_service.dart` → `user_service_test.dart`
|
|
44
|
+
- Dùng `package:mocktail` hoặc `package:mockito` cho mocking
|
|
45
|
+
- Mỗi test case chỉ test 1 behavior
|
|
46
|
+
- Tên test mô tả behavior: `should [expected] when [condition]`
|
|
47
|
+
|
|
48
|
+
> 🔴 **Fix bug?** Viết test reproduce bug TRƯỚC, rồi mới fix.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Rule: Performance
|
|
2
|
+
|
|
3
|
+
> Kích hoạt: Khi viết widget, xử lý danh sách, hoặc async operations
|
|
4
|
+
|
|
5
|
+
## Nguyên tắc: Performance là requirement, không phải nice-to-have
|
|
6
|
+
|
|
7
|
+
### Widget Performance
|
|
8
|
+
|
|
9
|
+
| ❌ Sai | ✅ Đúng | Impact |
|
|
10
|
+
|--------|---------|--------|
|
|
11
|
+
| `Container()` cho spacing | `const SizedBox(height: 16)` | Giảm rebuild |
|
|
12
|
+
| `ListView(children: [...])` | `ListView.builder(itemBuilder:)` | Lazy loading |
|
|
13
|
+
| Widget không có `const` | `const MyWidget()` | Prevent rebuild |
|
|
14
|
+
| Inline function trong `build()` | Extract method hoặc cached | Tránh tạo closure mỗi frame |
|
|
15
|
+
| Rebuild toàn tree | `ValueListenableBuilder` scoped | Chỉ rebuild phần thay đổi |
|
|
16
|
+
|
|
17
|
+
### Async & Computation
|
|
18
|
+
|
|
19
|
+
| Quy tắc | Lý do |
|
|
20
|
+
|----------|-------|
|
|
21
|
+
| KHÔNG gọi API/async trong `build()` | Block UI thread |
|
|
22
|
+
| Heavy JSON parsing → dùng `compute()` | Chạy trên isolate riêng |
|
|
23
|
+
| Image caching → dùng `CachedNetworkImage` | Tránh download lại |
|
|
24
|
+
| Debounce search input (300-500ms) | Giảm API calls |
|
|
25
|
+
|
|
26
|
+
### State Management Performance
|
|
27
|
+
|
|
28
|
+
```dart
|
|
29
|
+
// ❌ Toàn widget tree rebuild
|
|
30
|
+
setState(() => _counter++);
|
|
31
|
+
|
|
32
|
+
// ✅ Chỉ rebuild phần cần thiết
|
|
33
|
+
ValueListenableBuilder<int>(
|
|
34
|
+
valueListenable: _counter,
|
|
35
|
+
builder: (_, value, child) => Text('$value'),
|
|
36
|
+
child: const ExpensiveWidget(), // Không bị rebuild
|
|
37
|
+
);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Checklist trước ship
|
|
41
|
+
|
|
42
|
+
- [ ] Tất cả widget có `const` constructor nếu có thể
|
|
43
|
+
- [ ] ListView/GridView dùng `.builder` hoặc `.separated`
|
|
44
|
+
- [ ] Không có `print()` còn sót (dùng `developer.log`)
|
|
45
|
+
- [ ] Images có loading/error builders
|
|
46
|
+
|
|
47
|
+
> 🔴 **Nếu list > 20 items → BẮT BUỘC dùng `.builder`.** Không exceptions.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Rule: Security
|
|
2
|
+
|
|
3
|
+
> Kích hoạt: Khi xử lý authentication, API keys, user data, hoặc storage
|
|
4
|
+
|
|
5
|
+
## Nguyên tắc: Bảo mật không phải optional
|
|
6
|
+
|
|
7
|
+
### API Keys & Secrets
|
|
8
|
+
|
|
9
|
+
| ❌ KHÔNG BAO GIỜ | ✅ Thay bằng |
|
|
10
|
+
|-------------------|-------------|
|
|
11
|
+
| Hardcode API key trong source | Dùng `--dart-define` hoặc `.env` |
|
|
12
|
+
| Commit `.env` file | Thêm vào `.gitignore` |
|
|
13
|
+
| Log sensitive data | Mask/redact trước khi log |
|
|
14
|
+
| Lưu token trong `SharedPreferences` | Dùng `flutter_secure_storage` |
|
|
15
|
+
|
|
16
|
+
### Authentication
|
|
17
|
+
|
|
18
|
+
| Quy tắc | Chi tiết |
|
|
19
|
+
|----------|----------|
|
|
20
|
+
| Token storage | `flutter_secure_storage` (encrypted) |
|
|
21
|
+
| Token refresh | Interceptor tự động refresh khi 401 |
|
|
22
|
+
| Logout | Clear ALL tokens + secure storage |
|
|
23
|
+
| Deep link auth | Validate state parameter |
|
|
24
|
+
|
|
25
|
+
### Data Protection
|
|
26
|
+
|
|
27
|
+
| Tình huống | Xử lý |
|
|
28
|
+
|------------|-------|
|
|
29
|
+
| User input | Sanitize trước khi gửi API |
|
|
30
|
+
| Hiển thị PII (email, phone) | Mask một phần: `john***@gmail.com` |
|
|
31
|
+
| Cache sensitive data | Encrypt hoặc không cache |
|
|
32
|
+
| Screenshot prevention | `FLAG_SECURE` cho screens nhạy cảm |
|
|
33
|
+
|
|
34
|
+
### Network Security
|
|
35
|
+
|
|
36
|
+
- HTTPS only (không HTTP)
|
|
37
|
+
- Certificate pinning cho apps quan trọng
|
|
38
|
+
- Timeout cho mọi API call (30s max)
|
|
39
|
+
- Không trust user input từ deep links
|
|
40
|
+
|
|
41
|
+
> 🔴 **Mỗi lần thêm API key hay xử lý auth**, kiểm tra checklist trên.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Rule: State Management
|
|
2
|
+
|
|
3
|
+
> Kích hoạt: Khi quản lý state trong widget, screen, hoặc app-level
|
|
4
|
+
|
|
5
|
+
## Nguyên tắc: Native-First, Escalate khi cần
|
|
6
|
+
|
|
7
|
+
### Hierarchy (Ưu tiên từ trên xuống)
|
|
8
|
+
|
|
9
|
+
| Level | Giải pháp | Khi nào dùng |
|
|
10
|
+
|-------|-----------|-------------|
|
|
11
|
+
| 1️⃣ | `StatelessWidget` | UI tĩnh, không có state |
|
|
12
|
+
| 2️⃣ | `ValueNotifier` + `ValueListenableBuilder` | State đơn giản (counter, toggle, loading) |
|
|
13
|
+
| 3️⃣ | `ChangeNotifier` + `ListenableBuilder` | State phức tạp có nhiều fields (form, cart) |
|
|
14
|
+
| 4️⃣ | `InheritedWidget` / `Provider` | Shared state giữa nhiều widgets |
|
|
15
|
+
| 5️⃣ | Riverpod / Bloc | **CHỈ KHI user yêu cầu rõ ràng** |
|
|
16
|
+
|
|
17
|
+
### Quy tắc cứng
|
|
18
|
+
|
|
19
|
+
| ❌ Sai | ✅ Đúng |
|
|
20
|
+
|--------|---------|
|
|
21
|
+
| Dùng Riverpod cho counter đơn giản | `ValueNotifier<int>` |
|
|
22
|
+
| `setState` rebuild toàn screen | `ValueListenableBuilder` scoped |
|
|
23
|
+
| Global state cho state chỉ 1 screen dùng | Local state trong widget |
|
|
24
|
+
| Mutable state trực tiếp | Immutable state + `copyWith` |
|
|
25
|
+
|
|
26
|
+
### Pattern chuẩn
|
|
27
|
+
|
|
28
|
+
```dart
|
|
29
|
+
// ✅ Simple: ValueNotifier
|
|
30
|
+
class CounterWidget extends StatelessWidget {
|
|
31
|
+
final _count = ValueNotifier<int>(0);
|
|
32
|
+
|
|
33
|
+
@override
|
|
34
|
+
Widget build(BuildContext context) {
|
|
35
|
+
return ValueListenableBuilder<int>(
|
|
36
|
+
valueListenable: _count,
|
|
37
|
+
builder: (_, value, __) => Text('$value'),
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ✅ Complex: ChangeNotifier
|
|
43
|
+
class CartNotifier extends ChangeNotifier {
|
|
44
|
+
final List<Item> _items = [];
|
|
45
|
+
List<Item> get items => List.unmodifiable(_items);
|
|
46
|
+
|
|
47
|
+
void add(Item item) {
|
|
48
|
+
_items.add(item);
|
|
49
|
+
notifyListeners();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
> ⚠️ **KHÔNG tự ý thêm Riverpod/Bloc/GetX.** Hỏi user trước nếu cần escalate.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Rule: Naming & Conventions
|
|
2
|
+
|
|
3
|
+
> Kích hoạt: Khi tạo files, classes, functions, hoặc variables
|
|
4
|
+
|
|
5
|
+
## Nguyên tắc: Tên phải tự giải thích, cấu trúc phải nhất quán
|
|
6
|
+
|
|
7
|
+
### Naming Convention
|
|
8
|
+
|
|
9
|
+
| Element | Convention | Ví dụ |
|
|
10
|
+
|---------|-----------|-------|
|
|
11
|
+
| Files | `snake_case` | `user_profile_page.dart` |
|
|
12
|
+
| Classes | `PascalCase` | `UserProfilePage` |
|
|
13
|
+
| Functions/Methods | `camelCase` | `fetchUserProfile()` |
|
|
14
|
+
| Variables | `camelCase` | `userName`, `isLoading` |
|
|
15
|
+
| Constants | `camelCase` hoặc `SCREAMING_SNAKE` | `maxRetryCount`, `API_BASE_URL` |
|
|
16
|
+
| Private | Prefix `_` | `_buildHeader()`, `_items` |
|
|
17
|
+
| Enums | `PascalCase` values | `UserRole.admin` |
|
|
18
|
+
|
|
19
|
+
### File Naming theo Layer
|
|
20
|
+
|
|
21
|
+
| Layer | Pattern | Ví dụ |
|
|
22
|
+
|-------|---------|-------|
|
|
23
|
+
| Page/Screen | `*_page.dart` | `login_page.dart` |
|
|
24
|
+
| Widget | `*_widget.dart` hoặc tên mô tả | `user_avatar.dart` |
|
|
25
|
+
| Model | `*_model.dart` | `user_model.dart` |
|
|
26
|
+
| Repository | `*_repository.dart` | `auth_repository.dart` |
|
|
27
|
+
| Service/UseCase | `*_service.dart` / `*_use_case.dart` | `auth_service.dart` |
|
|
28
|
+
| Provider/Notifier | `*_provider.dart` / `*_notifier.dart` | `cart_notifier.dart` |
|
|
29
|
+
| Extension | `*_extension.dart` | `string_extension.dart` |
|
|
30
|
+
|
|
31
|
+
### Folder Structure
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
lib/
|
|
35
|
+
├── core/ # Shared: theme, utils, widgets, constants
|
|
36
|
+
│ ├── theme/
|
|
37
|
+
│ ├── widgets/ # Reusable widgets
|
|
38
|
+
│ ├── utils/
|
|
39
|
+
│ └── constants/
|
|
40
|
+
├── features/ # Feature-first organization
|
|
41
|
+
│ ├── auth/
|
|
42
|
+
│ │ ├── data/ # Repository implementations, models
|
|
43
|
+
│ │ ├── domain/ # Entities, use cases, repo interfaces
|
|
44
|
+
│ │ └── presentation/ # Pages, widgets, notifiers
|
|
45
|
+
│ └── home/
|
|
46
|
+
└── main.dart
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Git Commit Convention
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
<type>(<scope>): <description>
|
|
53
|
+
|
|
54
|
+
feat(auth): add biometric login support
|
|
55
|
+
fix(cart): resolve item count not updating
|
|
56
|
+
refactor(core): extract shared AppBar widget
|
|
57
|
+
docs(readme): update installation guide
|
|
58
|
+
test(auth): add login use case unit tests
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
| Type | Khi nào |
|
|
62
|
+
|------|---------|
|
|
63
|
+
| `feat` | Feature mới |
|
|
64
|
+
| `fix` | Sửa bug |
|
|
65
|
+
| `refactor` | Restructure code, không đổi behavior |
|
|
66
|
+
| `docs` | Documentation only |
|
|
67
|
+
| `test` | Thêm/sửa tests |
|
|
68
|
+
| `chore` | Config, dependencies, tooling |
|
|
69
|
+
|
|
70
|
+
> 🔴 **Đặt tên file/class sai convention?** Rename ngay, không để nợ.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Rule: Accessibility
|
|
2
|
+
|
|
3
|
+
> Kích hoạt: Khi tạo UI, interactive elements, hoặc form inputs
|
|
4
|
+
|
|
5
|
+
## Nguyên tắc: App phải dùng được cho MỌI NGƯỜI
|
|
6
|
+
|
|
7
|
+
### Bắt buộc cho mọi widget
|
|
8
|
+
|
|
9
|
+
| Requirement | Standard | Cách check |
|
|
10
|
+
|-------------|----------|------------|
|
|
11
|
+
| **Contrast** | Minimum 4.5:1 cho text | Dùng contrast checker tool |
|
|
12
|
+
| **Large Text** | Minimum 3:1 (18pt hoặc 14pt bold) | Visual check |
|
|
13
|
+
| **Touch Target** | Minimum 48x48dp | `SizedBox` wrapper nếu cần |
|
|
14
|
+
| **Semantics** | Label tất cả interactive elements | `Semantics()` widget |
|
|
15
|
+
| **Dynamic Scaling** | Hỗ trợ lên đến 200% font size | Test với `textScaleFactor` |
|
|
16
|
+
|
|
17
|
+
### Code Patterns
|
|
18
|
+
|
|
19
|
+
```dart
|
|
20
|
+
// ✅ Semantics cho interactive elements
|
|
21
|
+
Semantics(
|
|
22
|
+
label: 'Xóa sản phẩm khỏi giỏ hàng',
|
|
23
|
+
button: true,
|
|
24
|
+
child: IconButton(
|
|
25
|
+
icon: const Icon(Icons.delete),
|
|
26
|
+
onPressed: onDelete,
|
|
27
|
+
),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
// ✅ Form field accessible
|
|
31
|
+
TextFormField(
|
|
32
|
+
decoration: const InputDecoration(
|
|
33
|
+
labelText: 'Email', // Screen reader đọc được
|
|
34
|
+
hintText: 'example@email.com',
|
|
35
|
+
),
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// ✅ Image có description
|
|
39
|
+
Image.network(
|
|
40
|
+
url,
|
|
41
|
+
semanticLabel: 'Ảnh đại diện của người dùng',
|
|
42
|
+
);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Checklist
|
|
46
|
+
|
|
47
|
+
| Element | Kiểm tra |
|
|
48
|
+
|---------|----------|
|
|
49
|
+
| Buttons/Icons | Có `tooltip` hoặc `Semantics.label` |
|
|
50
|
+
| Images | Có `semanticLabel` |
|
|
51
|
+
| Forms | Có `labelText`, không chỉ `hintText` |
|
|
52
|
+
| Alerts/Dialogs | Có title mô tả rõ |
|
|
53
|
+
| Navigation | Focus order hợp lý |
|
|
54
|
+
| Colors | Không dùng màu là cách duy nhất truyền thông tin |
|
|
55
|
+
|
|
56
|
+
> 🔴 **Mỗi `IconButton` PHẢI có `tooltip`.** Mỗi `Image` PHẢI có `semanticLabel`.
|