Menu
CoddyTech

使用 finally 清理

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

无论调用成功还是失败,有些操作都必须执行:隐藏加载指示器、重新启用按钮。如果在 try 和 catch 中都写一遍,很容易漏掉其中一处。Dart 还有第三个代码块来处理这种情况:finally:

try {
  await saveName(name);
} catch (e) {
  // 显示错误
} finally {
  // 总会运行:在 try 或 catch 之后
}

finally 总是最后运行:操作成功时在 try 之后运行,操作失败时在 catch 之后运行。因此,你可以在这里撤销调用前所做的设置。

这里,应用在保存时会将保存按钮置灰,并使用 onPressed: saving ? null : save。onPressed 为 null 的按钮无法按下。如果 saving 没有恢复为 false,按钮就会一直置灰。

challenge icon

挑战

中等

留空后保存:会显示错误,而且 保存 按钮会保持灰色,这样你就无法再试一次。把 saving = false 移出 try 代码块,放入 finally 代码块中,并放在单独的 setState 中。

亲手试试

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 = '';
  bool saving = false;

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

  Future<void> saveName(String name) async {
    await Future.delayed(const Duration(seconds: 1));
    if (name.isEmpty) {
      throw Exception('The name cannot be empty');
    }
  }

  Future<void> save() async {
    setState(() {
      saving = true;
    });
    try {
      await saveName(controller.text);
      setState(() {
        status = 'Saved: ${controller.text}';
        saving = false;
      });
    } 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: saving ? null : save, child: const Text('Save')),
            const SizedBox(height: 16),
            Text(status, style: const TextStyle(fontSize: 18)),
          ],
        ),
      ),
    );
  }
}
quiz icon自我测试

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

数据与网络中的所有课

自己练习: Flutter 在线编译器