1.5 函数

基础知识

函数是组织好的、可重复使用的代码块,用于执行单一的、相关的操作。在 Dart 中,函数是第一类对象,这意味着函数可以像变量一样被传递、赋值和作为参数使用。Dart 函数的定义非常灵活,支持可选参数、命名参数、默认参数值以及匿名函数等特性。

1. 函数定义

Dart 函数的定义语法如下:

dart
复制代码
返回类型 函数名(参数列表) {
  // 函数体
  return 返回值; // 如果函数有返回值
}

如果函数只包含一个表达式,可以使用箭头语法(=>)进行简写:

dart
复制代码
返回类型 函数名(参数列表) => 表达式;

示例:

dart
复制代码
// 简单函数
void greet(String name) {
  print("Hello, $name!");
}

// 带返回值的函数
int add(int a, int b) {
  return a + b;
}

// 箭头函数
int multiply(int a, int b) => a * b;

void main() {
  greet("Alice"); // 输出: Hello, Alice!
  print("10 + 20 = ${add(10, 20)}"); // 输出: 10 + 20 = 30
  print("5 * 6 = ${multiply(5, 6)}"); // 输出: 5 * 6 = 30
}

2. 可选参数

Dart 支持两种可选参数:

  • 可选位置参数:用方括号 [] 包裹,放在参数列表的最后。调用时可以省略。
  • 可选命名参数:用花括号 {} 包裹,放在参数列表的最后。调用时需要使用参数名指定。

示例:

dart
复制代码
// 可选位置参数
void sayHello(String name, [String? greeting]) {
  if (greeting != null) {
    print("$greeting, $name!");
  } else {
    print("Hello, $name!");
  }
}

// 可选命名参数
void printUserInfo({String? name, int? age}) {
  print("Name: ${name ?? 'Unknown'}");
  print("Age: ${age ?? 'Unknown'}");
}

// 可选命名参数带默认值
void printProductInfo({String productName = 'Default Product', double price = 0.0}) {
  print("Product: $productName, Price: $price");
}

void main() {
  sayHello("Bob"); // 输出: Hello, Bob!
  sayHello("Charlie", "Hi"); // 输出: Hi, Charlie!

  printUserInfo(name: "David", age: 25); // 输出: Name: David, Age: 25
  printUserInfo(age: 30); // 输出: Name: Unknown, Age: 30
  printUserInfo(); // 输出: Name: Unknown, Age: Unknown

  printProductInfo(); // 输出: Product: Default Product, Price: 0.0
  printProductInfo(productName: "Laptop", price: 1200.0); // 输出: Product: Laptop, Price: 1200.0
}

3. 匿名函数

匿名函数是没有名称的函数,通常用于作为参数传递给其他函数,或者在需要一个函数对象但不需要为其命名时使用。匿名函数也称为 Lambda 表达式或闭包。

dart
复制代码
void main() {
  // 匿名函数作为变量
  var multiply = (int a, int b) {
    return a * b;
  };
  print(multiply(4, 5)); // 输出: 20

  // 匿名函数作为参数传递给 List 的 forEach 方法
  List<String> fruits = ["apple", "banana", "cherry"];
  fruits.forEach((fruit) {
    print("I like $fruit");
  });
  // 输出:
  // I like apple
  // I like banana
  // I like cherry
}

官方文档链接

Flutter 开发中的应用案例

函数在 Flutter 开发中无处不在,从 Widget 的 build 方法到各种回调函数,再到业务逻辑的封装,都离不开函数。理解 Dart 函数的灵活性对于编写清晰、可维护的 Flutter 代码至关重要。

案例:自定义按钮组件与回调函数

我们将创建一个自定义的按钮组件,并使用函数作为回调参数,演示如何通过函数传递行为,以及可选参数和命名参数在 Flutter 中的应用。

dart
复制代码
import 'package:flutter/material.dart';

// 自定义按钮组件
class CustomButton extends StatelessWidget {
  final String text;
  final VoidCallback onPressed; // 定义一个无参数无返回值的函数类型
  final Color? color; // 可选命名参数,颜色可空
  final double? fontSize; // 可选命名参数,字体大小可空

  const CustomButton({
    super.key,
    required this.text,
    required this.onPressed,
    this.color, // 可选参数
    this.fontSize, // 可选参数
  });

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      style: ElevatedButton.styleFrom(
        backgroundColor: color ?? Colors.blue, // 使用空值合并运算符提供默认颜色
        padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
      ),
      onPressed: onPressed, // 将传入的函数作为回调
      child: Text(
        text,
        style: TextStyle(
          fontSize: fontSize ?? 18.0, // 使用空值合并运算符提供默认字体大小
          color: Colors.white,
        ),
      ),
    );
  }
}

class FunctionExampleScreen extends StatefulWidget {
  const FunctionExampleScreen({super.key});

  @override
  State<FunctionExampleScreen> createState() => _FunctionExampleScreenState();
}

class _FunctionExampleScreenState extends State<FunctionExampleScreen> {
  String _message = '点击按钮';

  // 定义一个普通函数作为回调
  void _handleButtonClick() {
    setState(() {
      _message = '按钮被点击了!';
    });
    _showSnackBar('普通按钮被点击');
  }

  // 定义一个带参数的函数,用于传递给匿名函数
  void _showSnackBar(String msg) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(msg)),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('函数应用案例'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              _message,
              style: const TextStyle(fontSize: 24),
            ),
            const SizedBox(height: 20),
            CustomButton(
              text: '普通按钮',
              onPressed: _handleButtonClick, // 传递普通函数
            ),
            const SizedBox(height: 20),
            CustomButton(
              text: '匿名函数按钮',
              color: Colors.green,
              onPressed: () { // 传递匿名函数
                setState(() {
                  _message = '匿名函数按钮被点击了!';
                });
                _showSnackBar('匿名函数按钮被点击');
              },
            ),
            const SizedBox(height: 20),
            CustomButton(
              text: '带参数按钮',
              color: Colors.orange,
              fontSize: 20.0,
              onPressed: () => _showSnackBar('带参数按钮被点击'), // 传递箭头函数
            ),
          ],
        ),
      ),
    );
  }
}

void main() {
  runApp(const MaterialApp(home: FunctionExampleScreen()));
}

案例分析:

  • VoidCallback onPressed;:在 CustomButton 中,onPressed 被定义为一个 VoidCallback 类型。VoidCallback 是 Dart 中 typedef void VoidCallback(); 的别名,表示一个没有参数也没有返回值的函数。这展示了如何定义函数类型作为参数,以便在组件之间传递行为。
  • this.color, this.fontSize,CustomButton 的构造函数使用了可选命名参数 colorfontSize。这使得 CustomButton 在使用时更加灵活,可以根据需要选择性地提供这些参数。
  • backgroundColor: color ?? Colors.blue,:这里使用了 Dart 的空值合并运算符 ??。如果 color 参数为 null,则使用 Colors.blue 作为默认背景色。这在处理可选参数时非常有用,可以提供默认值。
  • onPressed: _handleButtonClick,:在 FunctionExampleScreen 中,我们将 _handleButtonClick 这个普通函数直接作为 CustomButtononPressed 参数传递。这演示了函数作为第一类对象的特性。
  • onPressed: () { ... },:第二个 CustomButtononPressed 参数是一个匿名函数。当按钮被点击时,这个匿名函数会被执行,从而更新 _message 并显示 SnackBar。匿名函数在 Flutter 中常用于处理事件回调。
  • onPressed: () => _showSnackBar('带参数按钮被点击'),:第三个 CustomButtononPressed 参数是一个箭头函数,它调用了 _showSnackBar 函数并传递了一个字符串参数。这展示了箭头函数在简洁地执行单行表达式时的优势。

这个案例全面展示了 Dart 函数在 Flutter 开发中的应用,包括函数作为参数传递、可选参数、命名参数以及匿名函数和箭头函数的使用。掌握这些概念对于构建交互式和可复用的 Flutter 组件至关重要。