dart-lang / linter

Linter for Dart.
https://dart.dev/tools/linter-rules
BSD 3-Clause "New" or "Revised" License
628 stars 170 forks source link

proposal: `nesting_too_deep` #4977

Open gardenofwine opened 4 months ago

gardenofwine commented 4 months ago

nesting_too_deep

Description

Limit the amount of prefix tabs/spaces in lines of code, by limiting nesting. Flutter widget code can sometimes become nested to a level where lines of code become prefixed by a considerable amount of tabs/spaces. combined with a linter such as lines_longer_than_80_chars, even short code lines would have to be broken up to multiple lines - so they won't exceed the 80 character limit.

Details

Below is an example of deeply nested code from the Flutter Constraints documentation. What might otherwise be code that is easy to read and occupy one line of code

TextStyle(color: Colors.blue[900], fontStyle: FontStyle.italic)

Has to be broken down to 3 lines, making the code less readable. The same code can be refactored with helper functions, to reduce nesting.

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Layout Article',
      home: SafeArea(
        child: Material(
          color: Colors.black,
          child: FittedBox(
            child: Container(
              width: 400,
              height: 670,
              color: const Color(0xFFCCCCCC),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  Expanded(
                      child: ConstrainedBox(
                          constraints: const BoxConstraints.tightFor(
                              width: double.infinity, height: double.infinity),
                          child: widget.examples[count - 1])),
                  Container(
                    height: 50,
                    width: double.infinity,
                    color: Colors.black,
                    child: SingleChildScrollView(
                      scrollDirection: Axis.horizontal,
                      child: Row(
                        mainAxisSize: MainAxisSize.min,
                        children: [
                          for (int i = 0; i < widget.examples.length; i++)
                            Container(
                              width: 58,
                              padding: const EdgeInsets.only(left: 4, right: 4),
                              child: button(i + 1),
                            ),
                        ],
                      ),
                    ),
                  ),
                  Container(
                    height: 273,
                    color: Colors.grey[50],
                    child: Scrollbar(
                      child: SingleChildScrollView(
                        key: ValueKey(count),
                        child: Padding(
                          padding: const EdgeInsets.all(10),
                          child: Column(
                            children: [
                              Center(child: Text(code)),
                              const SizedBox(height: 15),
                              Text(
                                explanation,
                                style: TextStyle(
                                    color: Colors.blue[900],
                                    fontStyle: FontStyle.italic),
                              ),
                            ],
                          ),
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }

Kind

Style advice

Bad Examples

See description for a code that could trigger the linter

Good Examples

  Widget _count() {
    return Expanded(
        child: ConstrainedBox(
            constraints: const BoxConstraints.tightFor(
                width: double.infinity, height: double.infinity),
            child: widget.examples[count - 1]));
  }

  Widget _buttons() {
    return Container(
      height: 50,
      width: double.infinity,
      color: Colors.black,
      child: SingleChildScrollView(
        scrollDirection: Axis.horizontal,
        child: Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            for (int i = 0; i < widget.examples.length; i++)
              Container(
                width: 58,
                padding: const EdgeInsets.only(left: 4, right: 4),
                child: button(i + 1),
              ),
          ],
        ),
      ),
    );
  }

  Widget _explanation() {
    return Container(
      height: 273,
      color: Colors.grey[50],
      child: Scrollbar(
        child: SingleChildScrollView(
          key: ValueKey(count),
          child: Padding(
            padding: const EdgeInsets.all(10),
            child: Column(
              children: [
                Center(child: Text(code)),
                const SizedBox(height: 15),
                Text(
                  explanation,
                  style: TextStyle(
                      color: Colors.blue[900], fontStyle: FontStyle.italic),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Layout Article',
      home: SafeArea(
        child: Material(
          color: Colors.black,
          child: FittedBox(
            child: Container(
              width: 400,
              height: 670,
              color: const Color(0xFFCCCCCC),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  _count(),
                  _buttons(),
                  _explanation(),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }

Discussion

Discussion checklist