在这里插入图片描述

函数就像生活中的各种工具,每个工具都有特定的功能。

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...
}

如果文章对您有帮助,麻烦动动发财的小手点赞、关注和收藏,将是作者不断更新的动力🙏🏻

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐