Flutter入门学习笔记(三):dart核心语法
1.导库方法
简单来说,通过 import
将别的库拿来用,基础语法为:
import 'uri' [as alias] [show/hide id] [deferred]
1.1 基础用法
// 1.引入系统库math
import 'dart:math';
// 2.引入自己的文件
import 'utils/string_helper.dart'; // 相对路径
import '/src/models/user.dart'; // 绝对路径(从项目根目录开始)
// 3.引入第三方包(pub上的)
import 'package:http/http.dart'; // 前缀package表示来自.packages映射
// 4.使用as引入同时起别名,解决名字冲突
import 'package:collection/collection.dart' as coll;
void main() {
coll.DeepCollectionEquality eq = coll.DeepCollectionEquality();
}
1.2 按需取用
使用 show/hide
只取部分:
import 'dart:math' show pi, cos; // 只导入pi和cos
import 'dart:math' hide Random; // 除了Random,其余都导入
1.3 懒加载
延迟加载,直至真正需要时再下载/加载:
import 'package:charts_flutter/flutter.dart' deferred as charts;
Future<void> showChart() async {
await charts.loadLibrary(); // 真正需要时再下载/加载
charts.BarChart(...);
}
1.4 大库拆分
使用 part/part of
将大库拆成多个文件:
- (1) 主库文件
lib/my_lib.dart
:
/*
* @Author: chenjingyu
* @Date: 2025-08-20 09:51:35
* @Contact: 2458006466@qq.com
* @Description: my_lib
*/
// 声明这是一个库,名字叫 my_lib
library my_lib;
// 把两个“子文件”合进来
part 'src/math_utils.dart';
part 'src/string_utils.dart';
// 主库里也可以写自己的代码
int sum(int a, int b) => a + b;
- (2) 子文件
lib/src/math_utils.dart
中:
/*
* @Author: chenjingyu
* @Date: 2025-08-20 09:52:15
* @Contact: 2458006466@qq.com
* @Description: math_utils
*/
// 必须指明属于哪个库
part of my_lib;
int multiply(int a, int b) => a * b;
- (3) 子文件
lib/src/string_utils.dart
中:
/*
* @Author: chenjingyu
* @Date: 2025-08-20 09:52:22
* @Contact: 2458006466@qq.com
* @Description: string_utils
*/
part of my_lib;
String greet(String name) => 'Hello, $name!';
- (4) 可执行文件
bin/my_lib.dart
中:
/*
* @Author: chenjingyu
* @Date: 2025-08-20 09:51:35
* @Contact: 2458006466@qq.com
* @Description: my_lib
*/
import 'package:my_lib/my_lib.dart' as my;
void main() {
print(my.sum(2, 3)); // 来自主库
print(my.multiply(4, 5)); // 来自 math_utils.dart
print(my.greet('Mirror')); // 来自 string_utils.dart
}
运行:
>> dart run
Building package executable...
Built my_lib:my_lib.
5
20
Hello, Mirror!
2.类和对象
Dart 是一门面向对象语言,支持类(class)以及基于混入(mixin)的继承机制。
- 每一个对象都是某个类的实例,除
Null
之外的所有类最终都继承自Object
。 - “基于混入的继承”指的是:除最顶层的
Object
(或Object?
)外,每个类有且仅有一个直接父类,但一个类体可以通过混入,从而被多个不同的类层次复用。 - 扩展方法(extension methods)允许在不修改原类、也不创建子类的情况下,为其添加新功能。
- 类修饰符(class modifiers)则让你可以控制其他库能否对该类进行子类化(subtyping)。
2.1 类成员访问
对象有两种成员:函数和数据(分别对应成员方法和成员变量)。我们可以使用 .
来访问一个实例的成员变量或成员方法:
var p = Point(2, 2);
// 1.获取p的成员变量y值
assert(p.y == 2);
// 2.调用p的成员方法distanceTo
double distance = p.distanceTo(Point(4, 4));
使用 ?.
代替 .
来避免当左侧操作数为 null
时抛出异常:
// 如果p非空,将变量a设置为其y值
var a = p?.y;
2.2 使用构造函数
你可以使用构造函数来创建一个对象。构造函数名可以是 ClassName
或 ClassName.identifier
。例如:
var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});
以下代码具有相同效果,但使用可选的 new
关键字在构造函数名前:
var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});
某些类提供常量构造函数,构造两个相同编译时常量会得到单一的实例:
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);
assert(identical(a, b)); // They are the same instance!
你可以使用 Object
属性 runtimeType
来在运行时获取对象类型:
print('The type of a is ${a.runtimeType}');
2.3 实例变量
使用可空类型声明的未初始化变量值为 null
,非空实例变量必须在声明是初始化。所有实例变量都会生成一个隐式 getter
方法。非 final
实例和没有初始化的 late/final
实例变量也会生成一个隐式的 setter
方法。
class Point {
double? x; // 可空变量x,初始化为null
double? y; // 可空变量y,初始化为null
}
void main() {
var point = Point();
point.x = 4; // 对x使用setter方法.
assert(point.x == 4); // 对x使用getter方法.
assert(point.y == null); // 值的默认值为null.
}
在声明处初始化非 late
实例变量会在实例创建时赋值,这发生在构造函数及其初始化列表执行之前。因此,非 late
实例变量的初始化表达式不能访问 this
:
double initialX = 1.5;
class Point {
// OK, initialX不依赖于this,可以访问:
double? x = initialX;
// ERROR, y不是late标记,不能访问this:
double? y = this.x;
// OK, z为late标记,可以访问this
late double? z = this.x;
// OK, `this.x`和`this.y`是参数声明,不是表达式
Point(this.x, this.y);
}
2.4 静态变量和讲台方法
静态变量在使用前不会被初始化,对于类范围的状态和常量非常有用。
class Queue {
static const initialCapacity = 16;
// ···
}
void main() {
assert(Queue.initialCapacity == 16);
}
静态方法(类方法)不作用于实例,因此无法访问 this
。但是,它们可以访问静态变量。
import 'dart:math';
class Point {
double x, y;
Point(this.x, this.y);
static double distanceBetween(Point a, Point b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
return sqrt(dx * dx + dy * dy);
}
}
void main() {
var a = Point(2, 2);
var b = Point(4, 4);
var distance = Point.distanceBetween(a, b);
assert(2.8 < distance && distance < 2.9);
print(distance);
}
2.5 构造函数
- (1) 生成式构造函数
创建新实例并初始化实例变量。
class Point {
// 存储点坐标的实例变量
double x;
double y;
// 生成式构造函数初始化实例变量
Point(this.x, this.y);
}
- (2) 默认构造函数
当没有指定构造函数时,用于创建新实例。它不接受参数且没有名称。
class Point {
double? x;
double? y;
}
void main() {
final p = Point();
print(p);
}
- (3) 命名构造函数
使用命名构造函数可以为一个类实现多个构造函数,或提供额外的清晰度。
const double xOrigin = 0;
const double yOrigin = 0;
class Point {
final double x;
final double y;
// Sets the x and y instance variables
// before the constructor body runs.
Point(this.x, this.y);
// Named constructor
Point.origin() : x = xOrigin, y = yOrigin;
}
子类不继承超类的命名构造函数,要在子类中创建超类中定义的命名构造函数,请在子类中实现该构造函数。
- (4) 常量构造函数
若类产生不可变对象,请将这些对象设为编译时常量。要使对象成为编译时常量,请定义一个 const
构造函数,并将所有实例变量设置为 final
。
class ImmutablePoint {
static const ImmutablePoint origin = ImmutablePoint(0, 0);
final double x, y;
const ImmutablePoint(this.x, this.y);
}
- (5) 重定向构造函数
构造函数可以将调用重定向到同一类的另一个构造函数。重定向构造函数没有函数体。构造函数在冒号 (:
) 后使用 this
而不是类名。
class Point {
double x, y;
// The main constructor for this class.
Point(this.x, this.y);
// Delegates to the main constructor.
Point.alongXAxis(double x) : this(x, 0);
}
- (6) 工厂构造函数
在实现构造函数时遇到以下两种情况之一时,使用 factory
关键字
- 构造函数并不总是创建其类的新实例。尽管工厂构造函数不能返回
null
,但它可能会返回- 缓存中的现有实例,而不是创建新实例
- 子类的新实例
- 在构造实例之前需要执行非平凡的工作。这可能包括检查参数或执行初始化列表中无法处理的任何其他处理。
class Logger {
final String name;
bool mute = false;
// _cache is library-private, thanks to
// the _ in front of its name.
static final Map<String, Logger> _cache = <String, Logger>{};
factory Logger(String name) {
return _cache.putIfAbsent(name, () => Logger._internal(name));
}
factory Logger.fromJson(Map<String, Object> json) {
return Logger(json['name'].toString());
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
2.6 方法
方法是为对象提供行为的函数。
- (1) 实例方法
对象上的实例方法可以访问实例变量和 this
。
import 'dart:math';
class Point {
final double x;
final double y;
// Sets the x and y instance variables
// before the constructor body runs.
Point(this.x, this.y);
double distanceTo(Point other) {
var dx = x - other.x;
var dy = y - other.y;
return sqrt(dx * dx + dy * dy);
}
}
- (2) 运算符
大多数运算符是具有特殊名称的实例方法。Dart 允许您定义以下名称的运算符:
< |
> |
<= |
>= |
== |
~ |
---|---|---|---|---|---|
- |
+ |
/ |
~/ |
* |
% |
` | ` | ˆ |
& |
<< |
>>> |
[]= |
[] |
class Vector {
final int x, y;
Vector(this.x, this.y);
Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
Vector operator -(Vector v) => Vector(x - v.x, y - v.y);
@override
bool operator ==(Object other) =>
other is Vector && x == other.x && y == other.y;
@override
int get hashCode => Object.hash(x, y);
}
void main() {
final v = Vector(2, 3);
final w = Vector(2, 2);
assert(v + w == Vector(4, 5));
assert(v - w == Vector(0, 1));
}
- (3) getter和setter
class Rectangle {
double left, top, width, height;
Rectangle(this.left, this.top, this.width, this.height);
// Define two calculated properties: right and bottom.
double get right => left + width;
set right(double value) => left = value - width;
double get bottom => top + height;
set bottom(double value) => top = value - height;
}
void main() {
var rect = Rectangle(3, 4, 20, 15);
assert(rect.left == 3);
rect.right = 12;
assert(rect.left == -8);
}
- (4) 抽象方法
实例、Getter 和 Setter 方法可以是抽象的,它们定义了一个接口,但将其实现留给其他类。抽象方法只能存在于抽象类或混入中。要使一个方法成为抽象方法,请使用分号 (;
) 代替方法体
abstract class Doer {
// Define instance variables and methods...
void doSomething(); // Define an abstract method.
}
class EffectiveDoer extends Doer {
void doSomething() {
// Provide an implementation, so the method is not abstract here...
}
}