Flutter---回调函数

1.带参数的回调示例

①先定义一个餐馆类

Dart 复制代码
class Restaurant {
  // 点餐方法,需要传入一个字符串和一个回调函数,onDelivered是形参,形参名称可以叫任何名字
  //形参名(onDelivered)是给方法内部用的,实参名(message)是给调用者用的,它们不需要相同,只要类型匹配就行
  void order(String food, Function(String) onDelivered) {
    print("🍔 您点了:$food");
    print("👨‍🍳 厨房正在制作...");

    // 模拟制作时间(2秒)
    Future.delayed(Duration(seconds: 2), () {
      print("✅ 外卖已做好!");

      // 调用回调函数,通知顾客
      onDelivered("您的$food已送达,请用餐!");
    });
  }
}

②创建一个餐馆类的实例

Dart 复制代码
var restaurant = Restaurant();

③点餐

方式1:匿名回调-直接写在参数里

Dart 复制代码
    // 点外卖,并留下"电话号码"(回调函数)
    restaurant.order("汉堡",(message) {

        // message是实参,实参名称也可以叫任何名字
      print("📞 收到通知:$message");
      print("🍽️ 开吃啦!");
    });

    print("🎉 点餐完成,等待外卖...");
  }



// 执行流程:
// 1. 实参 (message) {...} 被传递给形参 onDelivered
// 2. 方法内部调用 onDelivered("消息")
// 3. 实际执行的是 (message) {...} 里面的代码
// 4. "消息" 被传递给了 message 参数

方式2:命名函数-先定义,再传入(更清晰的写法)

Dart 复制代码
// 先定义一个回调函数
void myCallback(String message) {
  print("📞 收到通知:$message");
  print("🍽️ 开吃啦!");
}

// 调用时,把函数名传进去
restaurant.order("汉堡", myCallback);
//                            ↑
//                    传的是函数名,不是调用

方式3:变量传递-用变量存储回调

Dart 复制代码
// 把回调函数存在变量里
Function(String) myCallback = (message) {
  print("📞 收到通知:$message");
  print("🍽️ 开吃啦!");
};

// 把变量传进去
restaurant.order("汉堡", myCallback);

运行结果

详细的流程图

Dart 复制代码
时间线 →
─────────────────────────────────────────────────────────────

[0秒] 创建餐厅对象
      │
      ▼
      调用 order("汉堡", 回调函数)
      │
      ▼
      进入 order 方法
      │
      ▼
      打印 "🍔 您点了:汉堡"
      │
      ▼
      打印 "👨‍🍳 厨房正在制作..."
      │
      ▼
      注册定时器(2秒后执行)
      │
      ▼
      order 方法结束
      │
      ▼
      打印 "🎉 点餐完成,等待外卖..."
      │
      ▼
       main 方法结束
      
      【此时同步代码全部执行完毕,控制台已有3行输出】

═══════════════════════════════════════════════════════════════

[2秒后] 定时器触发
      │
      ▼
      打印 "✅ 外卖已做好!"
      │
      ▼
      onDelivered("您的汉堡已送达")
      │      ↓
      │   执行之前传入的回调函数
      │      ↓
      │   行16: 进入回调函数,message = "您的汉堡已送达"
      │      ↓
      │   行17: 打印 "📞 收到通知:您的汉堡已送达"
      │      ↓
      │   行18: 打印 "🍽️ 开吃啦!"
      │      ↓
      │   行19: 回调结束
      │
      ▼
      定时器回调结束

逻辑图

参数传递过程

2.定义函数类型的回调

①定义餐馆类

Dart 复制代码
// 1. 定义函数类型
typedef OnOrderDelivered = void Function(String message);
typedef OnOrderFailed = void Function(String error, int code);

// 2. 使用定义好的类型
class Restaurant {

  void order(
      String food,
      OnOrderDelivered onSuccess,  // 使用 typedef
      OnOrderFailed onFailed,       // 使用 typedef
      ) {

    print("🍔 您点了:$food");

    // 模拟订单处理
    bool hasFood = true;

    if (hasFood) {
      Future.delayed(Duration(seconds: 2), () {
        onSuccess("您的$food已送达!");  // 调用成功回调
      });
    } else {
      onFailed("食材不足", 404);  // 调用失败回调
    }
  }
}

②创建一个餐馆类的实例,并且点餐

Dart 复制代码
    var restaurant = Restaurant();

    // 使用定义好的回调类型
    restaurant.order(
      "汉堡",
          (message) {
        print("✅ $message");
        print("🍽️ 开吃啦!");
      },
          (error, code) {
        print("❌ 错误 [$code]: $error");
        print("😭 点餐失败");
      },
    );

运行结果

Dart 复制代码
I/flutter (29269): 🍔 您点了:汉堡
I/flutter (29269): ✅ 您的汉堡已送达!
I/flutter (29269): 🍽️ 开吃啦!
3.可选回调的示例

①定义登录类

Dart 复制代码
class AuthService {
  void login({
    required String username,
    required String password,
    Function()? onStart,           // 开始登录时回调
    Function(Map<String, dynamic> user)? onSuccess,  // 成功时回调
    Function(String error)? onFailed,                // 失败时回调
    Function()? onComplete,        // 无论成功失败都回调
  }) {
    // 调用开始回调(如果有)
    onStart?.call();
    print("🔐 正在登录:$username");

    // 模拟网络请求
    Future.delayed(Duration(seconds: 2), () {
      if (username == "admin" && password == "123456") {
        // 登录成功,创建user对象
        Map<String, dynamic> user = {
          "id": 1,
          "name": "管理员",
          "email": "admin@example.com",
          "token": "abc123xyz",
        };

        // 调用成功回调(如果有)
        onSuccess?.call(user); //把user传回去
      } else {
        // 登录失败
        // 调用失败回调(如果有)
        onFailed?.call("用户名或密码错误");//把字符串传回去
      }

      // 无论如何都调用完成回调(如果有)
      onComplete?.call();
    });
  }
}

②调用这个函数

Dart 复制代码
 var auth = AuthService();

    print("===== 场景1:完整处理所有情况 =====");
    auth.login(
      username: "admin",
      password: "123456",
      onStart: () {
        print("⏳ 开始登录流程...");
      },
      onSuccess: (user) { //拿到user
        print("✅ 登录成功!");
        print("   欢迎:${user['name']}");
        print("   Token:${user['token']}");
      },
      onFailed: (error) { //拿到字符串
        print("❌ 登录失败:$error");
      },
      onComplete: () {
        print("🏁 登录流程结束");
      },
    );

    Future.delayed(Duration(seconds: 3), () {
      print("\n===== 场景2:只关心成功 =====");
      auth.login(
        username: "admin",
        password: "123456",
        onSuccess: (user) {
          print("🎉 欢迎回来,${user['name']}!");
        },
        // 不关心开始、失败、完成
      );
    });

    Future.delayed(Duration(seconds: 6), () {
      print("\n===== 场景3:只处理失败 =====");
      auth.login(
        username: "wrong",
        password: "wrong",
        onFailed: (error) {
          print("⚠️ 登录失败:$error");
          print("请检查用户名和密码");
        },
        // 只关心失败情况
      );
    });

运行结果

Dart 复制代码
I/flutter ( 6011): ===== 场景1:完整处理所有情况 =====
I/flutter ( 6011): ⏳ 开始登录流程...
I/flutter ( 6011): 🔐 正在登录:admin
I/flutter ( 6011): ✅ 登录成功!
I/flutter ( 6011):    欢迎:管理员
I/flutter ( 6011):    Token:abc123xyz
I/flutter ( 6011): 🏁 登录流程结束
I/flutter ( 6011): ===== 场景2:只关心成功 =====
I/flutter ( 6011): 🔐 正在登录:admin
I/flutter ( 6011): 🎉 欢迎回来,管理员!
I/flutter ( 6011): ===== 场景3:只处理失败 =====
I/flutter ( 6011): 🔐 正在登录:wrong
I/flutter ( 6011): ! 登录失败:用户名或密码错误
I/flutter ( 6011): 请检查用户名和密码

注意事项

4.UI组件回调

数据向下,事件向上:父组件通过参数传数据给子组件,子组件通过回调传事件给父组件

①自定义按钮组件

Dart 复制代码
class MyButton extends StatelessWidget {
  final String text;
  final Color color;
  final VoidCallback? onPressed;  // 回调:无参数

  const MyButton({
    Key? key, //通过 key 定位 Widget,key是可选参数,静态布局不需要key
    required this.text,
    this.color = Colors.blue,
    this.onPressed,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(

      style: ElevatedButton.styleFrom(
        backgroundColor: color, //颜色
        padding: EdgeInsets.symmetric(horizontal: 30, vertical: 15),
      ),

      onPressed: onPressed,  // 把回调传给 Flutter 的按钮

      child: Text(
        text, //文字
        style: TextStyle(fontSize: 18),
      ),

    );
  }
}

②调用这个按钮类

Dart 复制代码
return Scaffold(
      appBar: AppBar(title: Text("自定义按钮")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // 按钮1:带回调
            MyButton(
              text: "点击我",
              color: Colors.green,
              onPressed: () {
                print("绿色按钮被点击了");
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(content: Text("你点击了绿色按钮!")),
                );
              },
            ),

            SizedBox(height: 20),

            // 按钮2:不带回调(禁用状态)
            MyButton(
              text: "禁用按钮",
              color: Colors.grey,
              onPressed: null,  // null 表示禁用
            ),
          ],
        ),
      ),
    );
5.数组传递回调

①构建多选列表组件类

Dart 复制代码
/// 多选列表组件
class MultiSelectList extends StatefulWidget {
  final List<String> items;                    // 所有选项
  final Function(List<String>)? onSelectionChanged;  // 回调:返回选中的数组

  const MultiSelectList({
    Key? key,
    required this.items,
    this.onSelectionChanged,
  }) : super(key: key);

  @override
  _MultiSelectListState createState() => _MultiSelectListState();
}

class _MultiSelectListState extends State<MultiSelectList> {

  List<String> _selectedItems = []; //存储用户选中的子项


  //切换选中状态
  void _toggleSelection(String item) {
    setState(() {
      if (_selectedItems.contains(item)) {
        _selectedItems.remove(item);
      } else {
        _selectedItems.add(item);
      }

      // ***回调:传递选中的数组
      widget.onSelectionChanged?.call(_selectedItems); //.call()是调用函数对象的标准方法
    });
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: widget.items.length, //列表项数量
      itemBuilder: (context, index) { //构建每一项

        String item = widget.items[index]; //获取当前项
        bool isSelected = _selectedItems.contains(item);//判断是否选中

        return ListTile(

          // 用户点击了"香蕉"旁边的复选框
          // Flutter 自动调用这个复选框的 onChanged 回调
          leading: Checkbox(
            value: isSelected, //当前是否选中
            onChanged: (_) => _toggleSelection(item), //状态改变时的回调
          ),

          title: Text(item),

          trailing: isSelected ? Icon(Icons.check, color: Colors.green) : null,
        );
      },
    );
  }
}

②调用这个类

Dart 复制代码
class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<StatefulWidget> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {

  List<String> _allItems = [
    '苹果', '香蕉', '橙子', '葡萄', '西瓜', '芒果'
  ];

  List<String> _selectedItems = [];



  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('多选列表'),
        actions: [
          if (_selectedItems.isNotEmpty)
            TextButton(
              onPressed: () {
                _showSelectedDialog();
              },
              child: Text(
                '确定 (${_selectedItems.length})',
                style: TextStyle(color: Colors.blue),
              ),
            ),
        ],
      ),
      body: Column(
        children: [
          // 显示已选中的数量
          Container(
            padding: EdgeInsets.all(16),
            color: Colors.blue[50],
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Text(
                  '已选中 ${_selectedItems.length} 项',
                  style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
                ),
                if (_selectedItems.isNotEmpty)
                  TextButton(
                    onPressed: () {
                      setState(() {
                        _selectedItems.clear();
                      });
                    },
                    child: Text('清空'),
                  ),
              ],
            ),
          ),

          // 多选列表
          Expanded(
            child: MultiSelectList(  //引用外部类
              items: _allItems, //传入原始数据
              onSelectionChanged: (selected) { //传入回调函数
                setState(() {
                  _selectedItems = selected; //把传回来的选中数组,更新UI的状态
                });
                print('选中的水果: $selected');
              },
            ),
          ),
        ],
      ),
    );
  }

  //展示选中了的列表项
  void _showSelectedDialog() {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: Text('选中的水果'),
        content: Container(
          width: double.maxFinite,
          child: ListView.builder( //构建列表
            shrinkWrap: true,
            itemCount: _selectedItems.length, //列表长度
            itemBuilder: (context, index) { //列表子项
              return ListTile(
                leading: Icon(Icons.check_circle, color: Colors.green),
                title: Text(_selectedItems[index]),
              );
            },
          ),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: Text('关闭'),
          ),
        ],
      ),
    );
  }
}

具体逻辑

Dart 复制代码
HomePage 调用 MultiSelectList,传入原始数据

MultiSelectList 内部处理用户的选择

用户每次点击复选框,MultiSelectList 通过回调把选中的列表项传回给 HomePage

6.Future与回调

7.stream与回调

相关推荐
大尚来也2 小时前
深入HashMap底层:从JDK1.7到1.8的架构演进与性能突围
开发语言
卷帘依旧2 小时前
JavaScript 闭包经典问题:为什么输出 10 次 i=10
javascript
圣光SG2 小时前
Vue.js 从入门到精通:技术成长之路
flutter
柳杉3 小时前
Three.js × Blender:从建模到 Web 3D 的完整工作流深度解析
前端·javascript·数据可视化
森林里的程序猿猿3 小时前
并发设计模式
java·开发语言·jvm
222you3 小时前
四个主要的函数式接口
java·开发语言
用户806138166594 小时前
发布为一个 npm 包
前端·javascript
smchaopiao4 小时前
Python中字典与列表合并的问题与解决方法
开发语言·python