Dart Code Linter (DCL)

website·Indexed 36 days ago

https://dcl.apps.bancolombia.com/

DCL is a tool designed to improve the quality and consistency of Dart code. It provides a comprehensive set of lint rules across Dart, Flutter, Flame, and Intl, covering categories such as performance, style, and warnings to help developers maintain high coding standards.

Tokens
32.3K
Snippets
168
Records
211
Agent score
56%

What's inside DCL

  1. Overview of DCL for Dart and Flutter

    DCL is a static analysis toolkit designed to identify and fix problems in Dart and Flutter code, including potential runtime bugs, best practice violations, and styling issues. It provides over 70 built-in rules for code validation and allows for custom configurations to meet specific project needs.
  2. Enforce identifier length constraints with prefer-correct-identifier-length

    The prefer-correct-identifier-length rule ensures that variable names in classes, functions, extensions, and mixins, as well as enum values, fall within a specified length range. This prevents identifiers from being too cryptic (too short) or excessively verbose (too long).
  3. Enforce member ordering in Dart classes

    The member-ordering rule (added in v1.0.0) enforces a specific sequence for class members. It supports separate configurations for regular classes via the order property and Flutter widget classes via the widgets-order property. Members are grouped by patterns (e.g., visibility, modifiers, and type) or by specific method/field names.
  4. Understand and calculate the Maintainability Index metric

    The Maintainability Index (MI) is a software metric that measures how easy source code is to support and change. It is calculated using a factored formula based on three other metrics: Source Lines Of Code (SLOC), Cyclomatic Complexity (CYCLO), and Halstead Volume (HALVOL).

    The linter uses the Microsoft Visual Studio derivative version, which shifts the scale to a range of 0 to 100:

    MI = max(0, (171 − 5.2 * log(HALVOL) − 0.23 * log(CYCLO) − 16.2 * log(SLOC)) * 100 / 171)

    Note: This metric is considered experimental and should be weighted less heavily than other metrics.

  5. Understand and configure the Source Lines of Code (SLOC) metric

    The Source Lines of Code (SLOC) metric provides an approximate count of source code lines within a function or method, specifically excluding blank lines and comment lines. It is used to estimate program size, development effort, programming productivity, and long-term maintainability.
  6. Configure the avoid-late-keyword lint rule

    The avoid-late-keyword rule can be customized via the dart_code_linter configuration to allow specific exceptions:

    • allow-initialized (default: false): When set to true, the linter will not warn about late variables that are initialized at the point of declaration.
    • ignored-types (default: [AnimationController]): A list of types that are permitted to use the late keyword without triggering a warning.
    dart_code_linter:
      rules:
        avoid-late-keyword:
          allow-initialized: true
  7. Use byName to retrieve Dart enum values by string

    Since Dart 2.15, you should use the byName method on enum values to retrieve an enum member by its string name. This is more concise than using firstWhere to match the name property.

    Caveat: The byName method will throw an exception if the enum does not contain a value matching the provided string name.

    // Avoid this (manual search)
    final styleDefinition = StyleDefinition.values.firstWhere(
      (def) => def.name == json['styleDefinition'],
    );
    
    // Do this (using byName)
    final styleDefinition = StyleDefinition.values.byName(json['styleDefinition']);
  8. Prevent double slashes in Dart import and export directives

    The avoid-double-slash-imports lint rule (introduced in version 1.0.0) warns when an import, export, or part directive contains a double slash (//) in the URI. While double slashes are technically valid in URIs, they can cause the program to fail to run in certain environments. This rule ensures paths are clean and consistent.
    // ❌ Bad: Contains double slashes
    import 'package:test//material.dart';
    import '../../..//rule_utils_test.dart';
    export 'package:mocktail//good_file.dart';
    part '../../..//individual/rules/empty.dart';
    
    // ✅ Good: Single slashes used
    import 'package:test/material.dart';
    import '../../../rule_utils_test.dart';
    export 'package:mocktail/good_file.dart';
    part '../../../individual/rules/empty.dart';
  9. Avoid using mutable global state in Dart

    The avoid-global-state rule prevents the use of top-level variables and static class members that are neither final nor const. Mutable global state is discouraged because it distributes application state across multiple files, leaves state unprotected from modification anywhere in the app, and increases debugging complexity. Developers should use dedicated state management solutions instead of mutable global variables.
    // ❌ Bad: Mutable top-level and static variables
    var answer = 42;
    var evenNumbers = [1, 2, 3].where((element) => element.isEven);
    
    class Foo {
      static int? bar;
    }
    
    // ✅ Good: Use const or final for top-level variables
    const answer = 42;
    final evenNumbers = [1, 2, 3].where((element) => element.isEven);
    
    class Foo {
      // Static variables should be initialized or final to avoid mutable global state
      static int bar = 42;
    }
  10. Enforce single widget per file with prefer-single-widget-per-file

    The prefer-single-widget-per-file rule warns when a Dart file contains more than one widget. This ensures that each widget is isolated in its own file, adhering to the single responsibility principle.
    // some_widget.dart
    class SomeWidget extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Text('Single widget per file');
      }
    }
  11. Use async/await instead of .then() for Future results

    The prefer-async-await lint recommends using the async/await syntax to handle Future results instead of using .then() callbacks. Using await improves readability and prevents common errors associated with mixing await and .then(), as awaiting a .then() call only awaits the completion of the callback itself rather than the original Future's intended flow.
    // Bad: Using .then() callbacks
    Future<void> main() async {
      someFuture.then((result) => handleResult(result));
      await (foo.asyncMethod()).then((result) => handleResult(result));
    }
    
    // Good: Using async/await
    Future<void> main() async {
      final result = await someFuture;
      handleResult(result);
    
      final anotherResult = await foo.asyncMethod();
      handleResult(anotherResult);
    }
  12. Avoid initializing late final variables in onMount

    Do not initialize late final variables inside a Component's onMount method. Because components may be removed and re-added to the UI tree, attempting to re-initialize a late final variable during a subsequent onMount call will trigger a runtime exception. To fix this, use a non-final variable if the value needs to be assigned during the mount phase.
    // ❌ Bad: late final will throw an exception if the component is remounted
    class MyComponent extends Component {
      late final int x;
    
      @override
      void onMount() {
        x = 1; // LINT: avoid-initializing-in-on-mount
      }
    }
    
    // ✅ Good: Use a regular variable for values initialized in onMount
    class MyComponent extends Component {
      int x;
    
      @override
      void onMount() {
        x = 1;
      }
    }