Flutter Code Quality & Formatting Guide#

This guide covers linting, formatting, and automated code quality checks for the Coves mobile app.


Tools Overview#

1. flutter analyze (Static Analysis / Linting)#

Checks code for errors, warnings, and style issues based on analysis_options.yaml.

2. dart format (Code Formatting)#

Auto-formats code to Dart style guide (spacing, indentation, line length).

3. analysis_options.yaml (Configuration)#

Defines which lint rules are enforced.


Quick Start#

Run All Quality Checks#

# Format code
dart format .

# Analyze code
flutter analyze

# Run tests
flutter test

1. Code Formatting with dart format#

Basic Usage#

# Check if code needs formatting (exits with 1 if changes needed)
dart format --output=none --set-exit-if-changed .

# Format all Dart files
dart format .

# Format specific directory
dart format lib/

# Format specific file
dart format lib/services/coves_api_service.dart

# Dry run (show what would change without modifying files)
dart format --output=show .

Dart Formatting Rules#

  • 80-character line limit (configurable in analysis_options.yaml)
  • 2-space indentation
  • Trailing commas for better git diffs
  • Consistent spacing around operators

Example: Trailing Commas#

// ❌ Without trailing comma (bad for diffs)
Widget build(BuildContext context) {
  return Container(
    child: Text('Hello')
  );
}

// ✅ With trailing comma (better for diffs)
Widget build(BuildContext context) {
  return Container(
    child: Text('Hello'),  // ← Trailing comma
  );
}

2. Static Analysis with flutter analyze#

Basic Usage#

# Analyze entire project
flutter analyze

# Analyze specific directory
flutter analyze lib/

# Analyze specific file
flutter analyze lib/services/coves_api_service.dart

# Analyze with verbose output
flutter analyze --verbose

Understanding Output#

  error • Business logic in widgets • lib/screens/feed.dart:42 • custom_rule
warning • Missing documentation • lib/services/api.dart:10 • public_member_api_docs
   info • Line too long • lib/models/post.dart:55 • lines_longer_than_80_chars
  • error: Must fix (breaks build in CI)
  • warning: Should fix (may break CI depending on config)
  • info: Optional suggestions (won't break build)

3. Upgrading to Stricter Lint Rules#

Replace your current analysis_options.yaml with the stricter version:

# Backup current config
cp analysis_options.yaml analysis_options.yaml.bak

# Use recommended config
cp analysis_options_recommended.yaml analysis_options.yaml

# Test it
flutter analyze

Option B: Use Very Good Analysis (Most Strict)#

For maximum code quality, use Very Good Ventures' lint rules:

# pubspec.yaml
dev_dependencies:
  very_good_analysis: ^6.0.0
# analysis_options.yaml
include: package:very_good_analysis/analysis_options.yaml

Option C: Customize Incrementally#

Start with your current rules and add these high-value rules:

# analysis_options.yaml
include: package:flutter_lints/flutter.yaml

linter:
  rules:
    # High-value additions
    - prefer_const_constructors
    - prefer_const_literals_to_create_immutables
    - prefer_final_locals
    - avoid_print
    - require_trailing_commas
    - prefer_single_quotes
    - lines_longer_than_80_chars
    - unawaited_futures

4. IDE Integration#

VS Code#

Add to .vscode/settings.json:

{
  "dart.lineLength": 80,
  "editor.formatOnSave": true,
  "editor.formatOnType": false,
  "editor.rulers": [80],
  "dart.showLintNames": true,
  "dart.previewFlutterUiGuides": true,
  "dart.previewFlutterUiGuidesCustomTracking": true,
  "[dart]": {
    "editor.formatOnSave": true,
    "editor.selectionHighlight": false,
    "editor.suggest.snippetsPreventQuickSuggestions": false,
    "editor.suggestSelection": "first",
    "editor.tabCompletion": "onlySnippets",
    "editor.wordBasedSuggestions": "off"
  }
}

Android Studio / IntelliJ#

  1. Settings → Editor → Code Style → Dart
    • Set line length to 80
    • Enable "Format on save"
  2. Settings → Editor → Inspections → Dart
    • Enable all inspections

Automate quality checks before every commit using lefthook.

Setup#

# Install lefthook
brew install lefthook  # macOS
# or
curl -1sLf 'https://dl.cloudsmith.io/public/evilmartians/lefthook/setup.deb.sh' | sudo -E bash
sudo apt install lefthook  # Linux

# Initialize
lefthook install

Configuration#

Create lefthook.yml in project root:

# lefthook.yml
pre-commit:
  parallel: true
  commands:
    # Format Dart code
    format:
      glob: "*.dart"
      run: dart format {staged_files} && git add {staged_files}

    # Analyze Dart code
    analyze:
      glob: "*.dart"
      run: flutter analyze {staged_files}

    # Run quick tests (optional)
    # test:
    #   glob: "*.dart"
    #   run: flutter test

pre-push:
  commands:
    # Full test suite before push
    test:
      run: flutter test

    # Full analyze before push
    analyze:
      run: flutter analyze

Alternative: Simple Git Hook#

Create .git/hooks/pre-commit:

#!/bin/bash

echo "Running dart format..."
dart format .

echo "Running flutter analyze..."
flutter analyze

if [ $? -ne 0 ]; then
  echo "❌ Analyze failed. Fix issues before committing."
  exit 1
fi

echo "✅ Pre-commit checks passed!"

Make it executable:

chmod +x .git/hooks/pre-commit

6. CI/CD Integration#

GitHub Actions#

Create .github/workflows/code_quality.yml:

name: Code Quality

on:
  pull_request:
    branches: [main, develop]
  push:
    branches: [main, develop]

jobs:
  analyze:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.24.0'
          channel: 'stable'

      - name: Install dependencies
        run: flutter pub get

      - name: Verify formatting
        run: dart format --output=none --set-exit-if-changed .

      - name: Analyze code
        run: flutter analyze

      - name: Run tests
        run: flutter test

GitLab CI#

# .gitlab-ci.yml
stages:
  - quality
  - test

format:
  stage: quality
  image: cirrusci/flutter:stable
  script:
    - flutter pub get
    - dart format --output=none --set-exit-if-changed .

analyze:
  stage: quality
  image: cirrusci/flutter:stable
  script:
    - flutter pub get
    - flutter analyze

test:
  stage: test
  image: cirrusci/flutter:stable
  script:
    - flutter pub get
    - flutter test

7. Common Issues & Solutions#

Issue: "lines_longer_than_80_chars"#

Solution: Break long lines with trailing commas

// Before
final user = User(name: 'Alice', email: 'alice@example.com', age: 30);

// After
final user = User(
  name: 'Alice',
  email: 'alice@example.com',
  age: 30,
);

Issue: "prefer_const_constructors"#

Solution: Add const where possible

// Before
return Container(child: Text('Hello'));

// After
return const Container(child: Text('Hello'));

Issue: "avoid_print"#

Solution: Use debugPrint with kDebugMode

// Before
print('Error: $error');

// After
if (kDebugMode) {
  debugPrint('Error: $error');
}

Issue: "unawaited_futures"#

Solution: Either await or use unawaited()

// Before
someAsyncFunction();  // Warning

// After - Option 1: Await
await someAsyncFunction();

// After - Option 2: Explicitly ignore
import 'package:flutter/foundation.dart';
unawaited(someAsyncFunction());

8. Project-Specific Rules#

Current Configuration#

We use flutter_lints: ^5.0.0 with default rules.

  1. Week 1: Add format-on-save to IDEs
  2. Week 2: Add pre-commit formatting hook
  3. Week 3: Enable stricter analysis_options.yaml
  4. Week 4: Add CI/CD checks
  5. Week 5: Fix all existing violations
  6. Week 6: Enforce in CI (fail builds on violations)

Custom Rules for Coves#

Add these to analysis_options.yaml for Coves-specific quality:

analyzer:
  errors:
    # Treat these as errors (not warnings)
    missing_required_param: error
    missing_return: error

  exclude:
    - '**/*.g.dart'
    - '**/*.freezed.dart'
    - 'packages/atproto_oauth_flutter/**'

linter:
  rules:
    # Architecture enforcement
    - avoid_print
    - prefer_const_constructors
    - prefer_final_locals

    # Code quality
    - require_trailing_commas
    - lines_longer_than_80_chars

    # Safety
    - unawaited_futures
    - close_sinks
    - cancel_subscriptions

9. Quick Reference#

Daily Workflow#

# Before committing
dart format .
flutter analyze
flutter test

# Or use pre-commit hook (automated)

Before PR#

# Full quality check
dart format --output=none --set-exit-if-changed .
flutter analyze
flutter test --coverage

Fix Formatting Issues#

# Auto-fix all formatting
dart format .

# Fix specific file
dart format lib/screens/home/feed_screen.dart

Ignore Specific Warnings#

// Ignore for one line
// ignore: avoid_print
print('Debug message');

// Ignore for entire file
// ignore_for_file: avoid_print

// Ignore for block
// ignore: lines_longer_than_80_chars
final veryLongVariableName = 'This is a very long string that exceeds 80 characters';

10. Resources#

Official Documentation#

Community Resources#


Next Steps#

  1. ✅ Review analysis_options_recommended.yaml
  2. ⬜ Decide on strictness level (current / recommended / very_good)
  3. ⬜ Set up IDE format-on-save
  4. ⬜ Create pre-commit hooks
  5. ⬜ Add CI/CD quality checks
  6. ⬜ Schedule time to fix existing violations
  7. ⬜ Enforce in team workflow