Thank you for contributing to LexiLingo! This document provides guidelines for contributing to the project.
- Getting Started
- Development Setup
- Branching Strategy
- Coding Standards
- Commit Guidelines
- Pull Request Process
- Code Review
- Flutter SDK 3.24.0 or higher
- Dart SDK 3.8.1 or higher
- Git
- IDE: VS Code or Android Studio (recommended)
# Clone the repository
git clone https://github.com/InfinityZero3000/LexiLingo.git
cd LexiLingo
# Install dependencies
cd flutter-app
flutter pub get
# Run the app
flutter run- Copy
.env.exampleto.env(if exists) - Add your API keys and configurations
- Never commit
.envfile
- Dart
- Flutter
- GitLens
- Better Comments
- Error Lens
- Flutter
- Dart
- GitToolBox
We follow Git Flow branching model. See GIT_WORKFLOW.md for detailed guidelines.
main → Production-ready code
develop → Integration branch
feature/* → New features
bugfix/* → Bug fixes
hotfix/* → Critical production fixes
release/* → Release preparation
feature/LEXI-123-add-vocabulary-feature
bugfix/LEXI-200-fix-login-crash
hotfix/LEXI-500-critical-crash-fix
release/v1.0.0We follow Clean Architecture principles:
lib/
├── core/ # Shared utilities
│ ├── di/ # Dependency Injection
│ ├── error/ # Error handling
│ ├── usecase/ # Base use case
│ ├── utils/ # Utilities
│ └── services/ # Shared services
└── features/ # Feature modules
└── [feature]/
├── domain/ # Business logic
│ ├── entities/
│ ├── repositories/
│ └── usecases/
├── data/ # Data layer
│ ├── models/
│ ├── datasources/
│ └── repositories/
└── presentation/ # UI layer
├── pages/
├── widgets/
└── providers/
// Good
user_repository.dart
vocab_word_model.dart
get_words_usecase.dart
// Bad
UserRepository.dart
vocabWordModel.dart
GetWords.dart// Good - PascalCase
class VocabRepository {}
class UserEntity {}
class GetWordsUseCase {}
// Bad
class vocabRepository {}
class user_entity {}// Good - camelCase
final userName = 'John';
final isAuthenticated = true;
final wordCount = 10;
// Bad
final UserName = 'John';
final is_authenticated = true;// Good - lowerCamelCase
const maxRetryCount = 3;
const apiTimeout = Duration(seconds: 30);
// For truly global constants, can use UPPER_SNAKE_CASE
const API_BASE_URL = 'https://api.example.com';class MyClass {
// Private with underscore
final String _privateField;
int _privateCounter = 0;
void _privateMethod() {}
}// 1. Dart imports
import 'dart:async';
import 'dart:io';
// 2. Flutter imports
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
// 3. Third-party packages
import 'package:provider/provider.dart';
import 'package:get_it/get_it.dart';
// 4. Local imports
import 'package:lexilingo_app/core/usecase/usecase.dart';
import 'package:lexilingo_app/features/auth/domain/entities/user_entity.dart';// Imports
import 'package:flutter/material.dart';
// Constants
const kMaxWords = 100;
// Main class
class VocabPage extends StatefulWidget {
const VocabPage({super.key});
@override
State<VocabPage> createState() => _VocabPageState();
}
// Private class
class _VocabPageState extends State<VocabPage> {
// State variables
// Lifecycle methods
@override
void initState() {
super.initState();
}
// Build method
@override
Widget build(BuildContext context) {
return Container();
}
// Private methods
void _handleAction() {}
}
// Helper classes (if needed)
class _HelperClass {}// Good
const Text('Hello');
const SizedBox(height: 16);
// Bad
Text('Hello');
SizedBox(height: 16);// Good
String? nullableString;
final nonNullString = nullableString ?? 'default';
final length = nullableString?.length ?? 0;
// Bad
String nullableString; // Should be String?
final length = nullableString!.length; // Avoid ! when possible// Good
Future<List<Word>> getWords() async {
try {
final words = await repository.getWords();
return words;
} catch (e) {
throw Exception('Failed to load words');
}
}
// Bad
Future<List<Word>> getWords() {
return repository.getWords().then((words) {
return words;
}).catchError((e) {
throw Exception('Failed to load words');
});
}// Good
try {
await performOperation();
} on NetworkException catch (e) {
logger.error('Network error: $e');
throw NetworkFailure(e.message);
} on CacheException catch (e) {
logger.error('Cache error: $e');
throw CacheFailure(e.message);
} catch (e) {
logger.error('Unexpected error: $e');
throw UnexpectedFailure(e.toString());
}
// Bad
try {
await performOperation();
} catch (e) {
print(e); // Don't use print
}// Good - Extract widgets
class UserProfile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
_buildHeader(),
_buildContent(),
_buildFooter(),
],
);
}
Widget _buildHeader() => Container();
Widget _buildContent() => Container();
Widget _buildFooter() => Container();
}
// Bad - Nested widgets
class UserProfile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
child: Row(
children: [
Container(
child: Column(
// Deep nesting...
),
),
],
),
),
],
);
}
}Follow Conventional Commits specification:
<type>(<scope>): <subject>
<body>
<footer>
feat: New featurefix: Bug fixdocs: Documentationstyle: Formattingrefactor: Code restructuringperf: Performancetest: Testingchore: Maintenanceci: CI/CDbuild: Build system
feat(vocabulary): add word search functionality
fix(auth): resolve login crash on iOS
docs(readme): update setup instructions
refactor(core): apply clean architecture
test(chat): add unit tests for message serviceSee GIT_WORKFLOW.md for detailed commit guidelines.
- Self-review your code
- Run all tests locally
- Update documentation
- Sync with latest develop
- No merge conflicts
- CI checks passing
- Small: < 100 lines (Ideal)
- Medium: 100-400 lines (Good)
- Large: > 400 lines (Should be split)
We provide a PR template. Fill it completely:
- Description
- Type of change
- Testing performed
- Screenshots (if UI changes)
- Checklist
- Create PR
- Assign reviewers (at least 1)
- Address review comments
- Get approval
- Merge (squash and merge recommended)
- Delete branch
- Review within 4 hours (first pass)
- Be constructive and respectful
- Explain why, not just what
- Use comment types:
- MUST FIX
- SHOULD FIX
- 💡 SUGGESTION
- ❓ QUESTION
- 🎉 PRAISE
- Don't take feedback personally
- Respond to all comments
- Ask questions if unclear
- Thank reviewers
test/
├── unit/
│ ├── core/
│ └── features/
├── widget/
│ └── features/
└── integration/
└── flows/
// Good test
test('should return list of words when repository call is successful', () async {
// Arrange
when(mockRepository.getWords()).thenAnswer((_) async => tWordList);
// Act
final result = await useCase(NoParams());
// Assert
expect(result, equals(tWordList));
verify(mockRepository.getWords());
verifyNoMoreInteractions(mockRepository);
});- Aim for > 80% coverage
- Focus on business logic (use cases, repositories)
- Test edge cases and error scenarios
- Check if really needed
- Evaluate package quality:
- Active maintenance
- Good documentation
- Community support
- Pub score > 100
- Add to
pubspec.yaml - Document usage in PR
# Check outdated packages
flutter pub outdated
# Update all packages
flutter pub upgrade
# Update specific package
flutter pub upgrade package_nameWhen reporting bugs:
- Use GitHub Issues
- Provide clear title
- Describe steps to reproduce
- Include expected vs actual behavior
- Add screenshots/videos
- Include device/OS information
- Add relevant logs
When requesting features:
- Use GitHub Issues
- Describe the problem
- Propose solution
- Explain use cases
- Consider alternatives
- Questions: GitHub Discussions
- Bugs: GitHub Issues
- Security: Email to security@lexilingo.com
- Chat: Team Slack/Discord
By contributing, you agree that your contributions will be licensed under the project's license.
Thank you for contributing to LexiLingo! Your efforts help make this project better for everyone.
For detailed branching strategy, see: GIT_WORKFLOW.md
For quick reference, see: GIT_QUICK_REFERENCE.md
Last Updated: January 10, 2026