diff --git a/packages/cross_file/CHANGELOG.md b/packages/cross_file/CHANGELOG.md index ac14e215177b..1a0c2364ccf1 100644 --- a/packages/cross_file/CHANGELOG.md +++ b/packages/cross_file/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.3.5+5 + +* Fixes native `readAsString` returning mojibake for `XFile.fromData`, which + decoded the bytes as UTF-16 code units instead of using the `encoding` + argument. + ## 0.3.5+4 * Adds a runnable `main` entry point and an additional `XFile.fromData` diff --git a/packages/cross_file/lib/src/types/io.dart b/packages/cross_file/lib/src/types/io.dart index 9143ec687b47..d1891874aad2 100644 --- a/packages/cross_file/lib/src/types/io.dart +++ b/packages/cross_file/lib/src/types/io.dart @@ -112,10 +112,9 @@ class XFile extends XFileBase { @override Future readAsString({Encoding encoding = utf8}) { - if (_bytes != null) { - // TODO(kevmoo): Remove ignore and fix when the MIN Dart SDK is 3.3 - // ignore: unnecessary_non_null_assertion - return Future.value(String.fromCharCodes(_bytes!)); + final Uint8List? bytes = _bytes; + if (bytes != null) { + return Future.sync(() => encoding.decode(bytes)); } return _file.readAsString(encoding: encoding); } diff --git a/packages/cross_file/pubspec.yaml b/packages/cross_file/pubspec.yaml index 099b57ae387c..57e81341a11f 100644 --- a/packages/cross_file/pubspec.yaml +++ b/packages/cross_file/pubspec.yaml @@ -2,7 +2,7 @@ name: cross_file description: An abstraction to allow working with files across multiple platforms. repository: https://github.com/flutter/packages/tree/main/packages/cross_file issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+cross_file%22 -version: 0.3.5+4 +version: 0.3.5+5 environment: sdk: ^3.10.0 diff --git a/packages/cross_file/test/x_file_io_test.dart b/packages/cross_file/test/x_file_io_test.dart index 8ca6768b36f3..f7aa19bd042d 100644 --- a/packages/cross_file/test/x_file_io_test.dart +++ b/packages/cross_file/test/x_file_io_test.dart @@ -82,6 +82,24 @@ void main() { test('Can be read as a string', () async { expect(await file.readAsString(), equals(expectedStringContents)); }); + + test('Can be read as a string with multi-byte characters', () async { + const contents = 'Hello, world! I ❤ ñ! 空手 😀'; + final multiByteFile = XFile.fromData(utf8.encode(contents)); + expect(await multiByteFile.readAsString(), equals(contents)); + }); + + test('Can be read as a string with a non-default encoding', () async { + const contents = 'Une soirée à Genève'; + final latin1File = XFile.fromData(latin1.encode(contents)); + expect(await latin1File.readAsString(encoding: latin1), equals(contents)); + }); + + test('Reading undecodable data fails the future', () async { + final malformedFile = XFile.fromData(Uint8List.fromList([0xc3, 0x28])); + await expectLater(malformedFile.readAsString(), throwsA(isA())); + }); + test('Can be read as bytes', () async { expect(await file.readAsBytes(), equals(bytes)); });