面向对象编程(OOP)是 Dart 的核心特性之一。类是创建对象的蓝图或模板,而对象是类的实例。通过类和对象,我们可以将数据(属性)和行为(方法)封装在一起,从而更好地组织和管理代码。
1. 类的定义
在 Dart 中,使用 class 关键字来定义类。类可以包含属性(实例变量)和方法(函数)。
class Person {
// 属性(实例变量)
String name; // 姓名
int age; // 年龄
// 构造函数
// 默认构造函数,如果未显式定义,Dart 会提供一个无参的默认构造函数
// Person(this.name, this.age); // 简写形式
// 显式定义构造函数
Person(String name, int age) {
this.name = name;
this.age = age;
}
// 方法
void greet() {
print("Hello, my name is $name and I am $age years old.");
}
// Getter 和 Setter (可选,Dart 会自动生成)
// String get getName => name;
// set setAge(int newAge) => age = newAge;
}
2. 对象的创建
使用 new 关键字(可选)和类的构造函数来创建对象。Dart 3 之后,new 关键字可以省略。
void main() {
// 创建 Person 类的对象
var person1 = Person("Alice", 30); // 推荐写法
// Person person2 = new Person("Bob", 25); // 旧写法,new 关键字可省略
// 访问对象的属性
print(person1.name); // 输出: Alice
print(person1.age); // 输出: 30
// 调用对象的方法
person1.greet(); // 输出: Hello, my name is Alice and I am 30 years old.
// 修改对象的属性
person1.age = 31;
person1.greet(); // 输出: Hello, my name is Alice and I am 31 years old.
}
3. 构造函数
Dart 支持多种类型的构造函数:
factory 关键字定义。它不总是创建类的新实例,可以返回缓存的实例或子类的实例。工厂构造函数不能访问 this。const 关键字定义。如果一个类只包含 final 字段,并且有一个 const 构造函数,那么可以创建编译时常量对象。示例:命名构造函数和工厂构造函数
class Point {
final double x;
final double y;
// 默认构造函数
Point(this.x, this.y);
// 命名构造函数:从原点创建点
Point.origin() : this(0.0, 0.0);
// 命名构造函数:从数组创建点
Point.fromList(List<double> coords) : this(coords[0], coords[1]);
// 工厂构造函数:返回缓存的实例
static final Map<String, Point> _cache = <String, Point>{};
factory Point.fromJson(Map<String, double> json) {
final key = "${json["x"]},${json["y"]}";
if (_cache.containsKey(key)) {
return _cache[key]!;
} else {
final newPoint = Point(json["x"]!, json["y"]!);
_cache[key] = newPoint;
return newPoint;
}
}
// 常量构造函数 (如果所有字段都是 final)
const Point.constant(this.x, this.y);
}
void main() {
var p1 = Point(10.0, 20.0);
var p2 = Point.origin(); // 使用命名构造函数
var p3 = Point.fromList([5.0, 5.0]); // 使用命名构造函数
print("p1: (${p1.x}, ${p1.y})"); // 输出: p1: (10.0, 20.0)
print("p2: (${p2.x}, ${p2.y})"); // 输出: p2: (0.0, 0.0)
print("p3: (${p3.x}, ${p3.y})"); // 输出: p3: (5.0, 5.0)
var jsonPoint1 = Point.fromJson({"x": 1.0, "y": 2.0});
var jsonPoint2 = Point.fromJson({"x": 1.0, "y": 2.0});
print(jsonPoint1 == jsonPoint2); // 输出: true (因为返回的是同一个缓存实例)
const Point constPoint = Point.constant(1.0, 1.0);
}
4. 初始化列表
构造函数除了可以有参数列表和函数体外,还可以有一个初始化列表。初始化列表在构造函数体执行之前运行,常用于初始化 final 字段或在构造函数体执行前进行一些断言。
class Rectangle {
final double width;
final double height;
Rectangle(this.width, this.height) : assert(width >= 0 && height >= 0, 'Width and height must be non-negative');
double get area => width * height;
}
void main() {
var rect = Rectangle(10.0, 5.0);
print("Area: ${rect.area}"); // 输出: Area: 50.0
// var invalidRect = Rectangle(-1.0, 5.0); // 会抛出 AssertionError
}
在 Flutter 中,几乎所有的 UI 元素都是 Widget,而 Widget 本质上就是 Dart 的类。理解类和对象的概念是掌握 Flutter UI 构建和状态管理的基础。Flutter 的声明式 UI 范式大量依赖于 Widget 类的组合和属性传递。
案例:自定义用户头像组件
我们将创建一个自定义的用户头像组件 UserAvatar,它是一个 StatelessWidget,用于展示用户的头像图片和姓名。这个案例将演示类的定义、属性、构造函数以及如何在 Flutter 中组合 Widget。
import 'package:flutter/material.dart';
// 定义一个表示用户数据的类
class User {
final String name;
final String avatarUrl;
// 构造函数,使用命名参数更清晰
const User({required this.name, required this.avatarUrl});
}
// 自定义用户头像组件,它是一个 StatelessWidget
class UserAvatar extends StatelessWidget {
final User user; // 接收一个 User 对象作为属性
final double radius; // 头像半径
// 构造函数,使用 required 关键字确保 user 和 radius 必须被提供
const UserAvatar({
super.key,
required this.user,
this.radius = 30.0, // 提供默认值
});
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
CircleAvatar(
radius: radius,
backgroundImage: NetworkImage(user.avatarUrl), // 使用用户头像 URL
backgroundColor: Colors.grey[200],
child: user.avatarUrl.isEmpty
? Icon(Icons.person, size: radius) // 如果没有头像 URL,显示默认图标
: null,
),
const SizedBox(height: 8.0),
Text(
user.name, // 显示用户姓名
style: const TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold),
),
],
);
}
}
class ClassAndObjectExampleScreen extends StatelessWidget {
const ClassAndObjectExampleScreen({super.key});
@override
Widget build(BuildContext context) {
// 创建 User 类的对象
final User user1 = User(
name: '张三',
avatarUrl: 'https://avatars.githubusercontent.com/u/1?v=4', // 示例头像 URL
);
final User user2 = User(
name: '李四',
avatarUrl: 'https://avatars.githubusercontent.com/u/2?v=4', // 示例头像 URL
);
final User user3 = User(
name: '王五',
avatarUrl: '', // 没有头像 URL 的用户
);
return Scaffold(
appBar: AppBar(
title: const Text('类与对象应用案例'),
),
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
UserAvatar(user: user1), // 传递 User 对象给 UserAvatar 组件
UserAvatar(user: user2, radius: 40.0), // 传递 User 对象并指定半径
UserAvatar(user: user3), // 传递没有头像 URL 的用户
],
),
),
);
}
}
void main() {
runApp(const MaterialApp(home: ClassAndObjectExampleScreen()));
}
案例分析:
class User:我们定义了一个 User 类,它有两个 final 属性 name 和 avatarUrl。final 关键字表示这些属性在对象创建后不能被修改,这符合 Flutter 中不可变 Widget 的设计理念。const User({required this.name, required this.avatarUrl});:User 类的构造函数使用了命名参数和 required 关键字。命名参数使得创建 User 对象时代码更具可读性,required 确保了这些参数在创建对象时必须被提供。class UserAvatar extends StatelessWidget:UserAvatar 是一个自定义的 Flutter Widget,它继承自 StatelessWidget。这意味着 UserAvatar 的状态在创建后不会改变。它接收一个 User 对象和一个 radius 作为属性。final User user;:UserAvatar 组件将 User 对象作为其属性之一。这展示了如何在 Flutter 中通过类的属性来传递数据。const UserAvatar({super.key, required this.user, this.radius = 30.0,});:UserAvatar 的构造函数也使用了命名参数和 required 关键字。radius 参数还提供了默认值 30.0,使得在创建 UserAvatar 时可以省略 radius 参数。NetworkImage(user.avatarUrl):在 UserAvatar 的 build 方法中,我们使用 user.avatarUrl 来加载网络图片。这直接使用了 User 对象的属性。UserAvatar(user: user1):在 ClassAndObjectExampleScreen 中,我们创建了 User 类的实例 user1、user2 和 user3,并将这些对象作为参数传递给 UserAvatar 组件。这体现了面向对象编程中对象作为参数传递的特性。这个案例清晰地展示了 Dart 的类和对象在 Flutter 开发中的核心作用。通过定义和组合类,我们可以构建出模块化、可复用且易于管理的用户界面组件。理解如何有效地使用类和对象是编写高质量 Flutter 应用的关键。