flutter异常处置

在 Flutter 中,合理使用 try-catch 和错误处理机制能有效定位问题并防止应用崩溃。以下是结合同步/异步代码、错误类型分类和全局捕获的完整实践方案:


一、同步代码的 try-catch

1. 基础语法
try {
  // 可能抛出异常的同步代码
  final result = int.parse('invalid_number');
} on FormatException catch (e) {
  // 捕获特定异常类型
  print('格式错误: $e');
} catch (e, s) {
  // 捕获所有其他异常,s 是堆栈跟踪
  print('未知错误: $e\n堆栈跟踪: $s');
} finally {
  // 无论是否异常都会执行
  print('清理资源');
}
2. 典型场景
  • 数据转换:解析 JSON、字符串转数字。
  • 文件操作:读取本地文件。
  • 条件检查:断言失败(assert)。

二、异步代码的 try-catch

1. async/await + try-catch
Future<void> fetchData() async {
  try {
    final response = await http.get(Uri.parse('https://api.example.com/data'));
    if (response.statusCode != 200) {
      throw Exception('请求失败: ${response.statusCode}');
    }
    final data = jsonDecode(response.body);
  } on SocketException {
    // 处理网络错误
    print('网络连接失败');
  } on TimeoutException {
    // 处理超时
    print('请求超时');
  } catch (e) {
    // 其他错误
    print('未知错误: $e');
  }
}
2. Future.catchError

适用于链式调用:

http.get(url)
  .then((response) => handleResponse(response))
  .catchError((e) => print('请求失败: $e'));

三、错误定位与日志记录

1. 记录堆栈跟踪(Stack Trace)
try {
  // ...
} catch (e, s) {
  // s 是堆栈跟踪对象
  log('错误: $e', stackTrace: s);
  // 上报到 Sentry/Firebase Crashlytics
  Sentry.captureException(e, stackTrace: s);
}
2. 使用 logger 库格式化日志
dependencies:
  logger: ^2.0.0
final logger = Logger();

try {
  // ...
} catch (e, s) {
  logger.e('错误发生', error: e, stackTrace: s);
}

四、全局错误捕获

1. Flutter 框架层错误
void main() {
  // 捕获 Widget 构建错误
  FlutterError.onError = (FlutterErrorDetails details) {
    logger.e('Flutter 错误', error: details.exception, stackTrace: details.stack);
    // 上报到监控平台
    FirebaseCrashlytics.instance.recordFlutterError(details);
  };

  // 捕获所有未处理的异常(包括异步)
  PlatformDispatcher.instance.onError = (error, stack) {
    logger.e('全局未捕获错误', error: error, stackTrace: stack);
    return true; // 阻止默认错误处理
  };

  runApp(MyApp());
}
2. Dart 层全局捕获(仅调试环境)
void main() {
  // 仅开发环境启用
  if (kDebugMode) {
    ErrorWidget.builder = (FlutterErrorDetails details) {
      return Scaffold(
        body: ErrorDetailsView(details), // 自定义错误页面
      );
    };
  }
  runApp(MyApp());
}

五、UI 层友好提示

1. 使用 FutureBuilder 展示错误
FutureBuilder<String>(
  future: fetchData(),
  builder: (context, snapshot) {
    if (snapshot.hasError) {
      return ErrorWidget(snapshot.error!); // 自定义错误组件
    } else if (snapshot.connectionState == ConnectionState.done) {
      return DataView(snapshot.data!);
    }
    return CircularProgressIndicator();
  },
);
2. 弹窗提示
try {
  await fetchData();
} catch (e) {
  if (!context.mounted) return; // 防止页面已销毁
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: Text('操作失败'),
      content: Text('错误原因: ${e.toString()}'),
    ),
  );
}

六、最佳实践总结

场景处理方式工具/组件
同步代码错误try-catch + 类型匹配on FormatException
异步请求错误async/await + try-catchFuture.catchError
全局未捕获异常FlutterError.onErrorFirebase Crashlytics/Sentry
用户界面反馈FutureBuilder + 弹窗showDialog/SnackBar
错误日志记录Logger 库 + 堆栈跟踪logger/sentry
开发调试辅助自定义错误页面ErrorWidget.builder

七、常见问题与解决

  1. try-catch 无法捕获异步错误

    • 确保异步操作使用 await,否则异常会进入 Future 的未捕获回调。
  2. 页面销毁后调用 setState

    • catch 块中检查 if (mounted)
      try {
        await fetchData();
      } catch (e) {
        if (!mounted) return;
        setState(() => _error = e);
      }
      
  3. 错误信息不明确

    • 使用 toString() 或自定义异常类(如 ApiException)提供清晰错误描述:
      class ApiException implements Exception {
        final String message;
        final int statusCode;
      
        ApiException(this.message, this.statusCode);
      
        
        String toString() => 'API错误 ($statusCode): $message';
      }
      

通过合理使用 try-catch、全局错误捕获和用户反馈机制,可以显著提升 Flutter 应用的健壮性和用户体验,避免因未处理异常导致的崩溃。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值