美文网首页
Flutter 语法规范

Flutter 语法规范

作者: 张_何 | 来源:发表于2023-02-17 17:32 被阅读0次

    命名

    • 1.1 库名、文件名使用小写加下划线来命名
      library prometheumTools
      prometheum-tools.dart
      library prometheum_tools
      prometheum_tools.dart
    • 1.2 类、枚举、类型使用大驼峰规则
      class trademodel{...}
      typedef voidCallback = void Function();
      enum app_lifecycle_state { ... }
      class Trademodel{...}
      typedef VoidCallback = void Function();
      enum AppLifecycleState { ... }
    • 1.3 使用小写或小写加下划线来命名导入前缀
      import 'dart:math' as MATH;
      import 'dart:math' as Math;
      import 'dart:math' as math;
      import 'dart:math' as dart_math;
    • 1.4 使用小驼峰命名变量名、函数名、枚举case、参数名
      String DogName = 'dudu';
      String dog_name = 'dudu';
      int FindMax() {....};
      String dogName = 'dudu';
      int findMax() {....};
      enum ActionType { touchUp, touchDown, ... };
    • 1.5 变量名、函数名、类型名、类名等应语义化,做到见名知意
      String a = "zhangsan";
      class Abc{ ... }
      void doAbc(){...}
      String name = "zhangsan";
      class TradePage{ ... }
      Widget chartLine(){...}
    • 1.6 变量名不要使用前缀字母
      double kDefaultTimeout = 5.0;
      double defaultTimeout = 5.0;

    导入

    • 2.1 导入顺序:先导入dart库,然后导入flutter库,其次导入三方库,再次导入项目工具库,最后导入自写的功能库
    import 'dart:async';
    import 'dart:html';
    
    import 'package:flutter/cupertino.dart'
    import 'package:flutter/material.dart';
    
    import 'package:oktoast/oktoast.dart';
    import 'package:provider/provider.dart';
    
    import 'extensions/color_extension.dart';
    import 'extensions/double_extension.dart';
    
    import './business/main/config/app_locales.dart';
    import './business/main/config/app_router.dart';
    
    • 2.2 导入方式:
      dart库:import 'dart:async';
      flutter库以package协议导入:import 'package:flutter/material.dart';
      三方库以package或http协议导入:import 'package:provider/provider.dart';
      自写库使用./或../以相对路径的方式导入:import './business/main/config/app_locales.dart';

    变量

    • 3.1 不需要外部访问的变量请使用下划线开头
      double _treasure = 100000000.0;
    • 3.2 编译时需要确定的常量请使用const修饰
      const String url = "http://www.baidu.com";
    • 3.3 运行时才确定的常量请使用final修饰
      final DateTime now = DateTime.now();
    • 3.4 只需要外部读取或赋值的请提供相应的get 或 set方法
    class Person{
      int _age;
      
      set age(int value){
        _age = value;
      }
    
      int get age {
        return _age;
      }
    }
    
    • 3.5 不要显示的给一个变量赋值为null,因为dart会帮我们将未赋初始值的变量设置为bull
      String name = null;
      String name;
    • 3.6 使用确定的类型声明变量
      var name;
      String name";

    函数

    • 4.1 构造函数
      不推荐如下写法:
      class Point {
        num x, y;
        Point(num x, num y) {
          this.x = x;
          this.y = y;
        }
      }
    

    推荐如下写法:

    class Point {
      num x, y;
      Point(this.x, this.y);
    }
    
    • 4.2 可选参数,尽量使用命名可选参数,少使用位置可选参数

    不推荐:

    Widget _sortBtn(context,[String title,Image image]){
        return Container();
    }
    

    推荐:

    Widget _sortBtn(context,{String title,Image image}){
        return Container();
    }
    
    • 4.3 单个必选参数可以省略参数名,多个必传参数的时候也需要参数名

    不推荐:

      Widget sortBtn(BuildContext context,String title){
        return Container();
      }
    

    推荐:

      Widget sortBtn({@required BuildContext context,@required String title}){
        return Container();
      }
    
    • 4.4 不需要被外部调用的函数,其名称请以下划线开始

    不推荐:

      Widget sortBtn(BuildContext context,String title){
        return Container();
      }
    

    推荐:

      Widget _sortBtn({@required BuildContext context,@required String title}){
        return Container();
      }
    

    集合

    • 5.1 声明集合变量的时候尽可能的指定类型

    不推荐:

    List studentNames = [];
    Map studentIdAndName = {};
    

    推荐:

    List<String> studentNames = [];
    Map<String,String> studentIdAndName = {};
    
    • 5.2 不要使用集合的length属性去做判空,而是使用isEmpty

    不推荐:

    List<String> studentNames = [];
    if (studentNames.length == 0) {
        ...
    }
    
    Map<String,String> studentIdAndName = {};
    if (studentIdAndName.length == 0) {
        ...
    }
    

    推荐:

    List<String> studentNames = [];
    if (studentNames.isEmpty) {
        ...
    }
    
    Map<String,String> studentIdAndName = {};
    if (studentIdAndName.isEmpty) {
        ...
    }
    
    • 5.3 使用字面量创建集合

    不推荐:

    List list = List();
    Map map = Map();
    

    推荐:

    List<String> list = <String>[];
    Map<String, dynamic> map = <String, dynamic>{};
    

    代码格式

    6.1 if后的大括号与else在同一行

    不推荐:

    if (条件) {
    
    } 
    else if(条件) {
    
    }
    else{
    
    }
    

    推荐:

    if (条件) {
    
    } else if(条件) {
    
    } else {
    
    }
    
    • 6.2 类、枚举、函数名后紧跟大括号

    不推荐:

    class Person
    {
        ...
    }
    

    推荐:

    class Person {
        ...
    }
    
    • 6.3 所有流控制结构请使用大括号括起来,包括一个if语句没有else子句的情况,也包括if代码块里只有一条语句的情况

    推荐:

    if (条件) {
    
    } else {
    
    }
    
    if (条件) {
    }
    

    不推荐

    if(条件) return value;
    或者 
    if (条件)
        return value;
    

    6.4 单行代码字符不易超过80

    6.5 链式调用过多和级联调用请分多行写

    不推荐:

    Paint paint = Paint()..color = Colors.black12..isAntiAlias = false..strokeCap = StrokeCap.round..strokeWidth = 2..style = PaintingStyle.stroke;
    
    var abd = Provider.of<MarketProvider>(context,listen: true).coinList[0].candle.first.price.toString().toDouble;
    
    

    推荐:

    Paint paint = Paint()
          ..color = Colors.black12
          ..isAntiAlias = false
          ..strokeCap = StrokeCap.round
          ..strokeWidth = 2
          ..style = PaintingStyle.stroke;
          
    var abd = Provider.of<MarketProvider>(context,listen: true)
        .coinList[0]
        .candle
        .first
        .price
        .toString()
        .toDouble;
    

    注释

    • 7.1 函数外的单行注释请使用///

    不推荐:

    /* Assume we have a valid name. */
     greet(name) {
        print('Hi, $name!');
      }
    

    推荐:

    /// Assume we have a valid name.
    greet(name) {
        print('Hi, $name!');
    }
    
    • 7.2 函数内的单行注释应在代码上面另起一行

    不推荐:

     greet(name) {
        print('Hi, $name!');//Assume we have a valid name.
      }
    

    推荐:

    greet(name) {
    // Assume we have a valid name.
        print('Hi, $name!');
    }
    
    • 7.3 使用/**/临时注释掉一段代码,除此之外所有的注释都应该使用///

    不推荐:

    //greet(name) {
    //    print('Hi, $name!');
    //}
    

    推荐:

    /*
    greet(name) {
        print('Hi, $name!');
    }
    */
    
    • 7.4 Doc 注释,
      使用///文档注释来记录成员和类型。使用doc注释而不是常规注释,可以让dartdoc找到并生成文档。用一句话总结开始doc注释,以简短的、以用户为中心的描述开始你的文档注释,以句号结尾。“doc注释”的第一句话分隔成自己的段落。

    不推荐:

      /// Depending on the state of the file system and the user's permissions,
      /// certain operations may or may not be possible. If there is no file at
      /// [path] or it can't be accessed, this function throws either [IOError]
      /// or [PermissionError], respectively. Otherwise, this deletes the file.
      void delete(String path) {
        ...
      }
    

    推荐:

      /// Deletes the file at [path].
      ///
      /// Throws an [IOError] if the file could not be found. Throws a
      /// [PermissionError] if the file is present but could not be deleted.
      void delete(String path) {
        ...
      }
    

    其他

    8.1 dart 2.0中创建新对象已经省略 new 关键字了
    Object obj = new Object();
    Object obj = Object();
    8.2 定义const常量时,不要=两边都使用const,推荐=左边使用const
    const Object obj = const Object();
    const Object obj = Object();
    8.3 组件嵌套不要超过3层,大于3层是请拆分

    不推荐:

      Widget _marketItem() {
        return InkWell(
          onTap: (){},
          child: Container(
              child:Column(
                children: <Widget>[
                  OutlineButton(
                    child:Row(
                      children: <Widget>[
                        Text('title'),
                        Container(
                          margin: EdgeInsets.only(left:12),
                          child:Image.asset('assets/images/arrowUp.png'),
                        ),
                      ],
                    ),
                  ),
                ],
              )
          ),
        );
      }
    

    推荐:

     Widget _marketItem() {
        return InkWell(
          onTap: (){},
          child: Container(
              child:Column(
                children: <Widget>[
                  _outLineBtn(),
                ],
              )
          ),
        );
      }
      
      Widget _outLineBtn() {
        return OutlineButton(
          child:Row(
            children: <Widget>[
              Text('title'),
              Container(
                margin: EdgeInsets.only(left:12),
                child:Image.asset('assets/images/arrowUp.png'),
              ),
            ],
          ),
        );
      }
    

    8.4 所有条件语句的结果值一定要是bool类型的

    错误:

      if(model?.isSelected) {
        
      }
    

    正确:

      bool isSelected = model?.isSelected ?? false;
      if(isSelected) {
    
      }
    

    Flutter代码开发规范0.1.0

    Style

    -定义了布局和组织代码的规则

    标识符

    在 Dart 中标识符有三种类型。

    • UpperCamelCase 每个单词的首字母都大写,包含第一个单词。
    • lowerCamelCase 每个单词的首字母都大写,除了第一个单词, 第一个单词首字母小写,即使是缩略词。
    • lowercase_with_underscores 只是用小写字母单词,即使是缩略词, 并且单词之间使用 _ 连接。
    使用 UpperCamelCase 风格命名类型

    Classes(类名)、 enums(枚举类型)、 typedefs(类型定义)、 以及 type parameters(类型参数)应该把每个单词的首字母都大写(包含第一个单词), 不使用分隔符。

    ✅正确示范

    class SliderMenu { ... }
    
    class HttpRequest { ... }
    
    typedef Predicate = bool Function<T>(T value);
    

    这里包括使用元数据注解的类。

    class Foo {
      const Foo([arg]);
    }
    
    @Foo(anArg)
    class A { ... }
    
    @Foo()
    class B { ... }
    
    使用lowercase_with_underscores风格命名库、包 、文件夹、源文件名

    ✅正确示范

    library peg_parser.source_scanner;
    
    import 'file_system.dart';
    import 'slider_menu.dart';
    

    ❌错误 示范

    library pegparser.SourceScanner;
    
    import 'file-system.dart';
    import 'SliderMenu.dart';
    
    使用 lowercase_with_underscores 风格命名导入的前缀

    ✅正确示范

    import 'dart:math' as math;
    import 'package:angular_components/angular_components'
        as angular_components;
    import 'package:js/js.dart' as js;
    

    ❌错误 示范

    import 'dart:math' as Math;
    import 'package:angular_components/angular_components'
        as angularComponents;
    import 'package:js/js.dart' as JS;
    
    使用 lowerCamelCase 风格来命名其他的标识符

    类成员、顶级定义、变量、参数以及命名参数等 除了第一个单词,每个单词首字母都应大写,并且不使用分隔符。

    var item;
    
    HttpRequest httpRequest;
    
    void align(bool clearItems) {
      // ...
    }
    
    优先使用 lowerCamelCase 来命名常量

    在新的代码中,使用 lowerCamelCase 来命名常量,包括枚举的值。 已有的代码使用了 SCREAMING_CAPS 风格, 你可以继续全部使用该风格来保持代码的一致性。

    ✅正确示范

    const pi = 3.14;
    const defaultTimeout = 1000;
    final urlScheme = RegExp('^([a-z]+):');
    
    class Dice {
      static final numberGenerator = Random();
    }
    

    ❌错误示范

    const PI = 3.14;
    const DefaultTimeout = 1000;
    final URL_SCHEME = RegExp('^([a-z]+):');
    
    class Dice {
      static final NUMBER_GENERATOR = Random();
    }
    
    把超过两个字母的首字母大写缩略词和缩写词当做一般单词来对待

    首字母大写缩略词比较难阅读, 特别是多个缩略词连载一起的时候会引起歧义。 例如,一个以 HTTPSFTP 开头的名字, 没有办法判断它是指 HTTPS FTP 还是 HTTP SFTP 。

    为了避免上面的情况,缩略词和缩写词要像普通单词一样首字母大写, 两个字母的单词除外。 (像 ID 和 Mr. 这样的双字母缩写词仍然像一般单词一样首字母大写。)

    ✅正确示范

    HttpConnectionInfo
    uiHandler
    IOStream
    HttpRequest
    Id
    DB
    

    ❌错误 示范

    HTTPConnection
    UiHandler
    IoStream
    HTTPRequest
    ID
    Db
    
    不要 使用前缀字母

    ✅正确示范

    defaultTimeout
    

    ❌错误 示范

    kDefaultTimeout
    

    顺序

    为了使文件前面部分保持整洁,规定关键字出现顺序。 每个“部分”应该使用空行分割。

    把 “dart:” 导入语句放到其他导入语句之前
    import 'dart:async';
    import 'dart:html';
    
    import 'package:bar/bar.dart';
    import 'package:foo/foo.dart';
    
    把 “package:” 导入语句放到项目相关导入语句之前
    import 'package:bar/bar.dart';
    import 'package:foo/foo.dart';
    
    import 'util.dart';
    
    优先把外部扩展 “package:” 导入语句放到其他语句之前

    如果使用了多个 “package:” 导入语句来导入自己的包以及其他外部扩展包, 推荐将自己的包分开放到一个额外的部分。

    import 'package:bar/bar.dart';
    import 'package:foo/foo.dart';
    
    import 'package:my_package/util.dart';
    
    把导出(export)语句作为一个单独的部分放到所有导入语句之后

    ✅正确示范

    import 'src/error.dart';
    import 'src/foo_bar.dart';
    
    export 'src/error.dart';
    

    ❌错误示范

    import 'src/error.dart';
    export 'src/error.dart';
    import 'src/foo_bar.dart';
    

    格式化

    使用 dartfmt 格式化你的代码

    代码编辑完成后,鼠标右键编辑器,在弹出窗中找到reformat code with dartfmt或者快捷键 cmd+option+L

    尽量避免单行超过80个字符
    对所有流控制结构使用花括号
    if (isWeekDay) {
      print('Bike to work!');
    } else {
      print('Go dancing or read a book!');
    }
    

    这里有一个例外:一个没有 elseif 语句, 并且这个 if 语句以及它的执行体适合在一行中实现。 在这种情况下,如果您愿意,可以不用括号:

    if (arg == null) return defaultValue;
    

    但是,如果执行体包含下一行,请使用大括号:

    if (overflowChars != other.overflowChars) {
      return overflowChars < other.overflowChars;
    }
    

    ❌错误示范

    if (overflowChars != other.overflowChars)
      return overflowChars < other.overflowChars;
    

    Documentation

    尽可能为类、成员变量、方法、关键逻辑判断写注释

    // 单行注释
    
    /// 文档注释
    
    /** 块状注释 */
    

    注释

    像句子一样来格式化注释
    // Not if there is nothing before it.
    if (_chunks.isEmpty) return false;
    

    如果第一个单词不是大小写相关的标识符,则首字母要大写。 使用句号,叹号或者问号结尾。所有的注释都应该这样: 文档注释,单行注释,甚至 TODO。即使它是一个句子的片段。

    不要使用块注释作用作解释说明

    可以使用块注释 (/* ... */) 来临时的注释掉一段代码, 但是其他的所有注释都应该使用 //

    ❌错误示范

    greet(name) {
      /* Assume we have a valid name. */
      print('Hi, $name!');
    }
    

    ✅正确示范

    greet(name) {
      // Assume we have a valid name.
      print('Hi, $name!');
    }
    

    文档注释

    使用 /// 文档注释来注释成员和类型

    ✅正确示范

    /// The number of characters in this chunk when unsplit.
    int get length => ...
    

    ❌错误示范

    // The number of characters in this chunk when unsplit.
    int get length => ...
    
    要在文档注释开头有一个单句总结

    注释文档要以一个以用户为中心,简要的描述作为开始。 通常句子片段就足够了。为读者提供足够的上下文来定位自己, 并决定是否应该继续阅读,或寻找其他解决问题的方法。

    ✅正确示范

    /// Deletes the file at [path] from the file system.
    void delete(String path) {
      ...
    }
    

    ❌错误示范

    /// Depending on the state of the file system and the user's permissions,
    /// certain operations may or may not be possible. If there is no file at
    /// [path] or it can't be accessed, this function throws either [IOError]
    /// or [PermissionError], respectively. Otherwise, this deletes the file.
    void delete(String path) {
      ...
    }
    
    让文档注释的第一句从段落中分开

    用一句话总结文档,在其后添加一个空行,将剩余的部分放在后面的段落中

    ✅正确示范

    /// Deletes the file at [path].
    ///
    /// Throws an [IOError] if the file could not be found. Throws a
    /// [PermissionError] if the file is present but could not be deleted.
    void delete(String path) {
      ...
    }
    

    ❌错误示范

    /// Deletes the file at [path]. Throws an [IOError] if the file could not
    /// be found. Throws a [PermissionError] if the file is present but could
    /// not be deleted.
    void delete(String path) {
      ...
    }
    
    使用方括号在文档注释中引用作用域内的标识符

    给变量,方法,或类型等名称加上方括号,让注释更清晰

    /// Throws a [StateError] if ...
    /// similar to [anotherMethod()], but ...
    

    要链接到特定类的成员,请使用以点号分割的类名和成员名:

    /// Similar to [Duration.inDays], but handles fractional days.
    

    点语法也可用于引用命名构造函数。 对于未命名的构造函数,在类名后面加上括号

    /// To create a point, call [Point()] or use [Point.polar()] to ...
    
    使用散文的方式来描述参数、返回值以及异常信息
    /// Defines a flag.
    ///
    /// Throws an [ArgumentError] if there is already an option named [name] or
    /// there is already an option using abbreviation [abbr]. Returns the new flag.
    Flag addFlag(String name, String abbr) => ...
    

    把文档注释放到注解之前

    ✅正确示范

    /// A button that can be flipped on and off.
    @Component(selector: 'toggle')
    class ToggleComponent {}
    

    ❌错误示范

    @Component(selector: 'toggle')
    /// A button that can be flipped on and off.
    class ToggleComponent {}
    

    Usage

    part of 中使用字符串

    很多 Dart 开发者会避免直接使用 part 。他们发现当库仅有一个文件的时候很容易读懂代码。 如果你确实要使用 part 将库的一部分拆分为另一个文件,则 Dart 要求另一个文件指示它所属库的路径。 由于遗留原因, Dart 允许 part of 指令使用它所属的库的名称。 这使得工具很难直接查找到这个文件对应主库文件,使得库和文件之间的关系模糊不清。

    推荐的现代语法是使用 URI 字符串直接指向库文件。 首选的现代语法是使用直接指向库文件的URI字符串,URI 的使用和其他指令中一样。 如果你有一些库,my_library.dart,其中包含:

    library my_library;
    
    part "some/other/file.dart";
    

    从库中拆分的文件应该如下所示:

    part of "../../my_library.dart";
    

    ❌错误示范

    part of my_library;
    
    使用相对路径在导入你自己 package 中的 lib 目录

    在同一个 package 下其中一个库引用另一个 lib 目录下的库时, 应该使用相对的 URI 或者直接使用 package:

    比如,下面是你的 package 目录结构:

    my_package
    └─ lib
       ├─ src
       │  └─ utils.dart
       └─ api.dart
    

    如果 api.dart 想导入 utils.dart ,应该这样使用:

    import 'src/utils.dart';
    

    而不是:

    import 'package:my_package/src/utils.dart';
    

    字符串

    使用相邻字符串连接字符串文字

    如果你有两个字面量字符串(不是变量,是放在引号中的字符串),你不需要使用 + 来连接它们。 应该想 C 和 C++ 一样,只需要将它们挨着在一起就可以了。 这种方式非常适合不能放到一行的长字符串的创建。

    ✅正确示范

    raiseAlarm(
        'ERROR: Parts of the spaceship are on fire. Other '
        'parts are overrun by martians. Unclear which are which.');
    

    ❌错误示范

    raiseAlarm('ERROR: Parts of the spaceship are on fire. Other ' +
        'parts are overrun by martians. Unclear which are which.');
    
    使用插值的形式来组合字符串和值

    ✅正确示范

    'Hello, $name! You are ${year - birth} years old.';
    

    ❌错误示范

    'Hello, ' + name + '! You are ' + (year - birth).toString() + ' y...';
    
    在不需要的时候,避免使用花括号

    如果要插入是一个简单的标识符,并且后面没有紧跟随在其他字母文本,则应省略 {}

    ✅正确示范

    'Hi, $name!'
    "Wear your wildest $decade's outfit."
    'Wear your wildest ${decade}s outfit.'
    

    ❌错误示范

    'Hi, ${name}!'
    "Wear your wildest ${decade}'s outfit."
    

    集合

    使用集合字面量

    有两种方式来构造一个空的可变 list : []List() 。有三总方式来构造一个空的链表哈希 map:{}Map(), 和 LinkedHashMap()

    如果想创建一个固定不变的 list 或者其他自定义集合类型,这种情况下你需要使用构造函数。 否则,使用字面量语法更加优雅

    ✅正确示范

    var points = [];
    var addresses = {};
    

    ❌错误示范

    var points = List();
    var addresses = Map();
    
    不要 使用 .length 来判断一个集合是否为空

    调用 .length 来判断集合是否包含内容是非常低效的,Dart 提供了更加高效率和易用的 getter 函数:.isEmpty.isNotEmpty

    ✅正确示范

    if (lunchBox.isEmpty) return 'so hungry...';
    if (words.isNotEmpty) return words.join(' ');
    

    ❌错误示范

    if (lunchBox.length == 0) return 'so hungry...';
    if (!words.isEmpty) return words.join(' ');
    
    尽量使用高阶函数来转换集合数据

    如果你有一个集合并且想要修改里面的内容转换为另外一个集合, 使用 .map().where() 以及 Iterable 提供的其他函数会让代码更加简洁

    var aquaticNames = animals
        .where((animal) => animal.isAquatic)
        .map((animal) => animal.name);
    
    避免使用带有函数字面量的Iterable.forEach()

    在Dart中,如果你想遍历一个序列,惯用的方法是使用循环。

    ✅正确示范

    for (var person in people) {
      ...
    }
    

    ❌错误示范

    people.forEach((person) {
      ...
    });
    

    例外:如果要执行的操作是调用一些已存在的并且将每个元素作为参数的函数, 在这种情况下,forEach() 是很方便的。

    people.forEach(print);
    
    不要 使用 List.from() 除非想修改结果的类型

    给定一个可迭代的对象,有两种常见方式来生成一个包含相同元素的 list:

    var copy1 = iterable.toList();
    var copy2 = List.from(iterable);
    

    明显的区别是前一个更短。 更重要的区别在于第一个保留了原始对象的类型参数:

    // Creates a List<int>:
    var iterable = [1, 2, 3];
    
    // Prints "List<int>":
    print(iterable.toList().runtimeType);
    
    // Creates a List<int>:
    var iterable = [1, 2, 3];
    
    // Prints "List<dynamic>":
    print(List.from(iterable).runtimeType);
    

    如果你想要改变类型,那么可以调用 List.from()

    var numbers = [1, 2.3, 4]; // List<num>.
    numbers.removeAt(1); // Now it only contains integers.
    var ints = List<int>.from(numbers);
    
    使用 whereType() 按类型过滤集合
    var objects = [1, "a", 2, "b", 3];
    var ints = objects.whereType<int>();
    

    函数

    在 Dart 中,就连函数也是对象

    使用函数声明的方式为函数绑定名称

    如果你确实需要给方法一个名字,请使用方法定义而不是把 lambda 赋值给一个变量

    ✅正确示范

    void main() {
      localFunction() {
        ...
      }
    }
    

    ❌错误示范

    void main() {
      var localFunction = () {
        ...
      };
    }
    
    不要 使用 lambda 表达式来替代 tear-off

    如果你在一个对象上调用函数并省略了括号, Dart 称之为”tear-off”—一个和函数使用同样参数的闭包, 当你调用闭包的时候执行其中的函数。

    如果你有一个方法,这个方法调用了参数相同的另一个方法。 那么,你不需要人为将这个方法包装到一个 lambda 表达式中。

    ✅正确示范

    names.forEach(print);
    

    ❌错误示范

    names.forEach((name) {
      print(name);
    });
    

    参数

    使用 = 来分隔参数名和参数默认值

    Dart 同时支持 := 作为参数名和默认值的分隔符。 为了与可选的位置参数保持一致,请使用 =

    ✅正确示范

    void insert(Object item, {int at = 0}) { ... }
    

    ❌错误示范

    void insert(Object item, {int at: 0}) { ... }
    
    不要 显式的为参数设置 null

    ✅正确示范

    void error([String message]) {
      stderr.write(message ?? '\n');
    }
    

    ❌错误示范

    void error([String message = null]) {
      stderr.write(message ?? '\n');
    }
    

    变量

    不要 显示的为参数初始化 null

    在Dart中,未自动显式初始化的变量或字段将初始化为 null ,不要多余赋值null

    ✅正确示范

    int _nextId;
    
    class LazyId {
      int _id;
    
      int get id {
        if (_nextId == null) _nextId = 0;
        if (_id == null) _id = _nextId++;
    
        return _id;
      }
    }
    

    ❌错误示范

    int _nextId = null;
    
    class LazyId {
      int _id = null;
    
      int get id {
        if (_nextId == null) _nextId = 0;
        if (_id == null) _id = _nextId++;
    
        return _id;
      }
    }
    
    避免储存你能计算的东西

    在设计类时,您通常希望将多个视图公开到相同的底层状态。通常你会看到在构造函数中计算所有视图的代码,然后存储它们:

    ❌错误示范

    class Circle {
        num radius;
        num area;
        num circumference;
      
        Circle(num radius)
            : radius = radius,
              area = pi * radius * radius,
              circumference = pi * 2.0 * radius;
      }
    

    如上代码问题:

    • 浪费内存
    • 缓存的问题是无效——如何知道何时缓存过期需要重新计算?

    ✅正确示范

    class Circle {
        num radius;
      
        Circle(this.radius);
      
        num get area => pi * radius * radius;
        num get circumference => pi * 2.0 * radius;
      }
    

    成员

    不要 为字段创建不必要的 getter 和 setter 方法

    ✅正确示范

    class Box {
      var contents;
    }
    

    ❌错误示范

    class Box {
      var _contents;
      get contents => _contents;
      set contents(value) {
        _contents = value;
      }
    }
    
    使用 final 关键字来创建只读属性

    如果你有一个变量,对于外部代买来说只能读取不能修改, 最简单的做法就是使用 final 关键字来标记这个变量

    ✅正确示范

    class Box {
      final contents = [];
    }
    

    ❌错误示范

    class Box {
      var _contents;
      get contents => _contents;
    }
    
    对简单成员使用 =>
    double get area => (right - left) * (bottom - top);
    
    bool isReady(num time) => minTime == null || minTime <= time;
    
    String capitalize(String name) =>
        '${name[0].toUpperCase()}${name.substring(1)}';
    

    如果你有很多行声明或包含深层的嵌套表达式,应该换做使用代码块和一些语句来实现

    ❌错误示范

    Treasure openChest(Chest chest, Point where) =>
        _opened.containsKey(chest) ? null : _opened[chest] = Treasure(where)
          ..addAll(chest.contents);
    

    ✅正确示范

    Treasure openChest(Chest chest, Point where) {
      if (_opened.containsKey(chest)) return null;
    
      var treasure = Treasure(where);
      treasure.addAll(chest.contents);
      _opened[chest] = treasure;
      return treasure;
    }
    
    在不需要的时候不要用this

    只有两种情况需要使用 this.

    其中一种情况是要访问的局部变量和成员变量命名一样的时候

    ❌错误示范

    class Box {
      var value;
    
      void clear() {
        this.update(null);
      }
    
      void update(value) {
        this.value = value;
      }
    }
    

    ✅正确示范

    class Box {
      var value;
    
      void clear() {
        update(null);
      }
    
      void update(value) {
        this.value = value;
      }
    }
    

    另一种使用 this. 的情况是在重定向到一个命名函数的时候:

    ❌错误示范

    class ShadeOfGray {
      final int brightness;
    
      ShadeOfGray(int val) : brightness = val;
    
      ShadeOfGray.black() : this(0);
    
      // 这样是无法解析和编译的!
      // ShadeOfGray.alsoBlack() : black();
    }
    

    ✅正确示范

    class ShadeOfGray {
      final int brightness;
    
      ShadeOfGray(int val) : brightness = val;
    
      ShadeOfGray.black() : this(0);
    
      // 现在就可以了!
      ShadeOfGray.alsoBlack() : this.black();
    }
    
    尽可能的在定义变量的时候初始化变量值

    ❌错误示范

    class Folder {
      final String name;
      final List<Document> contents;
    
      Folder(this.name) : contents = [];
      Folder.temp() : name = 'temporary'; // Oops! Forgot contents.
    }
    

    ✅正确示范

    class Folder {
      final String name;
      final List<Document> contents = [];
    
      Folder(this.name);
      Folder.temp() : name = 'temporary';
    }
    

    构造函数

    尽可能的使用初始化形式

    ✅正确示范

    class Point {
      num x, y;
      Point(this.x, this.y);
    }
    

    ❌错误示范

    class Point {
      num x, y;
      Point(num x, num y) {
        this.x = x;
        this.y = y;
      }
    }
    
    ; 来替代空的构造函数体 {}

    ✅正确示范

    class Point {
      int x, y;
      Point(this.x, this.y);
    }
    

    ❌错误示范

    class Point {
      int x, y;
      Point(this.x, this.y) {}
    }
    
    不要 使用 new

    ✅正确示范

    Widget build(BuildContext context) {
      return Row(
        children: [
          RaisedButton(
            child: Text('Increment'),
          ),
          Text('Click!'),
        ],
      );
    }
    

    ❌错误示范

    Widget build(BuildContext context) {
      return new Row(
        children: [
          new RaisedButton(
            child: new Text('Increment'),
          ),
          new Text('Click!'),
        ],
      );
    }
    

    错误处理

    避免 使用没有 on 语句的 catch
    不要 丢弃没有使用 on 语句捕获的异常
    只在代表编程错误的情况下才抛出实现了 Error 的异常

    Error 类是所有 编码 错误的基类。当一个该类型或者其子类型, 例如 ArgumentError 对象被抛出了,这意味着是你代码中的一个 bug。 当你的 API 想要告诉调用者使用错误的时候可以抛出一个 Error 来表明你的意图。

    同样的,如果一个异常表示为运行时异常而不是代码 bug, 则抛出 Error 则会误导调用者。 应该抛出核心定义的 Exception 类或者其他类型。

    不要 显示的捕获 Error 或者其子类

    既然 Error 表示代码中的 bug, 应该展开整个调用堆栈,暂停程序并打印堆栈跟踪,以便找到错误并修复。

    捕获这类错误打破了处理流程并且代码中有 bug。 不要在这里使用错误处理代码,而是需要到导致该错误出现的地方修复你的代码

    使用 rethrow 来重新抛出捕获的异常

    如果你想重新抛出一个异常,推荐使用 rethrow 语句。 rethrow 保留了原来的异常堆栈信息。 而 throw 会把异常堆栈信息重置为最后抛出的位置

    ❌错误示范

    try {
      somethingRisky();
    } catch (e) {
      if (!canHandle(e)) throw e;
      handle(e);
    }
    

    ✅正确示范

    try {
      somethingRisky();
    } catch (e) {
      if (!canHandle(e)) rethrow;
      handle(e);
    }
    

    异步

    使用 async/await 而不是直接使用底层的特性

    ✅正确示范

    Future<int> countActivePlayers(String teamName) async {
      try {
        var team = await downloadTeam(teamName);
        if (team == null) return 0;
    
        var players = await team.roster;
        return players.where((player) => player.isActive).length;
      } catch (e) {
        log.error(e);
        return 0;
      }
    }
    

    ❌错误示范

    Future<int> countActivePlayers(String teamName) {
      return downloadTeam(teamName).then((team) {
        if (team == null) return Future.value(0);
    
        return team.roster.then((players) {
          return players.where((player) => player.isActive).length;
        });
      }).catchError((e) {
        log.error(e);
        return 0;
      });
    }
    
    不要 在没有有用效果的情况下使用 async

    当成为习惯之后,你可能会在所有和异步相关的函数使用 async。但是在有些情况下, 如果可以忽略 async 而不改变方法的行为,则应该这么做:

    ✅正确示范

    Future afterTwoThings(Future first, Future second) {
      return Future.wait([first, second]);
    }
    

    ❌错误示范

    Future afterTwoThings(Future first, Future second) async {
      return Future.wait([first, second]);
    }
    

    async有用情况:

    • 使用了 await
    • 异步的抛出一个异常。 async 然后 throwreturn new Future.error(...) 要简短很多
    • 返回一个值,但是你希望他显式的使用 Future。asyncnew Future.value(...) 要简短很多
    • 不希望在事件循环发生事件之前执行任何代码
    Future usesAwait(Future later) async {
      print(await later);
    }
    
    Future asyncError() async {
      throw 'Error!';
    }
    
    Future asyncValue() async => 'value';
    

    相关文章

      网友评论

          本文标题:Flutter 语法规范

          本文链接:https://www.haomeiwen.com/subject/xzxvkdtx.html