Dart 语言速成:从函数闭包到面向对象,30 分钟补齐 Flutter 开发的语言短板(一)
·

函数就像生活中的各种工具,每个工具都有特定的功能。
2.1 基础函数
// 就像一个打招呼的机器
void sayHello() {
print('你好!');
}
// 带参数的函数,就像可以定制的打招呼机器
void greetPerson(String name) {
print('你好,$name!');
}
// 有返回值的函数,就像计算器
int addNumbers(int a, int b) {
return a + b;
}
// 使用函数
sayHello(); // 输出:你好!
greetPerson('小明'); // 输出:你好,小明!
int sum = addNumbers(5, 3); // sum = 8
2.2 高级函数特性和技巧
🎛️ 参数类型详解
🔑 关键区别:[] vs {}
在Dart中,函数参数有不同的传递方式,就像生活中不同的点餐方式:
| 参数类型 | 符号 | 特点 | 生活类比 |
|---|---|---|---|
| 位置参数 | 无符号 | 必须按顺序传递 | 排队点餐,必须按顺序说 |
| 可选位置参数 | [] |
按顺序传递,可以省略 | 套餐选择,按顺序可选 |
| 命名参数 | {} |
指定名称传递,顺序无关 | 自助点餐,指定要什么 |
📋 详细对比和生活场景
// 1. 位置参数 - 就像在快餐店排队点餐
void fastFoodOrder(String mainDish, String size) {
print('点餐:$size的$mainDish');
}
// 调用:必须按顺序,不能颠倒
fastFoodOrder('汉堡', '大份'); // ✅ 正确
// fastFoodOrder('大份', '汉堡'); // ❌ 错误,顺序不对
// 2. 可选位置参数 [] - 就像套餐升级选择
void comboOrder(String mainDish, [String? drink, String? dessert]) {
print('主食:$mainDish');
if (drink != null) print('饮料:$drink');
if (dessert != null) print('甜品:$dessert');
}
// 调用方式:按顺序,但可以省略后面的
comboOrder('汉堡'); // 只要主食
comboOrder('汉堡', '可乐'); // 主食 + 饮料
comboOrder('汉堡', '可乐', '冰淇淋'); // 全套
// comboOrder('汉堡', null, '冰淇淋'); // 要甜品但不要饮料,需要传null
// 3. 命名参数 {} - 就像自助餐厅点餐
void buffetOrder(String mainDish, {
String? drink,
String? dessert,
bool extraSpicy = false,
int quantity = 1,
}) {
print('点餐:${quantity}份$mainDish');
if (extraSpicy) print('加辣');
if (drink != null) print('饮料:$drink');
if (dessert != null) print('甜品:$dessert');
}
// 调用方式:可以任意顺序,指定参数名
buffetOrder('汉堡'); // 基础版
buffetOrder('汉堡', drink: '可乐'); // 指定饮料
buffetOrder('汉堡', dessert: '蛋糕', drink: '果汁'); // 顺序无关
buffetOrder('汉堡', quantity: 2, extraSpicy: true); // 指定数量和辣度
// 4. 必需命名参数 - 就像预约餐厅必须说桌号
void restaurantReservation(String customerName, {
required String phoneNumber, // 必须提供电话
required int tableNumber, // 必须提供桌号
String? specialRequest, // 可选的特殊要求
}) {
print('预约:$customerName,电话:$phoneNumber,桌号:$tableNumber');
if (specialRequest != null) print('特殊要求:$specialRequest');
}
// 调用:必须提供required参数
restaurantReservation(
'张三',
phoneNumber: '13800138000',
tableNumber: 5
);
🎯 实际应用场景对比
// 场景1:数学计算 - 位置参数最合适
double calculateArea(double width, double height) {
return width * height;
}
// 使用:calculateArea(10.0, 20.0) - 顺序清晰
// 场景2:字符串处理 - 可选位置参数
String formatText(String text, [bool upperCase = false, bool addPrefix = false]) {
String result = text;
if (upperCase) result = result.toUpperCase();
if (addPrefix) result = 'PREFIX: $result';
return result;
}
// 使用:formatText('hello', true) - 按需要传递参数
// 场景3:UI组件配置 - 命名参数最灵活
Widget createButton({
required String text,
Color? backgroundColor,
double? fontSize,
bool isEnabled = true,
VoidCallback? onPressed,
}) {
// 创建按钮的逻辑
return Container(); // 此处简化示例
}
// 使用:createButton(text: '点击', backgroundColor: Colors.blue, isEnabled: false)
// 场景4:网络请求 - 混合使用
Future<String> httpRequest(
String url, // 必需位置参数
[String method = 'GET'], // 可选位置参数
{
Map<String, String>? headers, // 可选命名参数
String? body,
required int timeout, // 必需命名参数
}
) async {
// 网络请求逻辑
return 'Response';
}
// 使用:httpRequest('https://api.com', 'POST', headers: {...}, timeout: 5000)
💡 选择建议和最佳实践
// ✅ 好的实践
class GoodExample {
// 1. 少量必需参数用位置参数
void move(double x, double y) { }
// 2. 多个可选配置用命名参数
void animate({
Duration duration = const Duration(seconds: 1),
Curve curve = Curves.linear,
bool repeat = false,
}) { }
// 3. 有默认值的用命名参数
void log(String message, {
String level = 'INFO',
bool timestamp = true,
}) { }
// 4. 必须指定的配置用required命名参数
void connect({
required String host,
required int port,
String? username,
String? password,
}) { }
}
// ❌ 不好的实践
class BadExample {
// 太多位置参数,容易搞混
void badFunction(String a, String b, String c, String d, String e) { }
// 可选位置参数太多,调用时需要传很多null
void badOptional(String required, [String? a, String? b, String? c]) { }
// 所有参数都是命名的,简单操作变复杂
void badNamed({required double x, required double y}) { }
}
🎪 生动记忆法
// 记忆口诀:
// [] = 方括号 = 方便省略 = 可选位置参数
// {} = 花括号 = 花样选择 = 命名参数
// 生活类比总结:
void lifeAnalogy() {
// 位置参数 = 排队买票:必须按顺序说"我要1张成人票"
buyTicket('成人票', 1);
// 可选位置参数 [] = 套餐选择:可以说"汉堡,可乐"或只说"汉堡"
orderCombo('汉堡', '可乐');
orderCombo('汉堡'); // 省略饮料
// 命名参数 {} = 自助点餐:可以说"我要汉堡,不要辣,要大杯可乐"
selfService('汉堡', spicy: false, drinkSize: '大杯');
// 必需命名参数 = 酒店登记:必须说"姓名张三,身份证123456"
hotelCheckIn(name: '张三', idCard: '123456');
}
// 实际函数定义
void buyTicket(String type, int quantity) { }
void orderCombo(String main, [String? drink]) { }
void selfService(String main, {bool spicy = false, String? drinkSize}) { }
void hotelCheckIn({required String name, required String idCard}) { }
🔧 实用技巧
// 技巧1:参数太多时的处理
class ConfigExample {
// ❌ 参数过多,难以使用
void badConfig(String host, int port, String user, String pass,
bool ssl, int timeout, bool retry, String encoding) { }
// ✅ 使用配置类
void goodConfig(ConnectionConfig config) { }
// ✅ 或者使用命名参数分组
void betterConfig({
required String host,
required int port,
String? username,
String? password,
bool useSSL = false,
int timeoutSeconds = 30,
bool autoRetry = true,
String encoding = 'utf-8',
}) { }
}
// 技巧2:参数验证
void validateParams(String email, {required String password}) {
if (email.isEmpty) throw ArgumentError('邮箱不能为空');
if (password.length < 6) throw ArgumentError('密码至少6位');
// 处理逻辑...
}
// 技巧3:参数转换
void flexibleInput(dynamic value, {bool strict = false}) {
String stringValue;
if (value is String) {
stringValue = value;
} else if (value is num) {
stringValue = value.toString();
} else if (strict) {
throw ArgumentError('严格模式下只接受String或num');
} else {
stringValue = value.toString();
}
// 使用stringValue...
}
如果文章对您有帮助,麻烦动动发财的小手点赞、关注和收藏,将是作者不断更新的动力🙏🏻
更多推荐



所有评论(0)