Dart 是一种由 Google 开发的客户端优化的编程语言,适用于移动、Web、服务器和物联网(IoT)等领域的开发。它是 Flutter 框架的官方语言,用于构建高性能、高保真的跨平台应用。

一、安装 Dart SDK

  • 访问官网: 前往 Dart 官方网站:https://dart.dev
  • 百度网盘:
    链接: https://pan.baidu.com/s/1Zeq9SdRa-zkVja3Z80V1fA 提取码: 5qpx

一直next即可

验证sdk:

dart --version

二、快速入门 

 安装Dart插件:

1.基础语法

  • 入口函数: 每个 Dart 程序都必须有一个 main() 函数,它是程序执行的起点。
  • 变量与类型:
    • var: 声明变量,类型由编译器推断。
    • String: 字符串类型。
    • int: 整数类型。
    • double: 浮点数类型。
    • bool: 布尔类型 (truefalse)。
    • dynamic: 可以存储任何类型的值(不推荐过度使用,会失去类型安全)。
    • final / const: 声明不可变的变量。final 在运行时赋值一次,const 在编译时常量。
1. 声明变量

有几种方式可以声明变量:

  • 使用 var 关键字 (推荐用于局部变量):

    • var 允许编译器根据你第一次赋的值来推断变量的类型。
    • 一旦推断出类型,该变量就不能再被赋予其他类型的值。
    void main() {
      var name = 'Alice'; // 推断为 String 类型
      var age = 25;       // 推断为 int 类型
      var height = 1.75;  // 推断为 double 类型
    
      // name = 100; // 错误!不能将 int 赋值给一个 String 类型的变量
      name = 'Bob'; // 正确!可以赋值给同为 String 类型的值
    }
  • 显式指定类型:

    • 你可以明确地写出变量的数据类型,这使得代码意图更清晰,也避免了类型推断可能带来的歧义。
    void main() {
      String name = 'Charlie';
      int age = 30;
      double price = 9.99;
      bool isActive = true;
    
      // 或者先声明后赋值
      String city;
      city = 'Beijing';
    }
  • 使用 dynamic 关键字 (谨慎使用):

    • dynamic 类型的变量可以存储任何类型的值,并且可以在运行时改变其类型。
    • 注意: 使用 dynamic 会失去 Dart 的类型安全检查,容易在运行时引发错误,应尽量避免,除非有特殊需求。
    void main() {
      dynamic something = 'Hello';
      print(something.runtimeType); // String
    
      something = 42;
      print(something.runtimeType); // int
    
      something = true;
      print(something.runtimeType); // bool
    }
  • 使用 Object?:

    • Object 是所有 Dart 类的根类。Object? 表示可以是任何对象,也可以是 null
    • 它比 dynamic 更安全,但使用时需要进行类型检查或转换。
2. 变量的命名规则
  • 名称必须以字母或下划线 (_) 开头。
  • 后续字符可以是字母、数字、下划线或美元符号 ($)。
  • 区分大小写(myVariable 和 myvariable 是不同的)。
  • 不能使用 Dart 的保留关键字(如 intclassvoid 等)作为变量名。
  • 推荐使用 驼峰命名法 (camelCase),例如:userNameaccountBalance

Dart 常量 (Constants)

常量是值在初始化后不能被改变的变量。Dart 提供了两种创建常量的方式,它们的使用场景和约束不同。

1. final 关键字
  • 含义: 一个 final 变量只能被赋值一次
  • 赋值时机: 可以在运行时确定其值。也就是说,它的值可以是程序执行过程中计算出来的。
  • 作用域: 通常用于声明在对象的生命周期内不应改变的变量,或者在函数内声明一个不应被修改的值。
void main() {
  // 在声明时赋值
  final String language = 'Dart';
  // language = 'Java'; // 错误!不能重新赋值

  // 在运行时赋值(例如,获取当前时间)
  final DateTime now = DateTime.now();
  print(now); // 每次运行程序,值都不同,但赋值后就不能再改

  // final 变量也可以在构造函数中初始化(在类中)
}
2. const 关键字
  • 含义const 代表编译时常量
  • 赋值时机: 其值必须在编译时就能完全确定。它不能依赖于运行时才能知道的值。
  • 深层含义const 不仅是一个值,它还代表一个规范化的对象。这意味着,两个具有相同 const 构造的表达式会引用内存中的同一个对象
void main() {
  // 基本数据类型的编译时常量
  const int maxUsers = 100;
  const String appName = 'My App';

  // 使用 const 构造函数创建对象
  const List<int> numbers = [1, 2, 3]; // 使用 const 列表字面量
  const Map<String, int> scores = {'Alice': 95, 'Bob': 87};

  // 这是允许的,因为 DateTime.utc 是一个 const 构造函数,且参数是编译时常量
  const DateTime compileTime = DateTime(2025, 1, 1);

  // 这是错误的!DateTime.now() 的值在运行时才能知道
  // const DateTime now = DateTime.now(); // 编译错误

  // 演示 const 的“规范性”
  const List<int> list1 = [1, 2, 3];
  const List<int> list2 = [1, 2, 3];
  print(identical(list1, list2)); // true,它们是同一个对象

  final List<int> list3 = [1, 2, 3];
  final List<int> list4 = [1, 2, 3];
  print(identical(list3, list4)); // false,它们是两个不同的对象
}
final vs const 关键区别总结
特性 final const
赋值时机 运行时 (Runtime) 编译时 (Compile-time)
值的来源 可以是运行时计算的结果 必须是编译时已知的常量
对象规范性 每次 final 声明都会创建新对象 相同的 const 表达式引用同一个对象
性能 略低(运行时赋值) 更高(编译时确定,可复用)
何时使用 值在初始化后不应改变,但初始化依赖运行时 值是固定的,且希望在编译时确定和优化
  • 字符串:
    • 可以用单引号 (') 或双引号 (")。
    • 字符串插值:'Hello, $name!' 或 'Hello, ${name.toUpperCase()}!'
1. 创建字符串

你可以使用单引号 (') 或双引号 (") 来创建字符串字面量。

void main() {
  String singleQuote = 'Hello, Dart!';
  String doubleQuote = "Hello, World!";

  // 两种方式效果完全相同
  print(singleQuote); // Hello, Dart!
  print(doubleQuote); // Hello, World!

  // 引号嵌套:如果字符串内需要包含引号,可以使用不同类型的引号包围
  String withSingle = "He said, 'Hello!'"; 
  String withDouble = 'She said, "Hi!"'; 

  // 或者使用反斜杠 \ 进行转义
  String escaped1 = 'It\'s a sunny day.';
  String escaped2 = "She said, \"Hello!\"";
}
2. 多行字符串

当你的字符串内容跨越多行时,可以使用以下两种方式:

  • 三重引号 ('''"""):

    • 使用三个单引号或三个双引号。
    • 字符串内的换行和空格会被原样保留。
    String multiLine1 = '''
    This is line one.
    This is line two.
      This line has leading spaces.
    ''';
    
    String multiLine2 = """
    JSON example:
    {
      "name": "Alice",
      "age": 30
    }
    """;
  • 相邻字符串拼接:

    • 将多个字符串字面量写在一起,Dart 会自动将它们连接成一个字符串。
    • 这种方式不会保留字面量之间的换行符。
    String concatenated = 'This is the first part '
                        'and this is the second part '
                        'on the same line.';
    // 结果: "This is the first part and this is the second part on the same line."
3. 字符串插值 (String Interpolation)

这是 Dart 字符串最强大的特性之一。你可以在字符串中嵌入变量或表达式的值。

  • 语法: 使用 $variableName 或 ${expression}
  • 如果只是插入一个简单的变量名,使用 $ 加变量名即可。
  • 如果要插入一个表达式、属性或更复杂的逻辑,必须用 ${} 包裹。
void main() {
  String name = 'Bob';
  int age = 25;
  double height = 1.80;

  // 插入变量
  String greeting = 'Hello, $name!'; 
  print(greeting); // Hello, Bob!

  // 插入表达式
  String info1 = '$name is ${age + 1} years old next year.'; 
  print(info1); // Bob is 26 years old next year.

  // 调用方法或访问属性
  String info2 = 'The length of the name is ${name.length}.'; 
  String info3 = '${name.toUpperCase()} is shouting!'; 
  print(info2); // The length of the name is 3.
  print(info3); // BOB is shouting!

  // 当变量名后紧跟字母数字字符时,必须用 {} 包裹
  String tricky = 'His name is ${name}y'; // 正确: Buby
  // String wrong = 'His name is $namey'; // 错误!编译器会找名为 'namey' 的变量
}
4. 字符串拼接

有几种方式可以将两个或多个字符串连接起来。

  • 使用 + 操作符:

    String firstName = 'John';
    String lastName = 'Doe';
    String fullName = firstName + ' ' + lastName; // John Doe
  • 使用相邻字符串 (如上所述):

    String sentence = 'Welcome to ' 'Dart programming!'; // Welcome to Dart programming!
  • 使用字符串插值:

    String fullName = '$firstName $lastName'; // 更简洁
  • 使用 StringBuffer 类 (推荐用于大量拼接):

    • 当你需要进行大量的字符串拼接操作时(例如在循环中),使用 + 会产生很多中间的字符串对象,效率较低。
    • StringBuffer 是一个可变的缓冲区,专门为此优化。
    void main() {
      StringBuffer buffer = StringBuffer();
      buffer.write('Hello');
      buffer.write(' ');
      buffer.write('World');
      buffer.writeln('!'); // write + 换行
      buffer.writeAll(['a', 'b', 'c'], '-'); // 用 '-' 连接并写入
    
      String result = buffer.toString(); // 转换为最终的 String
      print(result);
      /*
      Hello World!
      a-b-c
      */
    }

Dart 字符串的常用属性和方法

String 类提供了丰富的 API 来操作和查询字符串。

常用属性
  • length: 返回字符串的长度(字符数)。
  • isEmpty: 如果字符串为空(长度为0),返回 true
  • isNotEmpty: 如果字符串不为空,返回 true
String emptyStr = '';
String text = 'Dart';

print(text.length);     // 4
print(emptyStr.isEmpty); // true
print(text.isNotEmpty); // true
常用方法
  • 大小写转换:

    • toUpperCase(): 转换为大写。
    • toLowerCase(): 转换为小写。
    print('hello'.toUpperCase()); // HELLO
    print('WORLD'.toLowerCase()); // world
  • 查找和判断:

    • contains(Pattern pattern, [int startIndex = 0]): 判断是否包含某个子串或模式。
    • startsWith(Pattern pattern, [int index = 0]): 判断是否以某个模式开头。
    • endsWith(Pattern other): 判断是否以某个模式结尾。
    • indexOf(String needle, [int start = 0]): 返回子串第一次出现的索引,未找到返回 -1。
    String email = 'user@example.com';
    print(email.contains('@'));           // true
    print(email.startsWith('user'));      // true
    print(email.endsWith('.com'));        // true
    print(email.indexOf('example'));      // 5
  • 截取和分割:

    • substring(int start, [int? end]): 提取从 start 到 end(不包括)的子串。
    • split(Pattern pattern): 根据分隔符拆分成一个 List<String>
    String path = '/home/user/documents/file.txt';
    print(path.substring(1, 5));         // home
    print(path.split('/'));              // ['', 'home', 'user', 'documents', 'file.txt']
    print('a,b,c'.split(','));           // ['a', 'b', 'c']
  • 去除空白:

    • trim(): 去除字符串首尾的空白字符(空格、制表符、换行符等)。
    • trimLeft() / trimRight(): 分别去除左侧/右侧空白。
    String messy = '  Hello Dart  \n';
    print(messy.trim()); // 'Hello Dart'
  • 替换:

    • replaceAll(Pattern from, String replace): 将所有匹配项替换为新字符串。
    String text = 'I love cats. Cats are great.';
    print(text.replaceAll('Cats', 'Dogs')); // I love dogs. Dogs are great.
  • 其他:

    • compareTo(String other): 按字典顺序比较两个字符串,返回负数、0或正数。
    • codeUnitAt(int index): 获取指定索引处的 UTF-16 码元。

重要注意事项

  1. 不可变性 (Immutability): Dart 的 String 是不可变的。任何看起来“修改”了字符串的方法(如 toUpperCase, trim, replaceAll)实际上都返回了一个新的字符串对象,而原始字符串保持不变。

    String original = 'hello';
    String upper = original.toUpperCase(); 
    print(original); // hello (未改变)
    print(upper);    // HELLO (新对象)
  2. UTF-16 编码: Dart 字符串基于 UTF-16。对于大多数基本拉丁字母,一个字符对应一个码元。但对于一些特殊字符(如某些 emoji 或 Unicode 码点大于 U+FFFF 的字符),可能需要两个码元(代理对)。因此,length 属性返回的是码元的数量,而不是直观的“字符”数量。如果需要精确的字符计数,可以使用 characters 包。

    # pubspec.yaml
    dependencies:
      characters: ^1.2.1
    import 'package:characters/characters.dart';
    
    void main() {
      String emojiStr = '👨‍👩‍👧‍👦'; // 家庭 emoji
      print(emojiStr.length); // 可能输出 7 或 8 (码元数)
      print(emojiStr.characters.length); // 输出 1 (正确的字符数)
    }
  • 集合 (Collections):
    • List: 有序集合,类似数组。var list = [1, 2, 3];
    • Set: 无序且唯一元素的集合。var set = {1, 2, 3};
    • Map: 键值对集合。var map = {'name': 'Alice', 'age': 30};
  • 控制流:
    • if / else
    • for 循环 (for (var i = 0; i < 5; i++)) 和 for-in 循环 (for (var item in list))
    • while / do-while
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐