Skip to content

Instantly share code, notes, and snippets.

@woogi-kang
Last active August 26, 2025 14:52
Show Gist options
  • Select an option

  • Save woogi-kang/1cedf5071524a8504eb4432354a08564 to your computer and use it in GitHub Desktop.

Select an option

Save woogi-kang/1cedf5071524a8504eb4432354a08564 to your computer and use it in GitHub Desktop.
Flutter CursorRules - A set of standardized best practices for Flutter development that promote clean, maintainable, and efficient code.
# CursorRules for Flutter Developers
## Overview
This document provides a comprehensive set of cursor rules (development practices) for cr eating high-quality Flutter applications. These rules emphasize clean code, maintainability, and performance while leveraging modern Flutter and Dart features. The principles outlined here can be applied across projects to ensure consistency and quality.
## Core Principles
All cursor rules are based on these four fundamental principles:
1. **Readability**: Code must be easily understood by other developers.
2. **Predictability**: Functions, widgets, and components should behave as expected.
3. **Cohesion**: Related code should be kept together with a clear purpose.
4. **Coupling**: Minimize dependencies between different parts of the codebase.
## Project Structure
```
lib/
├── core/ # Core utilities and framework
│ ├── config/ # App configuration, constants
│ ├── di/ # Dependency injection
│ ├── network/ # Network utilities
│ ├── storage/ # Storage utilities
│ ├── theme/ # App theme
│ └── utils/ # Utilities and helpers
├── features/ # Feature modules
│ ├── auth/ # Authentication feature
│ │ ├── data/ # Data layer (repositories, models)
│ │ │ ├── datasources/
│ │ │ ├── models/
│ │ │ └── repositories/
│ │ ├── domain/ # Domain layer (entities, usecases)
│ │ │ ├── entities/
│ │ │ └── usecases/
│ │ ├── presentation/ # UI components
│ │ │ ├── screens/
│ │ │ └── widgets/
│ │ └── providers/ # State management
│ │
│ └── feature_name/ # Other features follow the same structure
└── main.dart # Entry point
```
### Cursor Rules for Project Structure:
- **CR-PS-01**: Group code by feature, not by type.
- **CR-PS-02**: Core module should only contain framework code and shared utilities.
- **CR-PS-03**: Feature modules should be self-contained with minimal dependencies.
- **CR-PS-04**: Place related files close to each other to minimize navigation.
- **CR-PS-05**: Use clean architecture principles within each feature.
## Readability
### Named Constants
**CR-R-01**: Replace magic numbers and string literals with named constants to improve clarity.
```dart
// ❌ Poor practice
Future<void> refreshData() async {
await Future.delayed(const Duration(milliseconds: 300));
await fetchItems();
}
// ✅ Good practice
const Duration kRefreshDebounce = Duration(milliseconds: 300);
Future<void> refreshData() async {
await Future.delayed(kRefreshDebounce);
await fetchItems();
}
```
### Widget Composition
**CR-R-02**: Break complex widgets into smaller, focused components.
```dart
// ❌ Poor practice: One large widget with multiple responsibilities
class ProductDetailScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Product')),
body: ListView(
children: [
// 50+ lines of image gallery...
// 40+ lines of product details...
// 60+ lines of reviews section...
// 40+ lines of related products...
],
),
);
}
}
// ✅ Good practice: Small, focused widgets
class ProductDetailScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Product')),
body: ListView(
children: [
ProductImageGallery(images: product.images),
ProductDetailCard(product: product),
ProductReviewsSection(productId: product.id),
RelatedProductsList(category: product.category),
],
),
);
}
}
```
### Specialized Widgets for Different States
**CR-R-03**: Create separate widgets for UI states instead of complex conditionals.
```dart
// ❌ Poor practice: Complex conditionals in a single widget
class OrderStatus extends StatelessWidget {
final Order order;
@override
Widget build(BuildContext context) {
if (order.status == OrderStatus.processing) {
return Row(children: [
CircularProgressIndicator(),
Text('Processing order...'),
]);
} else if (order.status == OrderStatus.shipped) {
return Row(children: [
Icon(Icons.local_shipping),
Text('Shipped on ${order.shippedDate}'),
]);
} else if (order.status == OrderStatus.delivered) {
return Row(children: [
Icon(Icons.check_circle),
Text('Delivered on ${order.deliveryDate}'),
]);
} else {
return Text('Unknown status');
}
}
}
// ✅ Good practice: Delegate to specialized widgets
class OrderStatus extends StatelessWidget {
final Order order;
@override
Widget build(BuildContext context) {
return switch (order.status) {
OrderStatus.processing => ProcessingOrderStatus(order: order),
OrderStatus.shipped => ShippedOrderStatus(order: order),
OrderStatus.delivered => DeliveredOrderStatus(order: order),
_ => UnknownOrderStatus(),
};
}
}
```
### Name Complex Conditions
**CR-R-04**: Give meaningful names to complex boolean conditions.
```dart
// ❌ Poor practice
if (user.subscription != null &&
user.subscription!.isActive &&
DateTime.now().isBefore(user.subscription!.expiryDate) &&
user.subscription!.plan == SubscriptionPlan.premium) {
showPremiumFeatures();
}
// ✅ Good practice
bool get isPremiumActive {
if (user.subscription == null) return false;
final isActive = user.subscription!.isActive;
final isNotExpired = DateTime.now().isBefore(user.subscription!.expiryDate);
final isPremiumPlan = user.subscription!.plan == SubscriptionPlan.premium;
return isActive && isNotExpired && isPremiumPlan;
}
// Usage
if (isPremiumActive) {
showPremiumFeatures();
}
```
## Predictability
### Consistent Return Types
**CR-P-01**: Use consistent return types for similar functions and methods.
```dart
// ❌ Poor practice: Inconsistent return types
Future<User?> getUser() async {
final response = await api.getUser();
return response.isSuccess ? User.fromJson(response.data) : null;
}
Future<List<Product>> getProducts() async {
final response = await api.getProducts();
if (!response.isSuccess) throw Exception('Failed to load products');
return response.data.map((json) => Product.fromJson(json)).toList();
}
// ✅ Good practice: Consistent return types using Result pattern
Future<Result<User>> getUser() async {
try {
final response = await api.getUser();
if (!response.isSuccess) {
return Result.failure(ApiError(response.errorMessage));
}
return Result.success(User.fromJson(response.data));
} catch (e) {
return Result.failure(UnexpectedError(e.toString()));
}
}
Future<Result<List<Product>>> getProducts() async {
try {
final response = await api.getProducts();
if (!response.isSuccess) {
return Result.failure(ApiError(response.errorMessage));
}
final products = response.data
.map((json) => Product.fromJson(json))
.toList();
return Result.success(products);
} catch (e) {
return Result.failure(UnexpectedError(e.toString()));
}
}
```
### Clear Function Naming
**CR-P-02**: Functions should have names that clearly indicate their purpose and behavior.
```dart
// ❌ Poor practice: Ambiguous names
Future<void> process() async { /* ... */ }
void handle(User user) { /* ... */ }
// ✅ Good practice: Clear, specific names
Future<void> processPayment(Order order) async { /* ... */ }
void handleUserRegistration(User user) { /* ... */ }
```
### Single Responsibility
**CR-P-03**: Functions should have a single responsibility and avoid hidden side effects.
```dart
// ❌ Poor practice: Function with hidden side effects
Future<User> getUser() async {
final user = await api.fetchUser();
analytics.logEvent('user_fetched'); // Hidden side effect
cache.save('user', user); // Hidden side effect
return user;
}
// ✅ Good practice: Explicit functions for each responsibility
Future<User> fetchUser() async {
return await api.fetchUser();
}
Future<void> logUserFetch() async {
analytics.logEvent('user_fetched');
}
Future<void> cacheUser(User user) async {
await cache.save('user', user);
}
// When using, all operations are explicit
Future<User> getUserWithLoggingAndCaching() async {
final user = await fetchUser();
await Future.wait([
logUserFetch(),
cacheUser(user),
]);
return user;
}
```
## Cohesion
### Form Validation Approaches
**CR-C-01**: Choose the appropriate form validation approach based on form complexity.
Field-level validation (for simple forms with independent fields):
```dart
class SimpleForm extends StatefulWidget {
@override
_SimpleFormState createState() => _SimpleFormState();
}
class _SimpleFormState extends State<SimpleForm> {
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
decoration: InputDecoration(labelText: 'Email'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter an email';
}
if (!value.contains('@')) {
return 'Please enter a valid email';
}
return null;
},
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
// Submit form
}
},
child: Text('Submit'),
),
],
),
);
}
}
```
Form-level validation (for complex forms with interdependent fields):
```dart
// Using a validation library like formz
class PasswordInput extends FormzInput<String, PasswordValidationError> {
const PasswordInput.pure() : super.pure('');
const PasswordInput.dirty([String value = '']) : super.dirty(value);
@override
PasswordValidationError? validator(String value) {
if (value.isEmpty) return PasswordValidationError.empty;
if (value.length < 8) return PasswordValidationError.tooShort;
return null;
}
}
class ConfirmPasswordInput extends FormzInput<String, ConfirmPasswordValidationError> {
const ConfirmPasswordInput.pure({this.password = ''}) : super.pure('');
const ConfirmPasswordInput.dirty({
required this.password,
String value = '',
}) : super.dirty(value);
final String password;
@override
ConfirmPasswordValidationError? validator(String value) {
if (value.isEmpty) return ConfirmPasswordValidationError.empty;
if (value != password) return ConfirmPasswordValidationError.mismatch;
return null;
}
}
```
### Keep Constants with Related Logic
**CR-C-02**: Define constants close to the related logic.
```dart
// ❌ Poor practice: Constants defined far from related logic
class Constants {
static const Duration animationDuration = Duration(milliseconds: 300);
static const Duration tooltipDelay = Duration(milliseconds: 500);
static const Duration refreshInterval = Duration(minutes: 5);
}
// Much later in codebase...
class AnimatedButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AnimatedContainer(
duration: Constants.animationDuration, // Hard to trace relationship
// ...
);
}
}
// ✅ Good practice: Constants defined near related logic
class AnimatedButton extends StatelessWidget {
// Duration clearly associated with this animation
static const Duration _animationDuration = Duration(milliseconds: 300);
@override
Widget build(BuildContext context) {
return AnimatedContainer(
duration: _animationDuration,
// ...
);
}
}
```
## State Management
**CR-SM-01**: Choose a consistent state management solution based on project complexity and team familiarity.
### BLoC Pattern
```dart
// Events
abstract class CounterEvent {}
class IncrementEvent extends CounterEvent {}
class DecrementEvent extends CounterEvent {}
// State
class CounterState {
final int count;
const CounterState(this.count);
}
// BLoC
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(const CounterState(0)) {
on<IncrementEvent>((event, emit) {
emit(CounterState(state.count + 1));
});
on<DecrementEvent>((event, emit) {
emit(CounterState(state.count - 1));
});
}
}
// Usage in UI
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => CounterBloc(),
child: CounterView(),
);
}
}
```
### Riverpod
```dart
// State class with freezed
@freezed
class CounterState with _$CounterState {
const factory CounterState({@Default(0) int count}) = _CounterState;
}
// Notifier with code generation
@riverpod
class CounterNotifier extends _$CounterNotifier {
@override
CounterState build() {
return const CounterState();
}
void increment() {
state = state.copyWith(count: state.count + 1);
}
void decrement() {
state = state.copyWith(count: state.count - 1);
}
}
// Usage in UI
class CounterPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final counter = ref.watch(counterNotifierProvider);
return Scaffold(
appBar: AppBar(title: const Text('Counter')),
body: Center(
child: Text(
'${counter.count}',
style: Theme.of(context).textTheme.displayLarge,
),
),
);
}
}
```
### Focused State Management
**CR-SM-02**: Break state into small, focused units to reduce coupling.
```dart
// ❌ Poor practice: Monolithic state management
@riverpod
class SuperAppStateNotifier extends _$SuperAppStateNotifier {
@override
SuperAppState build() {
return SuperAppState();
}
// User profile methods
void updateUserName(String name) { /* ... */ }
void updateUserAvatar(String url) { /* ... */ }
// Cart methods
void addToCart(Product product) { /* ... */ }
void removeFromCart(String productId) { /* ... */ }
void updateQuantity(String productId, int qty) { /* ... */ }
// Payment methods
void updatePaymentMethod(PaymentMethod method) { /* ... */ }
void applyPromoCode(String code) { /* ... */ }
// Order methods
Future<void> placeOrder() async { /* ... */ }
Future<void> cancelOrder(String orderId) async { /* ... */ }
}
// ✅ Good practice: Split state into focused units
@riverpod
class UserProfileNotifier extends _$UserProfileNotifier {
@override
UserProfileState build() {
return const UserProfileState();
}
void updateName(String name) {
state = state.copyWith(name: name);
}
void updateAvatar(String url) {
state = state.copyWith(avatarUrl: url);
}
}
@riverpod
class CartNotifier extends _$CartNotifier {
@override
CartState build() {
return const CartState();
}
void addItem(Product product) { /* ... */ }
void removeItem(String productId) { /* ... */ }
void updateQuantity(String productId, int quantity) { /* ... */ }
}
```
## Performance Optimization
### Optimized ListView
**CR-PO-01**: Use efficient list rendering methods.
```dart
// ❌ Poor practice: Inefficient list rendering
ListView(
children: products.map((product) => ProductListItem(product: product)).toList(),
);
// ✅ Good practice: Efficient list rendering
ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) => ProductListItem(product: products[index]),
);
```
### Image Optimization
**CR-PO-02**: Optimize image loading and caching.
```dart
// ❌ Poor practice: Unoptimized image loading
Image.network(product.imageUrl)
// ✅ Good practice: Optimized image loading
CachedNetworkImage(
imageUrl: product.imageUrl,
placeholder: (context, url) => const Center(
child: CircularProgressIndicator(),
),
errorWidget: (context, url, error) => const Icon(Icons.error),
fit: BoxFit.cover,
memCacheWidth: 400, // Size adjustment for memory efficiency
)
```
### Computation Optimization
**CR-PO-03**: Memoize expensive computations.
```dart
// ❌ Poor practice: Expensive computation in build method
@override
Widget build(BuildContext context) {
final sortedProducts = products
.where((p) => p.category == selectedCategory)
.toList()
..sort((a, b) => a.price.compareTo(b.price));
return ListView.builder(
itemCount: sortedProducts.length,
itemBuilder: (context, index) {
return ProductItem(product: sortedProducts[index]);
},
);
}
// ✅ Good practice: Memoized computation
final filteredProductsProvider = Provider<List<Product>>((ref) {
final products = ref.watch(productsProvider);
final selectedCategory = ref.watch(selectedCategoryProvider);
return products
.where((p) => p.category == selectedCategory)
.toList()
..sort((a, b) => a.price.compareTo(b.price));
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final sortedProducts = ref.watch(filteredProductsProvider);
return ListView.builder(
itemCount: sortedProducts.length,
itemBuilder: (context, index) {
return ProductItem(product: sortedProducts[index]);
},
);
}
```
## Error & Loading State Handling
**CR-EL-01**: Properly handle loading, error, and empty states for asynchronous operations.
```dart
// Riverpod example
class ProductListView extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final productsAsync = ref.watch(productsProvider);
return productsAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stack) => ErrorView(
message: 'Failed to load products',
onRetry: () => ref.refresh(productsProvider),
),
data: (products) {
if (products.isEmpty) {
return const EmptyStateView(
message: 'No products available',
icon: Icons.inventory_2_outlined,
);
}
return ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) => ProductListItem(
product: products[index],
),
);
},
);
}
}
```
## Modern Dart Features
**CR-DF-01**: Leverage modern Dart features for cleaner, more expressive code.
```dart
// Pattern matching with switch expressions
String getStatusMessage(OrderStatus status) => switch(status) {
OrderStatus.pending => 'Order is pending',
OrderStatus.processing => 'Order is being processed',
OrderStatus.shipped => 'Order has been shipped',
OrderStatus.delivered => 'Order has been delivered',
OrderStatus.cancelled => 'Order has been cancelled',
};
// Records for related return values
(int count, double total) getCartSummary() {
final items = _getCartItems();
return (
items.length,
items.fold(0.0, (sum, item) => sum + item.price * item.quantity),
);
}
// Extension types for type safety
extension type EmailAddress(String value) {
bool get isValid =>
value.isNotEmpty && value.contains('@') && value.contains('.');
String get domain => value.split('@').last;
}
// Enhanced enums
enum PaymentMethod {
creditCard(icon: Icons.credit_card, label: 'Credit Card'),
paypal(icon: Icons.payments, label: 'PayPal'),
applePay(icon: Icons.apple, label: 'Apple Pay'),
googlePay(icon: Icons.android, label: 'Google Pay');
final IconData icon;
final String label;
const PaymentMethod({required this.icon, required this.label});
bool get isDigitalWallet =>
this == PaymentMethod.applePay || this == PaymentMethod.googlePay;
}
```
## Testing
**CR-T-01**: Implement a comprehensive testing pyramid with unit, widget, and integration tests.
```dart
// Unit tests
test('Counter increments correctly', () {
final counter = Counter();
counter.increment();
expect(counter.value, 1);
});
// Widget tests
testWidgets('Counter UI displays correct value', (tester) async {
await tester.pumpWidget(MaterialApp(home: CounterWidget()));
expect(find.text('0'), findsOneWidget);
});
// Integration tests
testWidgets('Full login flow', (tester) async {
await tester.pumpWidget(MyApp());
await tester.enterText(find.byType(TextField).first, 'user@example.com');
await tester.enterText(find.byType(TextField).last, 'password');
await tester.tap(find.byType(ElevatedButton));
await tester.pumpAndSettle();
expect(find.text('Welcome'), findsOneWidget);
});
```
## Resource Management
### Asset Management
**CR-RM-01**: Centralize asset paths.
```dart
// ❌ Poor practice: Hardcoded asset paths
Image.asset('assets/images/logo.png')
// ✅ Good practice: Centralized asset paths
class AppAssets {
static const String logo = 'assets/images/logo.png';
static const String onboarding1 = 'assets/images/onboarding/step1.png';
static const String onboarding2 = 'assets/images/onboarding/step2.png';
static const String placeholder = 'assets/images/placeholder.png';
}
// Usage
Image.asset(AppAssets.logo)
```
### String Management
**CR-RM-02**: Use a centralized approach for strings.
```dart
// ❌ Poor practice: Hardcoded strings
Text('Welcome back!')
// ✅ Good practice: Centralized strings
class AppStrings {
static const String welcomeBack = 'Welcome back!';
static const String signIn = 'Sign In';
static const String emailHint = 'Enter your email';
static const String passwordHint = 'Enter your password';
static const String forgotPassword = 'Forgot password?';
}
// Usage
Text(AppStrings.welcomeBack)
```
## Internationalization
**CR-I-01**: Design your app for internationalization from the start.
```dart
// Using the intl package with ARB files
// lib/l10n/app_en.arb (English)
{
"helloWorld": "Hello World!",
"welcome": "Welcome {name}",
"@welcome": {
"placeholders": {
"name": {
"type": "String"
}
}
},
"itemCount": "{count, plural, =0{No items} =1{1 item} other{{count} items}}",
"@itemCount": {
"placeholders": {
"count": {
"type": "int"
}
}
}
}
// Usage in code
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final localizations = AppLocalizations.of(context)!;
return Scaffold(
appBar: AppBar(
title: Text(localizations.helloWorld),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(localizations.welcome('John')),
Text(localizations.itemCount(5)),
],
),
),
);
}
}
```
## CI/CD
**CR-CD-01**: Implement a CI/CD pipeline for your Flutter project.
```yaml
# GitHub Actions workflow example
name: Flutter CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.29.2'
channel: 'stable'
- name: Install dependencies
run: flutter pub get
- name: Check formatting
run: dart format --set-exit-if-changed .
- name: Analyze project source
run: flutter analyze
- name: Run tests
run: flutter test --coverage
```
## Environment Configuration
**CR-EC-01**: Manage different environments (development, staging, production).
```dart
// config.dart
enum Environment { dev, staging, prod }
class Config {
static Environment environment = Environment.dev;
static String get baseUrl {
switch (environment) {
case Environment.dev:
return 'https://dev-api.example.com';
case Environment.staging:
return 'https://staging-api.example.com';
case Environment.prod:
return 'https://api.example.com';
}
}
static bool get enableLogging => environment != Environment.prod;
static Future<void> initialize(Environment env) async {
environment = env;
// Initialize services based on environment
}
}
```
## Feature Flags
**CR-FF-01**: Use feature flags for gradual feature rollout and A/B testing.
```dart
// Service class for feature flags
class FeatureFlagService {
final FirebaseRemoteConfig _remoteConfig;
FeatureFlagService(this._remoteConfig);
bool get isDarkModeEnabled => _remoteConfig.getBool('enable_dark_mode');
bool get isNewPaymentUIEnabled => _remoteConfig.getBool('new_payment_ui');
bool get areBetaFeaturesEnabled => _remoteConfig.getBool('beta_features');
String get minimumAppVersion => _remoteConfig.getString('minimum_app_version');
}
// Usage
class PaymentScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final featureFlags = ref.watch(featureFlagServiceProvider);
return featureFlags.isNewPaymentUIEnabled
? NewPaymentUI()
: LegacyPaymentUI();
}
}
```
---
Following these cursor rules will help you create maintainable, performant, and high-quality Flutter applications. These principles establish a consistent approach across teams and projects, allowing for more efficient delivery of quality software.

Flutter CursorRules: A Brief Introduction

What Are CursorRules?

CursorRules are a standardized set of best practices for Flutter development that promote clean, maintainable, and efficient code. These rules serve as a shared language for our development team and help ensure consistency across our Flutter projects.

Core Principles

Our CursorRules are built on four foundational principles:

  1. Readability: Code should be easily understood by other developers.
  2. Predictability: Functions and components should behave as expected.
  3. Cohesion: Related code should stay together with clear purpose.
  4. Coupling: Dependencies between different parts should be minimized.

Key Benefits

By adopting these CursorRules, you will:

  • Write more maintainable code that's easier to review and modify
  • Reduce bugs through consistent patterns and predictable behavior
  • Improve team collaboration through shared conventions
  • Create more performant applications with optimized UI and state management
  • Ensure accessibility and internationalization from the start

Rules At A Glance

Our CursorRules cover several critical areas:

  • Project Structure: Feature-based organization following clean architecture
  • Code Readability: Named constants, widget composition, and specialized components
  • Predictable Code: Consistent return types, clear naming, and single responsibility
  • State Management: Focused state containers with clear boundaries
  • Performance: Optimized lists, image handling, and computation
  • Testing: Comprehensive unit, widget, and integration testing
  • Resource Management: Centralized assets and strings
  • CI/CD: Automated testing and deployment pipelines

Getting Started

The full documentation includes practical examples for each rule. Begin by focusing on project structure and readability rules, then gradually incorporate the more advanced patterns.

When reviewing code or writing new features, reference these rules by their identifiers (e.g., CR-R-01) to provide specific, actionable feedback to team members.

Remember: These rules aren't rigid laws but guidelines to help us build better Flutter applications together. The goal is to create code that's maintainable, performant, and enjoyable to work with.

Flutter 커서룰(CursorRules)

깔끔하고, 유지보수가 용이하며, 효율적인 코드를 위한 Flutter 개발 표준 모범 사례 모음입니다. 이 규칙들은 개발 및 코드 리뷰 중 쉽게 참조할 수 있도록 고유 식별자(CR-XX-XX)로 정리되어 있습니다.

핵심 원칙

  • 가독성: 이해하기 쉬운 코드
  • 예측 가능성: 일관된 동작 및 패턴
  • 응집성: 관련 코드는 함께 유지
  • 결합도: 컴포넌트 간 최소한의 의존성

주요 영역

  • 프로젝트 구조 (기능 기반 구성)
  • 코드 가독성 (명명된 상수, 위젯 구성)
  • 상태 관리 (집중된 컨테이너)
  • 성능 최적화 (리스트/이미지 처리)
  • 최신 Dart 기능
  • 테스트 전략
  • 리소스 관리
  • CI/CD 구현

이 규칙들은 일관된 패턴과 관행으로 더 나은 Flutter 애플리케이션을 구축하는 데 도움을 주는 가이드라인 역할을 합니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment