Flutter入门:Flutter开发必备Dart基础

Flutter之Dart常用数据类型(数字、类型转换)

1. Dart 数字类型简介

类型 说明 备注
int 64位整数 适合整数,索引、计数等场景
double 64位浮点数(双精度) 适合小数、精确度要求不高的场景
num intdouble 的父类 接受整数和浮点数的通用类型
BigInt 任意大小整数 用于超大整数计算或加密等场景

2. 数字变量示例

dart 复制代码
int a = 100;
double b = 3.1415;
num c = 50;       // 也可以赋 int 或 double
num d = 2.718;
BigInt big = BigInt.parse('123456789012345678901234567890');

3. 数字运算示例

dart 复制代码
int x = 10;
double y = 3.5;

var sum = x + y;      // 13.5,结果是 double
var div1 = x / 3;     // 3.3333 double
var div2 = x ~/ 3;    // 3,整数除法,舍弃小数部分

print(x.isEven);     // true
print(y.round());    // 4 四舍五入
print(y.floor());    // 3 向下取整
print(y.ceil());     // 4 向上取整

4. 类型转换

int 与 double 互转

dart 复制代码
int i = 5;
double d = i.toDouble();   // int -> double

double e = 3.7;
int j = e.toInt();         // double -> int(直接截断)
int k = e.round();         // 四舍五入
int l = e.floor();         // 向下取整
int m = e.ceil();          // 向上取整

BigInt 与 int 互转

dart 复制代码
BigInt bigInt = BigInt.from(123456789);
int normalInt = bigInt.toInt();   // 可能溢出,慎用

5. 字符串与数字的转换

字符串转数字

dart 复制代码
int x = int.parse('123');
double y = double.parse('3.14');

int? maybeInt = int.tryParse('abc');  // 解析失败返回 null

数字转字符串

dart 复制代码
String s1 = 123.toString();              // "123"
String s2 = 3.14159.toStringAsFixed(2); // "3.14" 保留两位小数
String s3 = 255.toRadixString(16);      // "ff" 转16进制字符串

6. 其它常用小技巧

  • 使用 num 类型时要注意,部分方法不同,如 ~/ 只能用于整数类型。
  • BigInt 不能直接与 intdouble 混合运算,需转换后再操作。
  • tryParse 可避免因格式错误导致程序异常崩溃。

7. 总结

操作 关键函数/方法 备注
整数与浮点转换 toDouble(), toInt(), round() 常用转换方式
字符串转数字 int.parse(), double.parse(), tryParse() 容错推荐用 tryParse
数字转字符串 toString(), toStringAsFixed(), toRadixString() 格式化输出
大整数处理 BigInt.parse(), BigInt.from() 用于大数及加密计算

Flutter之Dart常用数据类型(字符串)

1. Dart 中字符串的基本介绍

  • 类型String,表示一串 UTF-16 编码的字符序列。

  • 特点

    • 不可变(immutable),即字符串内容不可更改,修改会生成新字符串。
    • 支持单引号 '...' 或双引号 "..." 定义。
    • 支持多行字符串,使用三引号 '''...'''"""..."""

2. 字符串声明示例

dart 复制代码
String s1 = 'Hello, Dart!';
String s2 = "Hello, Flutter!";
String s3 = '''这是
一个多行
字符串''';

String s4 = """另一种
多行
字符串""";

3. 字符串拼接

dart 复制代码
String a = 'Hello';
String b = 'World';

String c = a + ' ' + b;            // 使用 + 连接
String d = '$a $b';                // 使用字符串插值
String e = '${a.toUpperCase()} $b'; // 表达式插值

4. 字符串常用操作方法

方法 说明 示例
length 字符串长度 'abc'.length // 3
isEmpty / isNotEmpty 是否为空 ''.isEmpty // true
toUpperCase() 转大写 'abc'.toUpperCase() // 'ABC'
toLowerCase() 转小写 'ABC'.toLowerCase() // 'abc'
contains() 是否包含子串 'hello'.contains('ll') // true
startsWith() 是否以指定字符串开头 'hello'.startsWith('he') // true
endsWith() 是否以指定字符串结尾 'hello'.endsWith('lo') // true
substring(start, [end]) 截取子串 'hello'.substring(1,4) // 'ell'
indexOf() 查找子串第一次出现的位置 'hello'.indexOf('l') // 2
lastIndexOf() 查找子串最后一次出现的位置 'hello'.lastIndexOf('l') // 3
replaceAll() 替换所有匹配子串 'hello'.replaceAll('l', 'x') // 'hexxo'
split() 按分隔符拆分字符串成列表 'a,b,c'.split(',') // ['a','b','c']
trim() 去除首尾空白 ' hello '.trim() // 'hello'

5. 字符串插值与表达式

dart 复制代码
String name = 'Alice';
int age = 30;

String greeting = 'Hello, $name. You are $age years old.';
String complex = 'Next year you will be ${age + 1} years old.';

6. 多行字符串和原始字符串(Raw String)

  • 多行字符串:
dart 复制代码
String multiLine = '''
这是一个
多行字符串
示例。
''';
  • 原始字符串(不转义 ``):
dart 复制代码
String raw = r'In a raw string, \n is not special.';

7. 常用转义字符

转义符 说明 示例
' 单引号 'It's OK'
" 双引号 "She said "Hi""
\ 反斜杠 'C:\Path'
\n 换行 'Line1\nLine2'
\t 制表符 'Column1\tColumn2'

8. 字符串编码与解码(简单示例)

dart 复制代码
import 'dart:convert';

String text = 'Hello, Dart!';
List<int> utf8Bytes = utf8.encode(text);      // 编码为 UTF8 字节
String decoded = utf8.decode(utf8Bytes);     // 解码回字符串

9. 字符串和字符

  • Dart 没有专门的 char 类型,单个字符用字符串的单字符表示。
dart 复制代码
String char = 'A';  // 单字符字符串
print(char.length); // 1

10. 示例:统计字符串中某字符出现次数

dart 复制代码
String str = "banana";
int count = str.split('a').length - 1;  // 计算 'a' 出现次数
print(count);  // 输出 3

Flutter之Dart常用数据类型(布尔、List)

1. 布尔类型(bool)

基础介绍

  • Dart 中布尔类型用 bool 表示,只有两个值:truefalse
  • 常用于条件判断、循环控制、开关标志等。

示例

dart 复制代码
bool isActive = true;
bool isDone = false;

if (isActive) {
  print('激活状态');
} else {
  print('未激活');
}

常用逻辑运算符

运算符 说明 示例
! 取反(NOT) !isActive
&& 逻辑与(AND) isActive && isDone
` `

2. 列表类型(List)

基础介绍

  • List 是 Dart 中的有序集合,类似数组。
  • 可以存储相同类型或混合类型的元素(如果未指定泛型)。
  • 支持动态长度,也可以声明为固定长度。

声明方式

dart 复制代码
// 泛型指定为int
List<int> numbers = [1, 2, 3, 4];

// 类型自动推断
var fruits = ['apple', 'banana', 'orange'];

// 空列表
List<String> emptyList = [];

// 固定长度列表
var fixedList = List.filled(3, 0); // 长度3,初始值0

常用操作

dart 复制代码
var list = [10, 20, 30];

// 访问元素
print(list[0]);   // 10

// 修改元素
list[1] = 25;

// 添加元素
list.add(40);

// 插入元素
list.insert(1, 15);

// 删除元素
list.remove(30);
list.removeAt(0);

// 列表长度
print(list.length);

// 遍历列表
for (var item in list) {
  print(item);
}

常用方法总结

方法 说明
add(value) 在末尾添加元素
insert(index, value) 在指定位置插入元素
remove(value) 删除首次出现的指定元素
removeAt(index) 删除指定位置元素
clear() 清空列表
contains(value) 判断是否包含指定元素
indexOf(value) 查找元素首次出现的索引
sort() 对列表排序
reversed 返回元素反转后的 Iterable

3. 结合示例

dart 复制代码
bool isLoggedIn = false;
List<String> messages = [];

if (!isLoggedIn) {
  messages.add('请先登录');
}

messages.add('欢迎回来!');

for (var msg in messages) {
  print(msg);
}

Flutter之Dart常用数据类型(Map)

1. Map 基础介绍

  • Map 是键值对(key-value)集合,也称为字典或哈希表。
  • 每个键对应一个值,键不允许重复,值可以重复。
  • 键和值的类型可以是任意类型,但通常键是 String 或基本类型。
  • Dart 的 Map 是无序的,若需要有序的 Map 可用 LinkedHashMap

2. Map 声明与初始化

dart 复制代码
// 空 Map,键和值都为 String
Map<String, String> capitals = {};

// 直接初始化
var countries = {
  'USA': 'Washington, D.C.',
  'Japan': 'Tokyo',
  'China': 'Beijing',
};

// 使用构造函数创建空 Map
var emptyMap = Map<String, int>();

// 也可以用 var,类型由内容推断
var scores = {'Alice': 90, 'Bob': 85, 'Cindy': 92};

3. Map 常用操作

dart 复制代码
var map = {'apple': 3, 'banana': 5};

// 访问
print(map['apple']);      // 3
print(map['orange']);     // null,如果key不存在

// 修改或添加
map['apple'] = 4;         // 修改
map['orange'] = 7;        // 新增

// 删除
map.remove('banana');

// 判断是否包含key或value
print(map.containsKey('apple'));   // true
print(map.containsValue(7));       // true

// 获取所有key和value
print(map.keys);   // (apple, orange)
print(map.values); // (4, 7)

// 清空
map.clear();

4. 遍历 Map

方式一:遍历键值对

dart 复制代码
var map = {'a': 1, 'b': 2};

map.forEach((key, value) {
  print('$key -> $value');
});

方式二:遍历 key 或 value 列表

dart 复制代码
for (var key in map.keys) {
  print('Key: $key');
}

for (var value in map.values) {
  print('Value: $value');
}

5. Map 与 JSON 互转(常见场景)

Flutter 常用 Map<String, dynamic> 来表示 JSON 对象。

dart 复制代码
import 'dart:convert';

String jsonString = '{"name":"Alice","age":25}';

// JSON字符串转Map
Map<String, dynamic> user = json.decode(jsonString);

// Map转JSON字符串
String jsonStr = json.encode(user);

print(user['name']);  // Alice

6. 总结

操作 方法/语法 说明
创建 Map {}Map() 声明空或初始化 Map
访问元素 map[key] 通过 key 访问值
添加/修改元素 map[key] = value 新增或修改元素
删除元素 map.remove(key) 删除指定 key 的元素
判断包含 containsKey(key) 是否存在指定 key
遍历 forEach(), for...in 遍历 key-value 或 keys/values

dynamic、var、Object的使用与区别

1. var

  • 含义:由编译器根据赋值自动推断变量类型。

  • 特点

    • 类型一旦推断确定,变量类型固定,后续赋值必须是相同类型。
    • 不能重新赋不同类型的值,否则编译错误。

示例

dart 复制代码
var x = 10;    // x 是 int 类型
x = 20;        // 合法
// x = 'hello';  // 编译错误,不能赋值 String 给 int 类型的变量
  • 如果没有初始化,var 就不能推断类型,必须显式声明类型:
dart 复制代码
var y;         // 编译错误,必须初始化或指定类型
int y;         // 正确,声明未初始化的 int 变量

2. dynamic

  • 含义:动态类型,关闭类型检查。

  • 特点

    • 赋值后变量类型可以随时改变。
    • 编译时不检查调用的方法和属性,运行时才会报错(类似弱类型语言)。
    • 灵活但风险较高,易出现运行时错误。

示例

dart 复制代码
dynamic a = 10;
a = 'hello';        // 合法,动态类型可以改变
a.someUndefinedMethod(); // 编译不报错,运行时才会出错

3. Object

  • 含义:所有 Dart 对象的基类(顶层父类)。

  • 特点

    • 编译器会检查方法和属性调用是否合法(需类型转换)。
    • 赋值可以是任何类型,但访问方法时需类型断言或转换。
    • dynamic 更安全,但比 var 更通用。

示例

dart 复制代码
Object obj = 10;
obj = 'hello';

// 不能直接调用字符串方法
// obj.length; // 编译错误

// 需先转换类型
if (obj is String) {
  print(obj.length); // 合法
}

4. 三者对比总结

特性 var dynamic Object
类型推断 编译时根据初始值推断固定类型 动态类型,无类型检查 编译时类型为 Object
类型安全 类型安全,后续赋值必须相同类型 不安全,任何类型均可赋值,调用不报错 相对安全,调用时需类型转换
访问成员 可调用变量实际类型的方法和属性 任何方法都可调用,运行时可能异常 只能调用 Object 定义的方法,需强转访问
赋值 必须符合推断类型 可随意更改类型 可赋任何类型,但限制访问

总结:

  • dynamicObject 最大的区别是:
    dynamic 关闭了静态类型检查,Object 则有静态类型检查。

5. 适用场景

类型 使用场景
var 普通变量声明,类型已知且确定,推荐使用
dynamic 需要完全动态、灵活的场景,如解析未知结构的数据、反射等
Object 需要通用类型但保持一定安全性时,作为基类引用使用

6. 示例代码综合比较

dart 复制代码
void main() {
  var x = 10;
  // x = 'hello'; // 编译错误

  dynamic y = 10;
  y = 'hello';    // 合法
  // y.noSuchMethod(); // 运行时错误,编译不报错

  Object z = 10;
  z = 'hello';
  // print(z.length); // 编译错误,需转换
  if (z is String) {
    print(z.length); // 合法,输出5
  }
}

Flutter中常用的Dart方法类型

方法的构成:返回值类型 + 方法名 + 参数

  • 返回值类型

    • 可以省略,默认是 void,也可以是具体类型如 intString 等。为了规范,一般有具体返回值的都需要写
  • 方法名

    • 匿名方法不需要方法名,这时调用时会用变量名或上下文来引用。
  • 参数

    • 包含参数类型和参数名。
    • 参数类型可省略,Dart 允许类型推断。
    • 参数可以是必选参数,也可以是可选参数(带默认值)。
    • 参数支持位置可选参数和命名可选参数。

1. 入口方法(main)

dart 复制代码
void main() {
  print('程序入口');
}

2. 实例方法

dart 复制代码
class Person {
  String name;

  Person(this.name);

  void greet() {
    print('Hello, $name');
  }
}

var p = Person('Alice');
p.greet();  // Hello, Alice

3. 私有方法

前边加下划线表示私有,用于方法、变量等。

dart 复制代码
class Example {
  void publicMethod() {
    _privateMethod();
  }

  void _privateMethod() {
    print('这是私有方法,只能在本库访问');
  }
}

var e = Example();
e.publicMethod();  // 可以调用私有方法间接访问
// e._privateMethod(); // 外部调用会报错

4. 匿名方法

dart 复制代码
var list = [1, 2, 3];
list.forEach((item) {
  print(item * 2);
});
// 输出 2,4,6

5. 静态方法

dart 复制代码
class MathUtil {
  static int add(int a, int b) => a + b;
}

print(MathUtil.add(3, 4));  // 7

6. 构造方法

dart 复制代码
class Point {
  int x, y;

  Point(this.x, this.y);  // 构造方法
}

var pt = Point(10, 20);
print('${pt.x}, ${pt.y}');  // 10, 20

7. setters 和 getters

dart 复制代码
class Rectangle {
  double _width = 0, _height = 0;

  double get area => _width * _height;

  set width(double w) => _width = w;
  set height(double h) => _height = h;
}

var rect = Rectangle();
rect.width = 5;
rect.height = 10;
print(rect.area);  // 50

8. 抽象方法

dart 复制代码
abstract class Animal {
  void speak();  // 抽象方法,子类必须实现
}

class Dog extends Animal {
  @override
  void speak() {
    print('汪汪');
  }
}

var dog = Dog();
dog.speak();  // 汪汪

9. 泛型方法

dart 复制代码
T echo<T>(T value) {
  return value;
}

print(echo<int>(123));     // 123
print(echo<String>('Hi')); // Hi

Flutter中的面向对象(标准构造方法、初始化列表与命名构造方法)

1. 标准构造方法(Default Constructor)

  • 最常用的构造函数,直接用类名定义。
  • 可带参数,常用于初始化对象属性。

示例

dart 复制代码
class Person {
  String name;
  int age;

  // 标准构造方法
  Person(this.name, this.age);
}

var p = Person('Alice', 30);
print(p.name);  // Alice

2. 初始化列表(Initializer List)

  • 用于在构造函数体执行前初始化字段。
  • 可以做一些复杂的计算、断言等。
  • 初始化列表在冒号 : 后,构造函数体 {} 前。

示例

dart 复制代码
class Point {
  final int x;
  final int y;
  final double distance;

  // 初始化列表计算 distance
  Point(this.x, this.y) 
    : distance = (x * x + y * y).toDouble().sqrt();
}

var pt = Point(3, 4);
print(pt.distance);  // 5.0

注意:

Dart 标准库里 sqrt()dart:math 里,用法如下:

dart 复制代码
import 'dart:math';

Point(this.x, this.y) : distance = sqrt(x * x + y * y);

3. 命名构造方法(Named Constructor)

  • 语法:ClassName.constructorName(...),前面factory

  • 在初始化列表里或构造体中,直接给 final 字段赋值。

  • 只能返回自身的新实例。

  • 补充 :除了普通命名构造,也可以写重定向构造

    dart 复制代码
    Point.zero() : this(0, 0);

示例

dart 复制代码
class Person {
  String name;
  int age;

  // 标准构造方法
  Person(this.name, this.age);

  // 命名构造方法,默认年龄
  Person.withNameOnly(this.name) : age = 18;

  // 命名构造方法,工厂构造
  Person.guest() {
    name = 'Guest';
    age = 0;
  }
}

var p1 = Person('Alice', 30);
var p2 = Person.withNameOnly('Bob');
var p3 = Person.guest();

print(p2.age);  // 18
print(p3.name); // Guest

4. 工厂构造方法

类似于java中的单例实现。

  • 严格说,这也是 factory 构造,只不过常见用例是单例模式

    dart 复制代码
    class Logger {
      factory Logger() => _instance ??= Logger._();
      static Logger? _instance;
      Logger._();
    }
  • 但它的能力不止单例:

    • 缓存已有对象
    • 抽象类返回具体子类
    • JSON、数据库等场景下的"解析工厂"
  • 补充 :任何带 factory 关键字的构造都属于"工厂构造",命名或未命名都行。

dart 复制代码
class Logger {
  static final Map<String, Logger> _cache = {};

  final String name;

  // 工厂构造方法,返回缓存实例
  factory Logger(String name) {
    if (_cache.containsKey(name)) {
      return _cache[name]!;
    } else {
      final logger = Logger._internal(name);
      _cache[name] = logger;
      return logger;
    }
  }

  Logger._internal(this.name);

  void log(String msg) {
    print('[$name] $msg');
  }
}

var logger1 = Logger('UI');
var logger2 = Logger('UI');

print(logger1 == logger2);  // true,返回同一个实例

5. 命名工厂构造方法

  • 语法:在命名构造方法前加 factory,形如 factory ClassName.name(...)

  • 不能 使用初始化列表(因此不能直接给 final 字段赋值),只能在函数体里 return 一个实例。

  • 可返回已缓存的实例、子类实例,甚至在抽象类中返回具体实现。

  • 补充 :factory 构造方法不能声明为 const

dart 复制代码
class Shape {
  factory Shape.circle(double radius) => Circle(radius);
  factory Shape.square(double size) => Square(size);
}

class Circle implements Shape {
  final double radius;
  Circle(this.radius);
}

class Square implements Shape {
  final double size;
  Square(this.size);
}

var circle = Shape.circle(5);
var square = Shape.square(10);

// 命名工厂构造方法返回不同子类
class Shape {
  factory Shape.circle(double radius) => Circle(radius);
  factory Shape.square(double size) => Square(size);
}

总结

关键点 说明
标准构造方法 类名作为函数名,简单初始化字段
初始化列表 在构造体执行前初始化字段或计算属性
命名构造方法 多个构造方法,提供灵活的初始化途径,返回值 自身新实例 可以为const,常用于 参数多样的初始化
工厂构造方法 使用 factory 关键字声明,不一定返回新实例,可返回已有实例,常用于缓存和控制实例创建
命名工厂构造方法 带名字的工厂构造方法,支持多种工厂创建方案,封装复杂初始化逻辑,返回多种类型对象,可返回已有实例、子类、null,不能是const,缓存/单例/多态工厂/抽象工厂

Dart的get和set方法

在 Dart 中,getset 关键字用来定义属性访问器,让我们可以像访问字段一样调用方法,同时还可以在读取或写入时添加自定义逻辑。l类似于java中的getter与setter。


1. 基本语法

dart 复制代码
class Person {
  String _name; // 私有字段,下划线表示私有

  // Getter:无参数,返回类型可省略或写具体类型
  String get name => _name;

  // Setter:单参数,返回值必须是 void
  set name(String value) {
    _name = value;
  }

  Person(this._name);
}

使用方式:

dart 复制代码
var p = Person('Alice');
print(p.name);   // 调用 getter,输出 "Alice"
p.name = 'Bob';  // 调用 setter
print(p.name);   // 输出 "Bob"

2. 只读或只写属性

  • 只读 :只定义 get,不提供 set(外部无法赋值)
  • 只写 :只定义 set,不提供 get(外部无法读取)
dart 复制代码
class Counter {
  int _count = 0;

  // 只读
  int get count => _count;

  // 只写
  set increment(int delta) {
    _count += delta;
  }
}

var c = Counter();
print(c.count);    // 0
c.increment = 5;   // 相当于调用 setter,_count += 5
print(c.count);    // 5
// c.count = 10;   // 编译错误:没有对应 setter
// print(c.increment); // 编译错误:没有对应 getter

3. 带验证或计算逻辑

dart 复制代码
class Rectangle {
  double _width, _height;

  Rectangle(this._width, this._height);

  // 计算属性:无需存字段,每次都从宽高算
  double get area => _width * _height;

  // 带验证的 setter:宽度必须>0
  set width(double w) {
    if (w <= 0) throw ArgumentError('宽度必须大于0');
    _width = w;
  }

  set height(double h) {
    if (h <= 0) throw ArgumentError('高度必须大于0');
    _height = h;
  }
}

4. 顶层(或静态)getter/setter

除了类内部,你也可以在最外层定义顶层属性访问:

dart 复制代码
int _globalCache = 0;

int get globalCache => _globalCache;
set globalCache(int v) {
  _globalCache = v;
}

或者在类里定义静态访问器:

dart 复制代码
class Config {
  static String _env = 'prod';

  static String get env => _env;
  static set env(String e) {
    _env = e;
  }
}

5. 注意事项

  • get 必须返回值,不能有参数;set 必须是 void 且只接受一个参数。
  • 属性访问器在外看起来就像字段,调用时无需加 ()
  • 如果没有显式定义 getter 或 setter,Dart 会自动为公有字段生成默认的访问器。

小结

  • get foo => ...:定义一个可读属性
  • set foo(value) { ... } :定义一个可写属性
  • 可用于封装私有字段、添加验证逻辑、计算属性、顶层/静态配置等。

Dart的抽象类和方法

抽象类(abstract class

  • 定义方式 :在类名前加 abstract,例如 abstract class Study

  • 内容:可以包含

    • 抽象方法(无方法体,需要子类实现)
    • 具体方法(带方法体,可直接复用或重写)
  • 特性

    • 不能被直接实例化
    • 用于定义接口并提供部分实现

抽象方法

  • 定义方式 :在抽象类中声明方法但不写方法体,例如

    dart 复制代码
    abstract class Study {
      void study();    // 抽象方法
    }
  • 关键点

    • 无需不允许abstract 关键字修饰
    • 子类必须实现所有抽象方法,否则编译失败
    • 可以存在多个抽象方法,提供接口契约

抽象类的作用

  • 替代接口 :Dart 无独立 interface,抽象类既能定义契约,也能提供默认实现
  • 模板方法:把共通行为写在抽象类,变化部分留给子类
  • 代码组织:通过继承保持一致性,通过重写实现可扩展性

实现(继承)抽象类

  • 继承方式

    dart 复制代码
    class StudyDart extends Study {
      @override
      void study() { /* 实现抽象方法 */ }
    }
  • 须实现 :抽象类中声明的所有抽象方法

  • 可选重写:抽象类中已有具体实现的方法,可选择重写或直接复用

实例化

  • 抽象类 :不能 new Study()
  • 子类:只要实现完抽象方法,就可被实例化

特性 抽象类 抽象方法
定义关键字 abstract class ClassName { ... } 在抽象类中声明、不写方法体
方法体 可包含具体实现方法 允许方法体
实例化 不能被实例化 ------
子类要求 必须实现所有抽象方法,或自身也标记为 abstract 子类必须重写,否则编译报错
主要用途 定义接口+可选默认实现(替代接口) 强制子类提供特定行为

在Flutter中使用mixin

Mixin 的定义与特征 当我们想 "给任意类加一项新能力" ,又不希望因为这项能力去改变它们的继承层级,就非常适合用 Mixin。

  • 代码复用工具:可以把一组方法和属性"混入"多个类,避免多重继承的复杂性。
  • 必须继承自 Object :不能 extends 其他类,也无法调用 super
  • 无构造函数 :不能定义构造方法,也不能在 mixin 里调用 super()

如何创建与使用 Mixin

  1. 定义(Dart 3.0+ 必须)

    dart 复制代码
    mixin StudyMixin {
      void study2() {
        print('Mixin: study2 方法被调用');
      }
    }
  2. 命名规范

    • 推荐以 Mixin 为后缀,比如 StudyMixinLoggerMixin,便于识别。
  3. 使用

    dart 复制代码
    class Person {
      final String name;
      Person(this.name);
    }
    
    // Test 继承自 Person,并"with" StudyMixin
    class Test extends Person with StudyMixin {
      Test(String name): super(name);
    
      void demo() {
        print('$name 开始 Demo');
        study2(); // 调用 mixin 方法
      }
    }

完整示例

dart 复制代码
// 1. 定义一个 Mixin 
mixin StudyMixin {
  void study2() {
    print('StudyMixin → study2() 被调用');
  }
}

// 2. 基础类
class Person {
  final String name;
  Person(this.name);
}

// 3. 混入 Mixin 的子类
class Test extends Person with StudyMixin {
  Test(String name) : super(name);

  void demo() {
    print('$name: demo 方法开始');
    study2(); // Mixin 提供的方法
  }
}

void main() {
  final t = Test('Alice');
  t.demo();
  // 输出:
  // Alice: demo 方法开始
  // StudyMixin → study2() 被调用
}

注意事项

  • Dart 3.0+ 必须mixin 关键字定义。
  • Mixin 中不能有构造函数,也不可调用 super()
  • 如果需要对宿主类有类型限制,可使用 on 关键字做约束(例如 mixin M on SomeBase)。

Dart泛型在Flutter中的应用

1. 泛型概述

泛型(Generics)允许在类、接口或方法中使用类型参数,使代码在保持类型安全的前提下具备更高的复用性。Dart 中使用尖括号 <> 来声明类型参数。


2. 泛型类:Cache<T> 示例

dart 复制代码
class Cache<T> {
  final Map<T, T> _storage = {};

  /// 设置缓存
  void setItem(T key, T value) {
    _storage[key] = value;
  }

  /// 获取缓存,若不存在则返回 null
  T? getItem(T key) {
    return _storage[key];
  }
}
  • 说明

    • T 为类型参数,可以是任意类型。
    • 内部使用 Map<T, T> 存储键值对。
    • getItem 返回可空类型 T?,避免直接抛出异常。

3. 泛型方法示例

dart 复制代码
/// 从列表中取出第一个元素
T? getFirst<T>(List<T> items) {
  return items.isNotEmpty ? items.first : null;
}
  • 说明

    • 方法前也可声明类型参数 <T>,根据调用时传入的类型自动推断。

4. 实际应用:存储不同类型

dart 复制代码
void main() {
  // 存储字符串
  final stringCache = Cache<String>();
  stringCache.setItem('greet', 'hello');
  print(stringCache.getItem('greet')); // hello

  // 存储整数
  final intCache = Cache<int>();
  intCache.setItem(1, 100);
  print(intCache.getItem(1)); // 100

  // 泛型方法
  print(getFirst<int>([10, 20, 30])); // 10
}

5. 高级应用:泛型约束与通用接口

  • 泛型约束

    限制类型参数必须是某个类的子类或实现某个接口。例如,只缓存 num 类型及其子类:

    dart 复制代码
    class NumCache<T extends num> {
      final Map<String, T> _map = {};
      void set(String key, T value) => _map[key] = value;
      T? get(String key) => _map[key];
    }
  • 通用接口

    定义一个仓储接口,并让具体类实现:

    dart 复制代码
    abstract class Repository<T> {
      T fetchById(String id);
      void save(T item);
    }
    
    class User { final String name; User(this.name); }
    
    class UserRepository implements Repository<User> {
      @override
      User fetchById(String id) => User('User-$id');
    
      @override
      void save(User item) {
        // 保存逻辑
      }
    }

有哪些可以用在Flutter上的编程技巧?

  1. 面向对象编程技巧

    • 封装、继承和多态是面向对象设计的关键概念。
    • 推荐将代码组织成小的可复用模块或方法,以提高代码的可维护性和复用性,并建议单个方法的长度不超过100行。
    • 使用"点"操作符来查看API文档,这有助于理解每个方法的具体功能。
  2. Dart编程技巧

    • 安全调用(Null Safety) :使用?.操作符处理可能为空的对象,避免空指针异常。
    • 默认值设置 :利用??运算符为可能为空的变量指定一个默认值。
    • 简化判断逻辑:采用集合或数组的方式优化多个条件的判断流程,使得代码更加简洁高效。

Dart高效编程技巧

相关推荐
GIS之路5 分钟前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug9 分钟前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu1213811 分钟前
React面向组件编程
开发语言·前端·javascript
持续升级打怪中32 分钟前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路36 分钟前
GDAL 实现矢量合并
前端
hxjhnct38 分钟前
React useContext的缺陷
前端·react.js·前端框架
前端 贾公子1 小时前
从入门到实践:前端 Monorepo 工程化实战(4)
前端
菩提小狗1 小时前
Sqlmap双击运行脚本,双击直接打开。
前端·笔记·安全·web安全
前端工作日常1 小时前
我学习到的AG-UI的概念
前端
韩师傅1 小时前
前端开发消亡史:AI也无法掩盖没有设计创造力的真相
前端·人工智能·后端