Solucionado (ver solução)
Solucionado
(ver solução)
1
resposta

[Sugestão] Possível sugestão para o desafio

Criei um arquivo a parte para exibir um Snackbar de erro;

void showErrorSnackbar({required BuildContext context, required message}) {
  ScaffoldMessenger.of(context).showSnackBar(
    SnackBar(
      duration: const Duration(
        milliseconds: 1500,
      ),
      backgroundColor: Colors.red,
      content: SingleChildScrollView(
        scrollDirection: Axis.horizontal,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Text(
              message,
              style: const TextStyle(
                color: Colors.white,
              ),
            ),
            const Icon(
              Icons.close,
              color: Colors.white,
            )
          ],
        ),
      ),
    ),
  );
}

Envolvi as chamadas do BalanceServices com try-catch e fiz erros customizados:

[...]

 Future<double> createPin({
    required String userId,
    required String pin,
  }) async {
    try {
      http.Response response = await http.post(
        Uri.parse("$url/create-pin"),
        headers: {"Content-Type": "application/json"},
        body: json.encode({
          "userId": userId,
          "newPin": pin,
        }),
      );

      if (response.statusCode == 200) {
        return (json.decode(response.body)["balance"] as int).toDouble();
      } else {
        throw CreatePinException("Ocorreu ao criar seu PIN, por favor tente novamente...");
      }
    } on CreatePinException catch (e) {
      throw CreatePinException(
        e.message,
      );
    } on http.ClientException catch (e) {
      throw http.ClientException(
        e.message,
      );
    } on SocketException catch (e) {
      throw SocketException(
        e.message,
      );
    } on Exception catch (e) {
      throw Exception(
        e.toString(),
      );
    }
  }

  Future<double> getBalance({
    required String userId,
    required String pin,
  }) async {
    try {
      http.Response response = await http.post(
        Uri.parse("$url/user-balance"),
        headers: {"Content-Type": "application/json"},
        body: json.encode({
          "userId": userId,
          "pin": pin,
        }),
      );

      if (response.statusCode == 200) {
        return (json.decode(response.body)["balance"] as int).toDouble();
      } else {
        throw AuthenticationException("PIN inválido, verifique-o e tente novamente...");
      }
    } on AuthenticationException catch (e) {
      throw AuthenticationException(
        e.message,
      );
    } on http.ClientException catch (e) {
      throw http.ClientException(
        e.message,
      );
    } on SocketException catch (e) {
      throw SocketException(
        e.message,
      );
    } on Exception catch (e) {
      throw Exception(
        e.toString(),
      );
    }
  }
}

Modifiquei a função da visibilidade do saldo, adicionando um catchError, que em caso de sucesso exibirá o Snackbar com a mensagem informada no catch da chamada da API.

Future<void> onVisibilityBalanceChanged() async {
    if (_isBalanceVisible) {
      setState(() {
        _isBalanceVisible = false;
      });
    } else {
      bool hasPin = await _balanceServices.hasPin(
        userId: widget.userId,
      );

      if (mounted) {
        showPinDialog(context, isRegister: !hasPin).then(
          (String? pin) {
            if (pin != null) {
              if (hasPin) {
                _balanceServices
                    .getBalance(
                  userId: widget.userId,
                  pin: pin,
                )
                    .then((double b) {
                  setState(() {
                    _isBalanceVisible = true;

                    balance = b;
                  });
                }).catchError((error) {
                  if (mounted) {
                    showErrorSnackbar(
                      context: context,
                      message: error.message,
                    );
                  }
                });
              } else {
                _balanceServices
                    .createPin(
                  userId: widget.userId,
                  pin: pin,
                )
                    .then((double b) {
                  setState(() {
                    _isBalanceVisible = true;

                    balance = b;
                  });
                }).catchError((error) {
                  if (mounted) {
                    showErrorSnackbar(
                      context: context,
                      message: error.message,
                    );
                  }
                });
              }
            }
          },
        );
      }
    }
  }
1 resposta
solução!

Olá, Cleiton, como vai?

Seu código está bem estruturado e segue boas práticas para tratamento de erros. A ideia de utilizar exceções personalizadas e um Snackbar para exibir mensagens de erro melhora bastante a experiência do usuário. Obrigado por compartilhar seu código.

Siga firme nos seus estudos e conte com o fórum sempre que precisar!

Abraços :)