Flutter代码质量检查插件grab_lints的使用

Flutter代码质量检查插件grab_lints的使用

简介

grab_lints 是一个用于 Flutter 的代码质量检查插件,可以帮助开发者发现并修正对 grab 库的常见误用。此插件依赖于 custom_lint

兼容性说明

请注意,此插件不兼容 grab 1.0.0-dev.1 及以上版本。由于 grab 较新版本的改进,不再需要 grab_lints 来避免旧版本中存在的误用问题。

安装与配置

pubspec.yaml

在项目的 pubspec.yaml 文件中添加以下依赖项:

dev_dependencies:
  custom_lint:
  grab_lints: x.x.x

analysis_options.yaml

在项目的 analysis_options.yaml 文件中启用 custom_lint 插件:

analyzer:
  plugins:
    - custom_lint

可用的检查规则

错误(Errors)

规则名称 是否可自动修复 详情
missing_grab_mixin with 子句中缺少必要的 Grab 混入类。
wrong_grab_mixin 小部件类中的 Grab 混入类不匹配。

警告(Warnings)

规则名称 是否可自动修复 详情
unnecessary_grab_mixin with 子句中存在不必要的 Grab 混入类。
maybe_wrong_build_context_for_grab 传递给 grab 方法的 BuildContext 存在问题,如不是来自 build 方法或通过变量传递。建议直接使用 build 方法的参数。
avoid_grab_outside_build grab 方法在小部件的 build 方法外被使用。虽然可能,但容易导致误用或混淆。

示例代码

以下是 grab_lints 的使用示例代码:

// ignore_for_file: avoid_single_cascade_in_expression_statements, invalid_override, mixin_application_not_implemented_interface, non_abstract_class_inherits_abstract_member, unused_element

//=========================================================
// 此文件既作为演示也作为测试。
// 如果 lint 警告成功被 `expect_lint` 注释抑制,则您的编辑器将不会显示错误。
//=========================================================

import 'package:flutter/material.dart';
import 'package:grab/grab.dart';

final valueNotifier = ValueNotifier(0);
final changeNotifier1 = MyChangeNotifier1(0);
final changeNotifier2 = MyChangeNotifier2(0);
final changeNotifier3 = MyChangeNotifier3(0);
final textController = TextEditingController();

class MyChangeNotifier1 extends ChangeNotifier {
  MyChangeNotifier1(this.value);

  final int value;
}

class MyChangeNotifier2 extends MyChangeNotifier1 {
  MyChangeNotifier2(super.value);
}

class MyChangeNotifier3 with ChangeNotifier {
  MyChangeNotifier3(this.value);

  final int value;
}

void main() => runApp(const App());

class App extends StatelessWidget {
  const App({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: const Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            _StatelessWidget1(),
            _StatelessWidget2(),
            _StatelessWidget3(),
            _StatelessWidget4(),
            _StatefulWidget1(),
            _StatefulWidget2(),
            _StatefulWidget3(),
            _StatefulWidget4(),
          ],
        ),
        floatingActionButton: FloatingActionButton(
          child: const Icon(Icons.add),
          onPressed: () => valueNotifier.value++,
        ),
      ),
    );
  }
}

// expect_lint: unnecessary_grab_mixin
class _StatelessWidget1 extends StatelessWidget with Grab {
  const _StatelessWidget1();

  [@override](/user/override)
  Widget build(BuildContext context) {
    return const SizedBox.shrink();
  }
}

class _StatelessWidget2 extends StatelessWidget with Grab {
  const _StatelessWidget2();

  [@override](/user/override)
  Widget build(BuildContext context) {
    // 使用 grab 在 if 条件或块中是允许的。
    if (valueNotifier.grab(context).isEven) {
      valueNotifier.grab(context);
    }

    int localFunc1() {
      // 在局部函数中使用 BuildContext 参数是可以接受的。
      return valueNotifier.grab(context);
    }

    int localFunc2(BuildContext context) {
      // 如果 BuildContext 参数未直接使用,则会发出警告。
      // expect_lint: maybe_wrong_build_context_for_grab
      return valueNotifier.grab(context);
    }

    final ctx = context;

    // build 方法的 BuildContext 参数应直接使用。
    // expect_lint: maybe_wrong_build_context_for_grab
    valueNotifier.grab(ctx);

    return Center(
      child: SizedBox.square(
        dimension: 500.0,
        child: Column(
          children: [
            Padding(
              // 在深层嵌套位置使用 grab 是允许的。
              padding: EdgeInsets.all(valueNotifier.grab(context).toDouble()),
              child: Text('${valueNotifier.grab(context)}'),
            ),
            ...[
              Text('${valueNotifier.grab(context)}'),
            ],
            Builder(
              builder: (_) {
                // 在回调函数中使用 BuildContext 参数是可以接受的。
                // 但是请注意,这会导致父级小部件和当前 Builder 重新构建。
                return Text('${valueNotifier.grab(context)}');
              },
            ),
            Builder(
              builder: (context) {
                // expect_lint: maybe_wrong_build_context_for_grab
                return Text('${valueNotifier.grab(context)}');
              },
            ),
            Builder(
              builder: (context2) {
                final context = context2;

                // expect_lint: maybe_wrong_build_context_for_grab
                return Text('${valueNotifier.grab(context)}');
              },
            ),
          ],
        ),
      ),
    );
  }
}

// expect_lint: wrong_grab_mixin
class _StatelessWidget3 extends StatelessWidget with Grabful {
  const _StatelessWidget3();

  [@override](/user/override)
  Widget build(BuildContext context) {
    _func(context);

    // expect_lint: missing_grab_mixin
    return Text('${valueNotifier.grab(context)}');
  }

  void _func(BuildContext context) {
    // expect_lint: avoid_grab_outside_build
    valueNotifier.grab(context);
  }
}

class _StatelessWidget4 extends StatelessWidget {
  const _StatelessWidget4();

  [@override](/user/override)
  Widget build(BuildContext context) {
    // 需要 Grab 或 StatelessGrabMixin。
    // expect_lint: missing_grab_mixin
    final count = valueNotifier.grab(context);
    // expect_lint: missing_grab_mixin
    changeNotifier1.grab(context);
    // expect_lint: missing_grab_mixin
    changeNotifier2.grab(context);
    // expect_lint: missing_grab_mixin
    changeNotifier3.grab(context);
    // expect_lint: missing_grab_mixin
    textController.grab(context);

    // expect_lint: missing_grab_mixin
    valueNotifier.grabAt(context, (v) => v);
    // expect_lint: missing_grab_mixin
    changeNotifier1.grabAt(context, (MyChangeNotifier1 n) => n.value);
    // expect_lint: missing_grab_mixin
    changeNotifier2.grabAt(context, (MyChangeNotifier2 n) => n.value);
    // expect_lint: missing_grab_mixin
    changeNotifier3.grabAt(context, (MyChangeNotifier3 n) => n.value);

    // expect_lint: missing_grab_mixin
    valueNotifier..grab(context);

    return Center(
      child: Text('$count'),
    );
  }
}

// expect_lint: unnecessary_grab_mixin
class _StatefulWidget1 extends StatefulWidget with Grabful {
  const _StatefulWidget1();

  [@override](/user/override)
  State<_StatefulWidget1> createState() => _StatefulWidget1State();
}

class _StatefulWidget1State extends State<_StatefulWidget1> {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return const SizedBox.shrink();
  }
}

class _StatefulWidget2 extends StatefulWidget with Grabful {
  const _StatefulWidget2();

  [@override](/user/override)
  State<_StatefulWidget2> createState() => _StatefulWidget2State();
}

class _StatefulWidget2State extends State<_StatefulWidget2> {
  [@override](/user/override)
  Widget build(BuildContext context) {
    // 使用 grab 在 if 条件或块中是允许的。
    if (valueNotifier.grab(context).isEven) {
      valueNotifier.grab(context);
    }

    int localFunc1() {
      // 在局部函数中使用 BuildContext 参数是可以接受的。
      return valueNotifier.grab(context);
    }

    int localFunc2(BuildContext context) {
      // 如果 BuildContext 参数未直接使用,则会发出警告。
      // expect_lint: maybe_wrong_build_context_for_grab
      return valueNotifier.grab(context);
    }

    final ctx = context;

    // build 方法的 BuildContext 参数应直接使用。
    // expect_lint: maybe_wrong_build_context_for_grab
    valueNotifier.grab(ctx);

    return Center(
      child: SizedBox.square(
        dimension: 500.0,
        child: Column(
          children: [
            Padding(
              // 在深层嵌套位置使用 grab 是允许的。
              padding: EdgeInsets.all(
                valueNotifier.grab(context).toDouble(),
              ),
              child: Text('${valueNotifier.grab(context)}'),
            ),
            ...[
              Text('${valueNotifier.grab(context)}'),
            ],
            Builder(
              builder: (_) {
                // 在回调函数中使用 BuildContext 参数是可以接受的。
                // 但是请注意,这会导致父级小部件和当前 Builder 重新构建。
                return Text('${valueNotifier.grab(context)}');
              },
            ),
            Builder(
              builder: (context) {
                // expect_lint: maybe_wrong_build_context_for_grab
                return Text('${valueNotifier.grab(context)}');
              },
            ),
            Builder(
              builder: (context2) {
                final context = context2;

                // expect_lint: maybe_wrong_build_context_for_grab
                return Text('${valueNotifier.grab(context)}');
              },
            ),
          ],
        ),
      ),
    );
  }
}

// expect_lint: wrong_grab_mixin
class _StatefulWidget3 extends StatefulWidget with Grab {
  const _StatefulWidget3();

  [@override](/user/override)
  State<_StatefulWidget3> createState() => _StatefulWidget3State();
}

class _StatefulWidget3State extends State<_StatefulWidget3> {
  [@override](/user/override)
  Widget build(BuildContext context) {
    _func(context);

    // expect_lint: missing_grab_mixin
    return Text('${valueNotifier.grab(context)}');
  }

  void _func(BuildContext context) {
    // expect_lint: avoid_grab_outside_build
    valueNotifier.grab(context);
  }
}

class _StatefulWidget4 extends StatefulWidget {
  const _StatefulWidget4();

  [@override](/user/override)
  State<_StatefulWidget4> createState() => _StatefulWidget4State();
}

class _StatefulWidget4State extends State<_StatefulWidget4> {
  [@override](/user/override)
  Widget build(BuildContext context) {
    // 需要 Grabful 或 StatefulGrabMixin。
    // expect_lint: missing_grab_mixin
    final count = valueNotifier.grab(context);
    // expect_lint: missing_grab_mixin
    changeNotifier1.grab(context);
    // expect_lint: missing_grab_mixin
    changeNotifier2.grab(context);
    // expect_lint: missing_grab_mixin
    changeNotifier3.grab(context);
    // expect_lint: missing_grab_mixin
    textController.grab(context);

    // expect_lint: missing_grab_mixin
    valueNotifier.grabAt(context, (v) => v);
    // expect_lint: missing_grab_mixin
    changeNotifier1.grabAt(context, (MyChangeNotifier1 n) => n.value);
    // expect_lint: missing_grab_mixin
    changeNotifier2.grabAt(context, (MyChangeNotifier2 n) => n.value);
    // expect_lint: missing_grab_mixin
    changeNotifier3.grabAt(context, (MyChangeNotifier3 n) => n.value);

    // expect_lint: missing_grab_mixin
    valueNotifier..grab(context);

    return Center(
      child: Text('$count'),
    );
  }
}

更多关于Flutter代码质量检查插件grab_lints的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter代码质量检查插件grab_lints的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


grab_lints 是一个用于 Flutter 项目的代码质量检查插件,它基于 lints 包,提供了一组推荐的 lint 规则,帮助开发者遵循 Dart 和 Flutter 的最佳实践。使用 grab_lints 可以简化 lint 规则的配置,并确保项目代码风格一致。

安装 grab_lints

  1. 添加到 pubspec.yaml 文件

    在你的 Flutter 项目的 pubspec.yaml 文件中,添加 grab_lints 作为开发依赖项:

    dev_dependencies:
      grab_lints: ^1.0.0
    

    然后运行 flutter pub get 来安装依赖。

  2. 配置 analysis_options.yaml 文件

    在项目的根目录下创建或编辑 analysis_options.yaml 文件,并引入 grab_lints 的规则:

    include: package:grab_lints/analysis_options.yaml
    

    这个配置会将 grab_lints 提供的 lint 规则应用到你的项目中。

使用 grab_lints

  1. 运行代码分析

    在终端中运行以下命令来执行代码分析:

    flutter analyze
    

    这将根据 grab_lints 提供的规则检查你的代码,并输出任何违反规则的地方。

  2. 自定义 lint 规则

    如果你想在 grab_lints 的基础上添加或修改 lint 规则,可以在 analysis_options.yaml 文件中进行自定义。例如:

    include: package:grab_lints/analysis_options.yaml
    
    analyzer:
      strong-mode:
        implicit-casts: false
        implicit-dynamic: false
    
    linter:
      rules:
        - avoid_print
        - prefer_const_constructors
    

    在这个例子中,我们禁用了隐式类型转换和动态类型,并添加了 avoid_printprefer_const_constructors 两条规则。

  3. 集成到 CI/CD

    你可以将 flutter analyze 集成到你的 CI/CD 流程中,以确保每次提交或构建时都进行代码质量检查。例如,在 GitHub Actions 中,可以添加如下步骤:

    - name: Analyze code
      run: flutter analyze
回到顶部