Menu
CoddyTech

抛出错误

本课属于 Coddy Flutter 学习旅程的“数据与网络”单元。第 11 课,共 27 课。

错误不只会出现在服务器上。你自己的函数也可以拒绝继续执行。当函数收到无法处理的内容时,它会抛出异常:

Future<void> saveName(String name) async {
  if (name.isEmpty) {
    throw Exception('The name cannot be empty');
  }
  // 保存姓名
}

throw 会立即停止函数,就像 return 一样,但它返回的是错误,而不是值。Exception('...') 就是错误,其中的消息会说明出了什么问题。

调用该函数的代码会用 try 和 catch 捕获这个异常,就像捕获来自 fetch 的错误一样。转换成文本后,Exception 会以 Exception: 开头,后面跟着你的消息。

真正的 fetch 也是这样工作的。服务器返回的每个响应都会附带一个状态码:200 表示正常,404 表示未找到。当状态码不是 200 时,你的代码会抛出异常,这样屏幕就不会把错误的响应当作数据展示出来:

final response = await http.get(Uri.parse(url));
if (response.statusCode != 200) {
  throw Exception('The server answered ${response.statusCode}');
}
return response.body;
challenge icon

挑战

简单

在输入框为空时点击 Save:它会显示 Saved: ,后面没有名字。让 saveName 拒绝空名字:等待结束后,如果 name 为空,就抛出 Exception('The name cannot be empty')。save 函数已经捕获了这个异常,并显示相应消息。

亲手试试

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: NameScreen(),
    );
  }
}

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

  @override
  State<NameScreen> createState() => _NameScreenState();
}

class _NameScreenState extends State<NameScreen> {
  final controller = TextEditingController();
  String status = '';

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  Future<void> saveName(String name) async {
    await Future.delayed(const Duration(seconds: 1));
  }

  Future<void> save() async {
    try {
      await saveName(controller.text);
      setState(() {
        status = 'Saved: ${controller.text}';
      });
    } catch (e) {
      setState(() {
        status = '$e';
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.all(24),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextField(
              controller: controller,
              decoration: const InputDecoration(labelText: 'Name'),
            ),
            const SizedBox(height: 16),
            ElevatedButton(onPressed: save, child: const Text('Save')),
            const SizedBox(height: 16),
            Text(status, style: const TextStyle(fontSize: 18)),
          ],
        ),
      ),
    );
  }
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

数据与网络中的所有课

自己练习: Flutter 在线编译器