如何通过异步请求在 Widget 或 Flutter Screen 中重用异常处理?

问题描述 投票:0回答:0

这就是我目前正在努力实现的目标:

我想使用

intl
包显示不同的错误消息,它使用
BuildContext
来了解用户语言。因此我想在 Widget 中处理异常,以便我可以用适当的语言显示它们。

我已经实现了我的代码如下:

  • 我显示所有内容的小部件/屏幕。
  • 管理状态的提供者。
  • 负责提出请求的服务。
  • 一组自定义异常来选择我想要显示的错误消息。

我的问题是,如果我尝试通过构建一个捕获异常的未来来重用异常,那么无论如何都会执行使查询的原始函数。

为了说明这个问题,我写了一个包含所有这些组件的最小示例:

import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:http/io_client.dart' as http;

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

///
/// CUSTOM EXCEPTION
///
class CustomTimeoutException implements Exception {}

///
/// SERVICE
///
class DemoService {
  /// Return the client with a set timeout
  http.IOClient _client() {
    final ioClient = HttpClient();
    ioClient.connectionTimeout = const Duration(seconds: 5);
    return http.IOClient(ioClient);
  }

  /// Fetch all items of the inventory
  Future<dynamic> fetchAllItems() async {
    const String url = "http://10.0.2.2:8081";

    http.Response response;
    try {
      response = await _client().get(Uri.parse(url));
    } on SocketException {
      print("Timeout reaching the server");
      throw CustomTimeoutException();
    }

    if (response.statusCode != 200) {
      print("Error fetching all data. ${response.statusCode}");
      throw Exception("Error trying to fetch. (${response.statusCode})");
    }

    return response.body;
  }
}

///
/// PROVIDER
///
class DemoProvider extends ChangeNotifier {
  final List<String> _items = [];
  bool _isLoading = false;

  List<String> get items => _items;
  bool get isLoading => _isLoading;

  /// Download the list of items
  Future<void> fetchAllItems(bool refresh) async {
    // Fetch
    _isLoading = true;
    notifyListeners();

    try {
      final response = await DemoService().fetchAllItems();

      _items.addAll(response['items']);
      _isLoading = false;
      notifyListeners();
    } on CustomTimeoutException {
      _isLoading = false;
      notifyListeners();
      rethrow;
    }
  }
}

///
/// MAIN WIDGET
///
class DemoWidget extends StatefulWidget {
  const DemoWidget({Key? key}) : super(key: key);

  @override
  State<DemoWidget> createState() => _DemoWidgetState();
}

class _DemoWidgetState extends State<DemoWidget> {
  String? _error;
  void _onTimeOutError(dynamic _) {
    setState(() {
      _error =
          "There was a timeout :( ${_.toString()}"; // TODO: Get translation from context
    });
  }

  Future<void> _fetchAllItems(bool refresh) async {
    setState(() {
      _error = null;
    });
    return context
        .read<DemoProvider>()
        .fetchAllItems(refresh)
        .catchError(_onTimeOutError, test: (e) => e is CustomTimeoutException);
  }

  void _onPressed() {
    print("Test");
    _fetchAllItems(true).then(
      (value) => print("Do something after fetch"),
    );
  }

  void _onPressed2() {
    print("Test2");
    _fetchAllItems(true).then(
      (value) => print("Do another thing after fetch"),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Consumer<DemoProvider>(builder: (ctx, provider, _) {
      return provider.isLoading
          ? const Center(
              child: CircularProgressIndicator(),
            )
          : Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  ElevatedButton(
                    onPressed: _onPressed,
                    child: const Text("Test"),
                  ),
                  ElevatedButton(
                    onPressed: _onPressed2,
                    child: const Text("Test2"),
                  ),
                  if (_error != null) Text(_error!)
                ],
              ),
            );
    });
  }
}

///
/// APP
///
class DemoApp extends StatefulWidget {
  const DemoApp({super.key});

  @override
  State<DemoApp> createState() => _DemoApp();
}

class _DemoApp extends State<DemoApp> {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => DemoProvider()),
      ],
      child: const MaterialApp(
        title: "Demo Application",
        home: Scaffold(
          body: DemoWidget(),
        ),
      ),
    );
  }
}

void main() {
  runApp(DemoApp());
}

我希望能够在不同的函数中重用异常处理(

_onPress
onPress1
)。但是
then
总是被执行。

I/flutter (15240): Test
I/flutter (15240): Timeout reaching the server
I/flutter (15240): Do something after fetch

我希望仅在未捕获到异常时才执行最后一次打印。

flutter dart asynchronous flutter-provider
© www.soinside.com 2019 - 2024. All rights reserved.