Flutter异常处理插件exception_handler的使用
Flutter异常处理插件exception_handler的使用
介绍
exception_handler
是一个 Dart 包,为在 Flutter 应用中处理 API 调用和异常提供了一个健壮的框架。它简化了网络请求、响应解析以及各种异常处理的过程,是 Flutter 开发者的重要工具。
特性
- API 处理:通过结构化的方法简化 API 调用,确保代码清晰且易于维护。
- 异常管理:全面的异常处理,包括网络问题和 HTTP 错误,提高应用的健壮性。
入门指南
要开始使用这个包,请在 pubspec.yaml
文件中添加依赖:
dependencies:
exception_handler: ^latest_version
然后,在你想要使用的 Dart 文件中导入它:
import 'package:exception_handler/exception_handler.dart';
使用示例
以下是一些示例,展示如何使用该包的各种功能。
定义模型
首先创建数据模型,并为其定义一个用于 JSON 反序列化的工厂构造函数。例如,UserModel
模型如下所示:
class UserModel {
final int id;
final String name;
final String email;
UserModel({required this.id, required this.name, required this.email});
factory UserModel.fromJson(Map<String, dynamic> json) {
return UserModel(
id: json['id'],
name: json['name'],
email: json['email'],
);
}
}
基本 API 调用
这是一个简单的例子,展示如何进行 API 调用并处理响应:
import 'package:dio/dio.dart';
import 'package:exception_handler/exception_handler.dart';
Future<void> fetchUserData() async {
final ApiHandler<Response, UserModel> apiHandler = ApiHandler(
apiCall: () => dio.get('https://example.com/api/user'),
parserModel: (data) => UserModel.fromJson(data),
);
ResultState<UserModel> result = await DioExceptionHandler.callApi_(apiHandler);
switch (result) {
case SuccessState<UserModel>(:UserModel data):
print('UserModel data: $data');
case FailureState<UserModel>(:ExceptionState exception):
print('Error: ${exception.toString()}');
}
}
高级 API 调用与自定义解析器
使用自定义解析器来处理复杂的 API 响应:
import 'package:dio/dio.dart';
import 'package:exception_handler/exception_handler.dart';
Future<void> fetchComplexData() async {
final ApiHandler<Response, ComplexData> apiHandler = ApiHandler(
apiCall: () => dio.get('https://example.com/api/complex'),
parserModel: customParser,
);
ResultState<ComplexData> result = await DioExceptionHandler.callApi_(apiHandler);
switch (result) {
case SuccessState<ComplexData>(:ComplexData data):
print('Complex Data: $data');
case FailureState<ComplexData>(:ExceptionState exception):
print('Error: ${exception.toString()}');
}
}
ComplexData customParser(dynamic responseData) {
// 自定义解析逻辑
return ComplexData.fromResponse(responseData);
}
基本异常处理
处理基本异常并记录信息:
void handleApiCall() async {
ResultState<UserModel> result = await DioExceptionHandler.callApi_(apiHandler);
switch (result) {
case SuccessState<UserModel>(:UserModel data):
print('User data retrieved successfully: $data');
case FailureState<UserModel>(:ExceptionState exception):
print('Exception occurred: ${exception.toString()}');
// 额外的日志或错误处理
}
}
高级异常处理与特定情况
实现详细的异常处理:
void advancedExceptionHandling() async {
ResultState<UserModel> result = await DioExceptionHandler.callApi_(apiHandler);
switch (result) {
case SuccessState<UserModel>(:UserModel data):
print('Fetched data: $data');
case FailureState<UserModel>(:ExceptionState exception):
_handleExceptions(exception);
}
}
void _handleExceptions(ExceptionState exception) {
switch (exception) {
case DataClientExceptionState():
handleClientException(exception);
case DataParseExceptionState():
handleParseException(exception);
case DataHttpExceptionState():
handleHttpException(exception);
case DataNetworkExceptionState():
handleNetworkException(exception);
case _:
handleUnknownException(exception);
}
}
void handleNetworkException(DataNetworkExceptionState exception) {
print('Network Exception: ${exception.networkException}');
// 处理网络异常的额外逻辑
}
void handleHttpException(DataHttpExceptionState exception) {
print('HTTP Exception: ${exception.httpException}');
// 处理HTTP异常的额外逻辑
}
void handleParseException(DataParseExceptionState exception) {
print('Parse Exception: ${exception.parseException}');
// 处理解析异常的额外逻辑
}
void handleClientException(DataClientExceptionState exception) {
print('Client Exception: ${exception.clientException}');
// 处理客户端异常的额外逻辑
}
void handleUnknownException(ExceptionState exception) {
print('Unknown Exception: ${exception.toString()}');
// 处理未知异常的额外逻辑
}
示例完整 Demo
以下是一个完整的 Flutter 应用示例,展示如何使用 exception_handler
插件处理 API 请求和异常:
import 'dart:math';
import 'package:dio/dio.dart';
import 'package:exception_handler/exception_handler.dart';
import 'package:flutter/material.dart';
// UserModel 类定义
class UserModel extends CustomEquatable {
const UserModel({
this.id,
this.name,
this.username,
this.email,
this.address,
this.phone,
this.website,
this.company,
});
factory UserModel.fromJson(Map<String, dynamic> json) {
if (json case {
'id': int? id,
'name': String? name,
'username': String? username,
'email': String? email,
'address': Map<String, dynamic>? address,
'phone': String? phone,
'website': String? website,
'company': Map<String, dynamic>? company,
}) {
return UserModel(
id: id,
name: name,
username: username,
email: email,
address: address != null ? Address.fromJson(address) : null,
phone: phone,
website: website,
company: company != null ? Company.fromJson(company) : null,
);
} else {
throw FormatException('Invalid JSON: $json');
}
}
final Address? address;
final Company? company;
final String? email;
final int? id;
final String? name;
final String? phone;
final String? username;
final String? website;
[@override](/user/override)
Map<String, Object?> get namedProps => {
'id': id,
'name': name,
'username': username,
'email': email,
'address': address,
'phone': phone,
'website': website,
'company': company,
};
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['name'] = name;
data['username'] = username;
data['email'] = email;
if (address != null) {
data['address'] = address!.toJson();
}
data['phone'] = phone;
data['website'] = website;
if (company != null) {
data['company'] = company!.toJson();
}
return data;
}
}
// Address 类定义
class Address extends CustomEquatable {
const Address({
this.street,
this.suite,
this.city,
this.zipcode,
this.geo,
});
factory Address.fromJson(Map<String, dynamic> json) {
if (json case {
'street': String? street,
'suite': String? suite,
'city': String? city,
'zipcode': String? zipcode,
'geo': Map<String, dynamic>? geo,
}) {
return Address(
street: street,
suite: suite,
city: city,
zipcode: zipcode,
geo: geo != null ? Geo.fromJson(geo) : null,
);
} else {
throw FormatException('Invalid JSON: $json');
}
}
final String? city;
final Geo? geo;
final String? street;
final String? suite;
final String? zipcode;
[@override](/user/override)
Map<String, Object?> get namedProps => {
'suite': suite,
'street': street,
'city': city,
'zipcode': zipcode,
'geo': geo,
};
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['street'] = street;
data['suite'] = suite;
data['city'] = city;
data['zipcode'] = zipcode;
if (geo != null) {
data['geo'] = geo!.toJson();
}
return data;
}
}
// Geo 类定义
class Geo extends CustomEquatable {
const Geo({this.lat, this.lng});
factory Geo.fromJson(Map<String, dynamic> json) {
if (json case {
'lat': String? lat,
'lng': String? lng,
}) {
final Geo geo = Geo(lat: lat, lng: lng);
return geo;
} else {
throw FormatException('Invalid JSON: $json');
}
}
final String? lat;
final String? lng;
[@override](/user/override)
Map<String, Object?> get namedProps => {
'lat': lat,
'lng': lng,
};
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['lat'] = lat;
data['lng'] = lng;
return data;
}
}
// Company 类定义
class Company extends CustomEquatable {
const Company({this.name, this.catchPhrase, this.bs});
factory Company.fromJson(Map<String, dynamic> json) {
if (json case {
'name': String? name,
'catchPhrase': String? catchPhrase,
'bs': String? bs,
}) {
return Company(
name: name,
catchPhrase: catchPhrase,
bs: bs,
);
} else {
throw FormatException('Invalid JSON: $json');
}
}
final String? bs;
final String? catchPhrase;
final String? name;
[@override](/user/override)
Map<String, Object?> get namedProps => {
'name': name,
'catchPhrase': catchPhrase,
'bs': bs,
};
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['name'] = name;
data['catchPhrase'] = catchPhrase;
data['bs'] = bs;
return data;
}
}
// UserService 类定义
class UserService {
final Dio dio = Dio();
Future<ResultState<UserModel>> getDataUser(int id) async {
final ResultState<UserModel> result = await DioExceptionHandler.callApi_<Response, UserModel>(
ApiHandler(
apiCall: () => dio.get('https://jsonplaceholder.typicode.com/users/$id'),
parserModel: (Object? data) => UserModel.fromJson(data as Map<String, dynamic>),
),
);
return result;
}
Future<ResultState<UserModel>> getDataUserExtensionDio(int id) async {
final ResultState<UserModel> result = await dio
.get('https://jsonplaceholder.typicode.com/users/$id')
.fromJson(UserModel.fromJson);
return result;
}
}
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
[@override](/user/override)
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 1;
void _incrementCounter() {
setState(() {
_counter++;
});
}
void _decrementCounter() {
setState(() {
_counter--;
});
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: SingleChildScrollView(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'ID: $_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 16),
FutureBuilder(
future: UserService().getDataUserExtensionDio(_counter),
builder: (
BuildContext context,
AsyncSnapshot<ResultState<UserModel>> snapshot,
) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
final ResultState<UserModel> resultState = snapshot.requireData;
final StatelessWidget uiWidget = switch (resultState) {
SuccessState<UserModel> success =>
UiUserWidget(success.data),
FailureState<UserModel> failure =>
UiExceptionWidget(failure.exception),
};
return uiWidget;
},
),
],
),
),
),
floatingActionButton: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: _decrementCounter,
tooltip: 'Decrement',
child: const Icon(Icons.remove),
),
const SizedBox(width: 10),
FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
],
),
);
}
}
// UiExceptionWidget 类定义
class UiExceptionWidget extends StatelessWidget {
const UiExceptionWidget(
this.exception, {
super.key,
});
static const List<String> links = [
'https://http.pizza',
'https://http.garden',
'https://httpducks.com',
'https://httpgoats.com',
'https://http.dog',
'https://httpcats.com',
];
final ExceptionState<UserModel> exception;
[@override](/user/override)
Widget build(BuildContext context) {
final String textException = switch (exception) {
DataClientExceptionState<UserModel>() =>
'Debugger Error Client: $exception',
DataParseExceptionState<UserModel>() =>
'Debugger Error Parse: $exception',
DataHttpExceptionState<UserModel>() => 'Debugger Error Http: $exception',
DataNetworkExceptionState<UserModel>() =>
'Debugger Error Network: $exception\n\nError: ${exception.toString().split('.').last}',
DataCacheExceptionState<UserModel>() =>
'Debugger Error Cache: $exception',
DataInvalidInputExceptionState<UserModel>() =>
'Debugger Error Invalid Input: $exception',
DataUnknownExceptionState<UserModel>() =>
'Debugger Error Unknown: $exception',
};
final Text text = Text(
textException,
style: TextStyle(color: Colors.orange[800]),
);
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: text));
});
final Widget imageException = switch (exception) {
DataClientExceptionState<UserModel>() =>
const Icon(Icons.devices_outlined, size: 200),
DataParseExceptionState<UserModel>() =>
const Icon(Icons.sms_failed_outlined, size: 200),
DataHttpExceptionState<UserModel>() => Image.network(
'${links[Random().nextInt(links.length)]}/404.webp',
),
DataNetworkExceptionState<UserModel>() =>
const Icon(Icons.wifi_off_outlined, size: 200),
DataCacheExceptionState<UserModel>() =>
const Icon(Icons.storage_outlined, size: 200),
DataInvalidInputExceptionState<UserModel>() =>
const Icon(Icons.textsms_outlined, size: 200),
DataUnknownExceptionState<UserModel>() =>
const Icon(Icons.close, size: 200),
};
return Column(
children: [
SizedBox(
height: 400,
child: imageException,
),
const SizedBox(height: 8),
text,
],
);
}
}
// UiUserWidget 类定义
class UiUserWidget extends StatelessWidget {
const UiUserWidget(
this.user, {
super.key,
});
final UserModel user;
[@override](/user/override)
Widget build(BuildContext context) {
final TextTheme textTheme = Theme.of(context).textTheme;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Name: ${user.name}',
style: textTheme.headlineMedium,
),
const SizedBox(height: 8),
Text(
'Phone: ${user.phone}',
style: textTheme.bodyMedium,
),
const SizedBox(height: 8),
Text(
'Address: ${user.address?.street} - ${user.address?.city} ${user.address?.zipcode}',
style: textTheme.bodyMedium,
),
const SizedBox(height: 8),
Text(
'Email: ${user.email}',
style: textTheme.bodyMedium,
),
],
);
}
}
更多关于Flutter异常处理插件exception_handler的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter异常处理插件exception_handler的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用exception_handler
插件进行异常处理的示例代码。exception_handler
插件可以帮助你捕获未处理的异常并将其发送到指定的处理函数。
首先,你需要在pubspec.yaml
文件中添加exception_handler
依赖:
dependencies:
flutter:
sdk: flutter
exception_handler: ^x.y.z # 请使用最新版本号替换x.y.z
然后,运行flutter pub get
来获取依赖。
接下来,在你的Flutter应用中配置ExceptionHandler
。以下是一个完整的示例,包括如何在应用启动时注册异常处理函数,以及如何故意抛出一个异常来测试处理逻辑。
main.dart
import 'package:flutter/material.dart';
import 'package:exception_handler/exception_handler.dart';
void main() {
ExceptionHandler.registerGlobalExceptionHandler((error, stackTrace) {
// 在这里处理未捕获的异常
print("Caught an exception: $error");
print("Stack trace: $stackTrace");
// 你可以在这里执行一些其他的逻辑,比如发送错误报告到服务器
});
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void _throwError() {
// 这里故意抛出一个异常来测试异常处理
throw Exception("This is a test exception");
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Exception Handler Demo'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
try {
_throwError();
} catch (e, stack) {
// 这里捕获并处理按钮点击时抛出的异常
// 但全局异常处理器仍然会捕获未在其他地方捕获的异常
print("Caught in button: $e");
print("Stack trace in button: $stack");
// 可以选择重新抛出异常以让全局处理器捕获
// throw e;
}
},
child: Text('Throw Error'),
),
),
);
}
}
解释
-
注册全局异常处理器:
ExceptionHandler.registerGlobalExceptionHandler((error, stackTrace) { print("Caught an exception: $error"); print("Stack trace: $stackTrace"); });
在
main
函数中,我们使用ExceptionHandler.registerGlobalExceptionHandler
注册了一个全局异常处理函数。这个函数会捕获所有未在其他地方捕获的异常。 -
故意抛出异常:
void _throwError() { throw Exception("This is a test exception"); }
在
_MyHomePageState
类中,我们定义了一个_throwError
方法,该方法会抛出一个异常。 -
按钮点击事件:
ElevatedButton( onPressed: () { try { _throwError(); } catch (e, stack) { print("Caught in button: $e"); print("Stack trace in button: $stack"); // throw e; // 如果取消注释,这个异常会再次被全局处理器捕获 } }, child: Text('Throw Error'), )
在按钮的点击事件中,我们调用
_throwError
方法,并在try-catch
块中捕获异常。这里的catch
块仅用于演示如何在局部捕获异常,但你可以选择重新抛出异常以让全局处理器捕获。
通过上述代码,你可以看到如何在Flutter项目中使用exception_handler
插件来捕获和处理未处理的异常。