From c49b2dc7ff4d36bfc63d5991cf028faa12c22cf3 Mon Sep 17 00:00:00 2001 From: glitchfl Date: Sun, 16 Aug 2026 16:11:20 +0300 Subject: [PATCH 1/2] [cross_file] fixed `readAsString` decoding in-memory bytes as UTF-16 --- packages/cross_file/CHANGELOG.md | 6 ++++++ packages/cross_file/lib/src/types/io.dart | 6 ++---- packages/cross_file/pubspec.yaml | 2 +- packages/cross_file/test/x_file_io_test.dart | 18 ++++++++++++++++++ 4 files changed, 27 insertions(+), 5 deletions(-) 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..270721d66112 100644 --- a/packages/cross_file/lib/src/types/io.dart +++ b/packages/cross_file/lib/src/types/io.dart @@ -111,11 +111,9 @@ class XFile extends XFileBase { } @override - Future readAsString({Encoding encoding = utf8}) { + Future readAsString({Encoding encoding = utf8}) async { 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!)); + return 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)); }); From 28e75bbd1952bb617cded9f7577f16d829793e4e Mon Sep 17 00:00:00 2001 From: glitchfl Date: Sun, 16 Aug 2026 17:45:35 +0300 Subject: [PATCH 2/2] changed to use Future.sync instead of async --- packages/cross_file/lib/src/types/io.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/cross_file/lib/src/types/io.dart b/packages/cross_file/lib/src/types/io.dart index 270721d66112..d1891874aad2 100644 --- a/packages/cross_file/lib/src/types/io.dart +++ b/packages/cross_file/lib/src/types/io.dart @@ -111,9 +111,10 @@ class XFile extends XFileBase { } @override - Future readAsString({Encoding encoding = utf8}) async { - if (_bytes != null) { - return encoding.decode(_bytes); + Future readAsString({Encoding encoding = utf8}) { + final Uint8List? bytes = _bytes; + if (bytes != null) { + return Future.sync(() => encoding.decode(bytes)); } return _file.readAsString(encoding: encoding); }