Repository: GetStream/stream-chat-flutter

Issue:
Network errors are not handled properly
### Which packages are you using?

stream_chat_flutter, stream_chat_persistance, stream_chat_localizations

### On what platforms did you experience the issue?

iOS

### What version are you using?

  stream_chat_flutter: 7.0.0
  stream_chat_localizations: 7.0.0
  stream_chat_persistence: 7.0.0

### What happened?

```
_TypeError: type 'String' is not a subtype of type 'Map<String, dynamic>'
  File "stream_chat_error.dart", line 97, in new StreamChatNetworkError.fromDioException
  File "stream_http_client.dart", line 102, in StreamHttpClient._parseError
  File "stream_http_client.dart", line 125, in StreamHttpClient.get
  File "<asynchronous suspension>"
  File "channel_api.dart", line 61, in ChannelApi.queryChannels
  File "<asynchronous suspension>"
  File "client.dart", line 683, in StreamChatClient.queryChannelsOnline
  File "<asynchronous suspension>"
  File "client.dart", line 489, in StreamChatClient._connectionStatusHandler
```

### Steps to reproduce

```bash
1. Work with stream chat in a environment with bad network connection
2. Observe crashes

We see this when the users connectivity drops to "None":
```


### Supporting info to reproduce

![image](https://github.com/GetStream/stream-chat-flutter/assets/6661557/0d415eb1-8c41-45a9-9a10-30e9fb8c378c)


It looks like the ErrorReponse.fromJson(data) fails:
in `stream_chat_error.dart`
```dart
  factory StreamChatNetworkError.fromDioException(DioException exception) {
    final response = exception.response;
    ErrorResponse? errorResponse;
    final data = response?.data;
    if (data != null) {
      errorResponse = ErrorResponse.fromJson(data);
    }
    return StreamChatNetworkError.raw(
      code: errorResponse?.code ?? -1,
      message: errorResponse?.message ??
          response?.statusMessage ??
          exception.message ??
          '',
      statusCode: errorResponse?.statusCode ?? response?.statusCode,
      data: errorResponse,
      isRequestCancelledError: exception.type == DioExceptionType.cancel,
    )..stackTrace = exception.stackTrace;
  }
```
### Relevant log output

_No response_

### Flutter analyze output

_No response_

### Flutter doctor output

_No response_

### Code of Conduct

- [X] I agree to follow this project's Code of Conduct


Discussion: https://github.com/GetStream/stream-chat-flutter/issues/1634 is possible related
This issue is stale because it has been open for 20 days with no activity.

Fix patch:
diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md
index 07314700e6..a466c2289f 100644
--- a/packages/stream_chat/CHANGELOG.md
+++ b/packages/stream_chat/CHANGELOG.md
@@ -4,6 +4,8 @@
 
 - [[#1837]](https://github.com/GetStream/stream-chat-flutter/issues/1837) Delete image and file 
   attachments from the CDN, when the message get's hard deleted.
+- [[#1819]](https://github.com/GetStream/stream-chat-flutter/issues/1819) Handle network errors
+  with String payload.
 
 ## 7.0.1
 
diff --git a/packages/stream_chat/lib/src/core/error/stream_chat_error.dart b/packages/stream_chat/lib/src/core/error/stream_chat_error.dart
index 5ce26f00d5..869439ddd3 100644
--- a/packages/stream_chat/lib/src/core/error/stream_chat_error.dart
+++ b/packages/stream_chat/lib/src/core/error/stream_chat_error.dart
@@ -1,3 +1,5 @@
+import 'dart:convert';
+
 import 'package:equatable/equatable.dart';
 import 'package:stream_chat/stream_chat.dart';
 import 'package:web_socket_channel/web_socket_channel.dart';
@@ -93,8 +95,10 @@ class StreamChatNetworkError extends StreamChatError {
     final response = exception.response;
     ErrorResponse? errorResponse;
     final data = response?.data;
-    if (data != null) {
+    if (data is Map<String, Object?>) {
       errorResponse = ErrorResponse.fromJson(data);
+    } else if (data is String) {
+      errorResponse = ErrorResponse.fromJson(jsonDecode(data));
     }
     return StreamChatNetworkError.raw(
       code: errorResponse?.code ?? -1,


Test patch:
diff --git a/packages/stream_chat/test/src/core/error/stream_chat_error_test.dart b/packages/stream_chat/test/src/core/error/stream_chat_error_test.dart
index 8d4e5465b1..c95afd3cb4 100644
--- a/packages/stream_chat/test/src/core/error/stream_chat_error_test.dart
+++ b/packages/stream_chat/test/src/core/error/stream_chat_error_test.dart
@@ -87,6 +87,75 @@ void main() {
       expect(error.data?.message, data.message);
     });
 
+    test('.fromDioException with String data', () {
+      const code = 333;
+      const statusCode = 666;
+      const message = 'test-error-message';
+      final options = RequestOptions(path: 'test-path');
+      const data = '''
+      {
+        "code": $code,
+        "StatusCode": $statusCode,
+        "message": "$message"
+      }''';
+      final dioError = DioException(
+        requestOptions: options,
+        response: Response(
+          requestOptions: options,
+          statusCode: statusCode,
+          data: data,
+        ),
+      );
+      final error = StreamChatNetworkError.fromDioException(dioError);
+      expect(error, isNotNull);
+      expect(error.code, code);
+      expect(error.message, message);
+      expect(error.statusCode, statusCode);
+      expect(error.data?.code, code);
+      expect(error.data?.statusCode, statusCode);
+      expect(error.data?.message, message);
+    });
+
+    test('.fromDioException with null data and existing response message', () {
+      const statusCode = 666;
+      const message = 'test-error-message';
+      final options = RequestOptions(path: 'test-path');
+      final dioError = DioException(
+        requestOptions: options,
+        response: Response(
+          requestOptions: options,
+          statusCode: statusCode,
+          statusMessage: message,
+        ),
+      );
+      final error = StreamChatNetworkError.fromDioException(dioError);
+      expect(error, isNotNull);
+      expect(error.code, -1);
+      expect(error.message, message);
+      expect(error.statusCode, statusCode);
+      expect(error.data, isNull);
+    });
+
+    test('.fromDioException with null data and existing exception message', () {
+      const statusCode = 666;
+      const message = 'test-error-message';
+      final options = RequestOptions(path: 'test-path');
+      final dioError = DioException(
+        requestOptions: options,
+        message: message,
+        response: Response(
+          requestOptions: options,
+          statusCode: statusCode,
+        ),
+      );
+      final error = StreamChatNetworkError.fromDioException(dioError);
+      expect(error, isNotNull);
+      expect(error.code, -1);
+      expect(error.message, message);
+      expect(error.statusCode, statusCode);
+      expect(error.data, isNull);
+    });
+
     test('should match if message, code and statusCode is same', () {
       const code = 333;
       const statusCode = 666;