DCM lint fix: prefer-null-aware-spread | `...?playersMap['new'],`

Up Your Dart and Flutter Programming Game: Learning by Linting

Due to an innate burden that saddles me with self-comparisons of everything I do with the likes of highly talented software engineers, I can never consider myself to be a great developer (albeit context is everything, and I certainly aspire to be). However, in my pursuit of that ever-elusive aspirational goal, a few primary concepts I’ve learned (and am still learning) in the last few years stand out with which I believe have helped me to expand my horizons the most.

Coming from a fairly lengthy procedural-based development background, the first couple topics I focused on gaining experience with are likely already quite common to other programmers, for which I’ll keep the elaborations below brief. However, …

the third topic, I believe, may be an undervalued learning opportunity for a lot of developers.

The three most significant advances in my learning adventures into mobile app development via Flutter and Dart have stemmed primarily from:

1) Learning OOP (and SOLID)
2) Learning about design patterns
3) Working with lint analyzers

OOP

I must say that after a lifetime of procedural programming with various procedural languages — like AutoLISP, ASP 2.0, JavaScript, JSP and PHP — and while it is still a work in progress, outside of learning Flutter, learning OOP has been the most rewarding of learned experiences I’ve had in the last few years.

From personal experience, the transition from procedural to OOP is in no way a natural occurrence. Fortunately, a presentation by Sandi Metz helped with insights into a lot of OOP principles, like, “factories are where conditionals go to die”—Polly Want a Message. A couple other talks of hers were also filled with a lot of valuable goodies, like her Rail Conference talks on “All the Little Things” (refactoring) and “Get a Whiff of This” (code smells).

Design Patterns

Learning design patterns is also still a work in progress. Going through dozens of patterns and being aware of the three types of patterns is certainly helpful, but for me, studying a bunch of individual patterns for situations you may never encounter feels unproductive. And although the certificate program from Refactoring.Guru felt quite comprehensive, I’ve yet to find an engaging interactive way of really learning the fundamentals of the various patterns, short of just studying them one at a time. Some good news, however, is I’ve got the repository pattern down fairly well thanks to Flutter Bloc. 🥳

Learning via Linting

While learning pure OOP will likely be a lifelong work in progress, and with discovering design patterns naturally when deriving solutions, I’ve found that linting tools are also quite revealing for learning better Flutter and Dart programming structures and patterns.

The Dos and Don’ts of a language are exposed with linting tools.

Lint rules yield advice and suggestions, and can even help to isolate some code smells. And because not all smells are bad, you can also learn when to disable rules, or the various ways of ignoring them. Ergo, you’re not only learning the rules (i.e. suggestions) for the language, you’re also learning when not to apply them. Sometimes a file will have only one or two rules to ignore. Other times a rule may need to be ignored for an entire file, or by simply disabling the rule altogether.

What’s more, you can expand your knowledge of these rules by adopting additional lint tools or libraries. At least with Flutter and Dart, you’re not stuck with just one. Alongside Flutter Lints, there’s also Very Good Analysis (VGV), Lint, and a tool I’m currently learning a lot from which is DCM* (formerly Dart Code Metrics).

Both the Lint and Very Good Analysis packages track development of new Dart and Flutter lint rules well and are updated regularly.

RydMike (published July 28, 2021, updated Nov 20, 2023). Flutter Linting and Linter Comparison. https://rydmike.com/blog_flutter_linting.html
DCM lint fix: prefer-null-aware-spread | `...?playersMap['new'],`

*Note that DCM is subscription-based. As a solo developer, the subscription runs a tad bit more than an Individual GitHub Copilot subscription, and a tad bit less than a ChatGPT Plus plan. At the time of writing the Individual Plan had been merged into the Teams Plan who’s cost is $14/month if billed annually. For anyone who can afford it, the code analysis is truly helpful. For those who can’t afford the annual, just one or two months could help as well. Or, it would seem they also have a special free license for open source projects, which could prove a great entry point.

While a copy of my open source app’s lint analysis configuration is available in GitHub, it does not include the DCM configuration section, because DCM is a paid service, so I’ve included the configuration in the expandable section below (DCM Lint Analysis Configuration). When you expand the section, you’ll notice that half the rules are commented; that’s how I make my way through the list, by commenting out the avoid rules, typically about one per day (I also do this with my private KD-reCall apps).

DCM Lint Analysis Configuration
# This is for anyone using DCM.

dart_code_metrics:
  extends:
    - recommended
  rules:
    - avoid-accessing-collections-by-constant-index: false
    - avoid-duplicate-initializers: false
    - avoid-dynamic: false
    - avoid-empty-setstate: false
    - avoid-incomplete-copy-with: false
    - avoid-inferrable-type-arguments: false
    - avoid-missed-calls: false
    - avoid-missing-enum-constant-in-map: false
    - avoid-missing-image-alt: false
    - avoid-nullable-interpolation: false
    - avoid-nullable-tostring: false
    - avoid-passing-async-when-sync-expected: false
    - avoid-passing-self-as-argument: false
    - avoid-redundant-async: false
    # - avoid-redundant-else: false
    - avoid-shadowing:
        ignored-names:
          - context
    # - avoid-unassigned-late-fields: false
    # - avoid-undisposed-instances: false
    # - avoid-unnecessary-futures: false
    # - avoid-unnecessary-local-late: false
    # - avoid-unnecessary-nullable-return-type: false
    # - avoid-unnecessary-reassignment: false
    # - avoid-unnecessary-setstate: false
    # - avoid-unnecessary-stateful-widgets: false
    # - avoid-unused-parameters: false
    # - dispose-fields: false
    - match-getter-setter-field-names: false

    # Some of these are needed (for now) for context-after-await calls.
    # - move-variable-closer-to-its-usage: false

    # - no-equal-then-else: false
    # - prefer-correct-callback-field-name: false
    # - prefer-correct-json-casts: false
    # - prefer-declaring-const-constructor: false
    # - prefer-match-file-name: false
    # - prefer-prefixed-global-constants: false
    # - prefer-single-widget-per-file: false
    # - prefer-switch-with-enums: false
    # - prefer-trailing-comma: false
    # - proper-super-calls: false
    # - use-setstate-synchronously: false
    #
    - unnecessary-trailing-comma: false
    - avoid-unused-instances: false
    - no-empty-block: false

The code refactoring involved in some of these lint changes is where the learning comes in — at least when you take the time to look into why the refactors are being suggested, and don’t just apply them blindly. A few extracted examples of some rules that have helped with dozens of refactors include:

/// DCM lint fix: prefer-null-aware-spread

  playersMap['new'] = [
    // ...playersMap['new']!,
    ...?playersMap['new'],

/// DCM lint fix: prefer-iterable-of

  // sublists.add(List.from(currentSublist));
  sublists.add(List.of(currentSublist));

/// DCM lint fix: prefer-dedicated-media-query-methods

  // width: MediaQuery.of(context).size.width * 0.4,
  width: MediaQuery.sizeOf(context).width * 0.4,

/// DCM lint fix: avoid-unsafe-collection-methods

  // final newName = widget.listRowPlayerList[value];
  final newName = widget.listRowPlayerList.elementAtOrNull(value) ?? '';

Bonus Read

A much more in-depth explanation of Flutter linting is available in an article titled, Flutter Linting and Linter Comparison: Custom linting setup, and comparing linting rules by different packages.

It provides a very detailed configuration and lint comparison analysis and is definitely a worthwhile read!

CI/CD (WIP)

My fourth pillar of knowledge advancement is with CI/CD, for which I’m currently knee-deep in learning and aim to walk through at least a few different approaches and tools. A separate blog post on that is expected next.

Cheers!
– Keith | https://keithdc.com

Leave a Reply