From dc45cb5044861f78cd58580a6ab3a98c8dfe4393 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 10 Sep 2026 14:19:21 -0400 Subject: [PATCH 1/6] move out platform interface --- .../cross_file_platform_interface/AUTHORS | 6 + .../CHANGELOG.md | 3 + .../cross_file_platform_interface/LICENSE | 25 +++ .../cross_file_platform_interface/README.md | 21 +++ .../lib/cross_file_platform_interface.dart | 12 ++ .../lib/src/cross_file_platform.dart | 162 ++++++++++++++++++ .../platform_file_system_cross_directory.dart | 99 +++++++++++ .../platform_file_system_cross_file.dart | 114 ++++++++++++ .../lib/src/platform_cross_directory.dart | 92 ++++++++++ .../lib/src/platform_cross_entity.dart | 36 ++++ .../lib/src/platform_cross_file.dart | 122 +++++++++++++ ...atform_scoped_storage_cross_directory.dart | 104 +++++++++++ .../platform_scoped_storage_cross_file.dart | 102 +++++++++++ .../pubspec.yaml | 24 +++ .../test/cross_file_platform_test.dart | 117 +++++++++++++ 15 files changed, 1039 insertions(+) create mode 100644 packages/cross_file/cross_file_platform_interface/AUTHORS create mode 100644 packages/cross_file/cross_file_platform_interface/CHANGELOG.md create mode 100644 packages/cross_file/cross_file_platform_interface/LICENSE create mode 100644 packages/cross_file/cross_file_platform_interface/README.md create mode 100644 packages/cross_file/cross_file_platform_interface/lib/cross_file_platform_interface.dart create mode 100644 packages/cross_file/cross_file_platform_interface/lib/src/cross_file_platform.dart create mode 100644 packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_directory.dart create mode 100644 packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_file.dart create mode 100644 packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_directory.dart create mode 100644 packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_entity.dart create mode 100644 packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_file.dart create mode 100644 packages/cross_file/cross_file_platform_interface/lib/src/scoped_storage/platform_scoped_storage_cross_directory.dart create mode 100644 packages/cross_file/cross_file_platform_interface/lib/src/scoped_storage/platform_scoped_storage_cross_file.dart create mode 100644 packages/cross_file/cross_file_platform_interface/pubspec.yaml create mode 100644 packages/cross_file/cross_file_platform_interface/test/cross_file_platform_test.dart diff --git a/packages/cross_file/cross_file_platform_interface/AUTHORS b/packages/cross_file/cross_file_platform_interface/AUTHORS new file mode 100644 index 000000000000..a4a27429da16 --- /dev/null +++ b/packages/cross_file/cross_file_platform_interface/AUTHORS @@ -0,0 +1,6 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google LLC diff --git a/packages/cross_file/cross_file_platform_interface/CHANGELOG.md b/packages/cross_file/cross_file_platform_interface/CHANGELOG.md new file mode 100644 index 000000000000..6073234226b2 --- /dev/null +++ b/packages/cross_file/cross_file_platform_interface/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.1.0 + +* Initial release. diff --git a/packages/cross_file/cross_file_platform_interface/LICENSE b/packages/cross_file/cross_file_platform_interface/LICENSE new file mode 100644 index 000000000000..29b709dac6c7 --- /dev/null +++ b/packages/cross_file/cross_file_platform_interface/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/cross_file/cross_file_platform_interface/README.md b/packages/cross_file/cross_file_platform_interface/README.md new file mode 100644 index 000000000000..bbe36d350d9e --- /dev/null +++ b/packages/cross_file/cross_file_platform_interface/README.md @@ -0,0 +1,21 @@ +# cross_file_platform_interface + +A common platform interface for the [`cross_file`](https://pub.dev/packages/cross_file) plugin. + +This interface allows platform implementations of the `cross_file` plugin, as well as the plugin +itself, to ensure they are supporting the same interface. + +# Usage + +To implement a new platform implementation of `cross_file`, extend +[`CrossFilePlatform`](lib/src/cross_file_platform.dart) with an implementation that performs the +platform-specific behavior, and when you register your plugin, set the default +`CrossFilePlatform` by calling `CrossFilePlatform.instance = CrossFileMyPlatform()`. + +# Note on breaking changes + +Strongly prefer non-breaking changes (such as adding a method to the interface) +over breaking changes for this package. + +See https://flutter.dev/go/platform-interface-breaking-changes for a discussion on why a less-clean +interface is preferable to a breaking change. diff --git a/packages/cross_file/cross_file_platform_interface/lib/cross_file_platform_interface.dart b/packages/cross_file/cross_file_platform_interface/lib/cross_file_platform_interface.dart new file mode 100644 index 000000000000..790f897a7bd9 --- /dev/null +++ b/packages/cross_file/cross_file_platform_interface/lib/cross_file_platform_interface.dart @@ -0,0 +1,12 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +export 'src/cross_file_platform.dart'; +export 'src/file_system/platform_file_system_cross_directory.dart'; +export 'src/file_system/platform_file_system_cross_file.dart'; +export 'src/platform_cross_directory.dart'; +export 'src/platform_cross_entity.dart'; +export 'src/platform_cross_file.dart'; +export 'src/scoped_storage/platform_scoped_storage_cross_directory.dart'; +export 'src/scoped_storage/platform_scoped_storage_cross_file.dart'; diff --git a/packages/cross_file/cross_file_platform_interface/lib/src/cross_file_platform.dart b/packages/cross_file/cross_file_platform_interface/lib/src/cross_file_platform.dart new file mode 100644 index 000000000000..8500c179626e --- /dev/null +++ b/packages/cross_file/cross_file_platform_interface/lib/src/cross_file_platform.dart @@ -0,0 +1,162 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:convert'; +import 'dart:typed_data'; + +import 'file_system/platform_file_system_cross_directory.dart'; +import 'file_system/platform_file_system_cross_file.dart'; +import 'platform_cross_directory.dart'; +import 'platform_cross_entity.dart'; +import 'platform_cross_file.dart'; +import 'scoped_storage/platform_scoped_storage_cross_directory.dart'; +import 'scoped_storage/platform_scoped_storage_cross_file.dart'; + +/// Interface for a platform implementation of `cross_file`. +abstract base class CrossFilePlatform { + /// The instance of [CrossFilePlatform] to be used. + /// + /// Platform implementations packages should set this with their own + /// implementation of [CrossFilePlatform] when they register themselves. + static CrossFilePlatform? instance; + + /// Creates a new [PlatformXFile]. + PlatformFileSystemXFile createPlatformFileSystemXFile( + PlatformFileSystemXFileCreationParams params, + ) { + return _DefaultFileSystemXFile(params); + } + + /// Creates a new [PlatformFileSystemXDirectory]. + PlatformFileSystemXDirectory createPlatformFileSystemXDirectory( + PlatformFileSystemXDirectoryCreationParams params, + ) { + return _DefaultFileSystemXDirectory(params); + } + + /// Creates a new [PlatformScopedStorageXDirectory]. + PlatformScopedStorageXFile createPlatformScopedStorageXFile( + PlatformScopedStorageXFileCreationParams params, + ) { + return _DefaultScopedStorageXFile(params); + } + + /// Creates a new [PlatformScopedStorageXDirectory]. + PlatformScopedStorageXDirectory createPlatformScopedStorageXDirectory( + PlatformScopedStorageXDirectoryCreationParams params, + ) { + return _DefaultScopedStorageXDirectory(params); + } +} + +/// Implementation of [PlatformFileSystemXFile} that represents a resource that +/// does not exist. +final class _DefaultFileSystemXFile extends PlatformFileSystemXFile { + _DefaultFileSystemXFile(super.params) : super.implementation(); + + @override + Future exists() async => false; + + @override + Future lastModified() async => null; + + @override + Future length() async => null; + + @override + Future name() async => null; + + @override + Stream openRead([int? start, int? end]) async* { + throw UnsupportedError('This instance does not represent any resource.'); + } + + @override + Future readAsBytes() { + throw UnsupportedError('This instance does not represent any resource.'); + } + + @override + Future readAsString({Encoding encoding = utf8}) { + throw UnsupportedError('This instance does not represent any resource.'); + } + + @override + Future writeAsBytes(PlatformWriteAsBytesParams params) { + throw UnsupportedError('This instance does not represent any resource.'); + } +} + +/// Implementation of [PlatformFileSystemXDirectory} that represents a directory +/// that does not exist. +final class _DefaultFileSystemXDirectory extends PlatformFileSystemXDirectory { + _DefaultFileSystemXDirectory(super.params) : super.implementation(); + + @override + Future exists() async => false; + + @override + Stream list(PlatformListParams params) async* { + throw UnsupportedError('This instance does not represent any directory.'); + } +} + +/// Implementation of [PlatformScopedStorageXFile} that represents a resource +/// that does not exist. +final class _DefaultScopedStorageXFile extends PlatformScopedStorageXFile { + _DefaultScopedStorageXFile(super.params) : super.implementation(); + + @override + Future canRead() async => false; + + @override + Future exists() async => false; + + @override + Future lastModified() async => null; + + @override + Future length() async => null; + + @override + Future name() async => null; + + @override + Stream openRead([int? start, int? end]) async* { + throw UnsupportedError('This instance does not represent any resource.'); + } + + @override + Future readAsBytes() { + throw UnsupportedError('This instance does not represent any resource.'); + } + + @override + Future readAsString({Encoding encoding = utf8}) { + throw UnsupportedError('This instance does not represent any resource.'); + } + + @override + Future dispose() async {} +} + +/// Implementation of [PlatformScopedStorageXDirectory} that represents a +/// directory that does not exist. +final class _DefaultScopedStorageXDirectory extends PlatformScopedStorageXDirectory { + _DefaultScopedStorageXDirectory(super.params) : super.implementation(); + + @override + Future exists() async => false; + + @override + Future canRead() async => false; + + @override + Stream list(PlatformListParams params) async* { + throw UnsupportedError('This instance does not represent any directory.'); + } + + @override + Future dispose() async {} +} diff --git a/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_directory.dart b/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_directory.dart new file mode 100644 index 000000000000..fb856bed4d1a --- /dev/null +++ b/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_directory.dart @@ -0,0 +1,99 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; + +import '../cross_file_platform.dart'; +import '../platform_cross_directory.dart'; + +/// Object specifying creation parameters for creating a [PlatformFileSystemXDirectory]. +/// +/// Platform-specific implementations can add additional fields by extending +/// this class. +/// +/// This example demonstrates how to extend the [PlatformFileSystemXDirectoryCreationParams] to +/// provide additional platform-specific parameters. +/// +/// When extending [PlatformFileSystemXDirectoryCreationParams] additional parameters +/// should always accept `null` or have a default value to prevent breaking +/// changes. +/// +/// ```dart +/// base class AndroidFileSystemXDirectoryCreationParams +/// extends PlatformFileSystemXDirectoryCreationParams { +/// AndroidFileSystemXDirectoryCreationParams({required super.uri, this.platformValue}); +/// +/// factory AndroidFileSystemXDirectoryCreationParams.fromCreationParams( +/// PlatformFileSystemXDirectoryCreationParams params, { +/// Object? platformValue, +/// }) { +/// return AndroidFileSystemXDirectoryCreationParams( +/// uri: params.uri, +/// platformValue: platformValue, +/// ); +/// } +/// +/// final Object? platformValue; +/// } +/// ``` +@immutable +base class PlatformFileSystemXDirectoryCreationParams extends PlatformXDirectoryCreationParams { + /// Constructs a [PlatformFileSystemXDirectoryCreationParams]. + PlatformFileSystemXDirectoryCreationParams(this.path) : super(uri: Uri.file(path).toString()); + + /// The path of the directory. + final String path; +} + +/// Base mixin used to provide platform-specific features for implementations of +/// [PlatformFileSystemXDirectory]. +/// +/// When providing platform specific features, platform implementations are +/// expected to declare a mixin that implements this mixin and return an +/// instance with [PlatformFileSystemXDirectory.extension]. +/// +/// ```dart +/// base class AndroidFileSystemXDirectory extends PlatformFileSystemXDirectory with AndroidFileSystemXDirectoryExtension { +/// // ... +/// @override +/// PlatformFileSystemXDirectoryExtension? get extension => this; +/// +/// Future platformMethod() { +/// // ... +/// } +/// } +/// +/// mixin AndroidFileSystemXDirectoryExtension implements PlatformFileSystemXDirectoryExtension { +/// Future platformMethod(); +/// } +/// ``` +mixin PlatformFileSystemXDirectoryExtension implements PlatformXDirectoryExtension {} + +/// Interface for a reference to a directory (or folder) on the file system. +abstract base class PlatformFileSystemXDirectory extends PlatformXDirectory { + /// Creates a new [PlatformFileSystemXDirectory] + factory PlatformFileSystemXDirectory(PlatformFileSystemXDirectoryCreationParams params) { + assert( + CrossFilePlatform.instance != null, + 'A platform implementation for `cross_file` has not been set. Please ' + 'ensure that an implementation of `CrossFilePlatform` has been set to ' + '`CrossFilePlatform.instance` before use. For unit testing, ' + '`CrossFilePlatform.instance` can be set with your own test implementation.', + ); + return CrossFilePlatform.instance!.createPlatformFileSystemXDirectory(params); + } + + /// Used by the platform implementation to create a new + /// [PlatformFileSystemXDirectory]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + PlatformFileSystemXDirectory.implementation( + PlatformFileSystemXDirectoryCreationParams super.params, + ); + + @override + PlatformFileSystemXDirectoryCreationParams get params => + super.params as PlatformFileSystemXDirectoryCreationParams; +} diff --git a/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_file.dart b/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_file.dart new file mode 100644 index 000000000000..07432a08a5aa --- /dev/null +++ b/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_file.dart @@ -0,0 +1,114 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; + +import '../cross_file_platform.dart'; +import '../platform_cross_file.dart'; + +/// Object specifying creation parameters for creating a [PlatformFileSystemXFile]. +/// +/// Platform-specific implementations can add additional fields by extending +/// this class. +/// +/// This example demonstrates how to extend the [PlatformFileSystemXFileCreationParams] to +/// provide additional platform-specific parameters. +/// +/// When extending [PlatformFileSystemXFileCreationParams] additional parameters +/// should always accept `null` or have a default value to prevent breaking +/// changes. +/// +/// ```dart +/// base class AndroidFileSystemXFileCreationParams +/// extends PlatformFileSystemXFileCreationParams { +/// AndroidFileSystemXFileCreationParams({required super.uri, this.platformValue}); +/// +/// factory AndroidFileSystemXFileCreationParams.fromCreationParams( +/// PlatformFileSystemXFileCreationParams params, { +/// Object? platformValue, +/// }) { +/// return AndroidFileSystemXFileCreationParams( +/// uri: params.uri, +/// platformValue: platformValue, +/// ); +/// } +/// +/// final Object? platformValue; +/// } +/// ``` +@immutable +base class PlatformFileSystemXFileCreationParams extends PlatformXFileCreationParams { + /// Constructs a [PlatformFileSystemXFileCreationParams]. + PlatformFileSystemXFileCreationParams(this.path) : super(uri: Uri.file(path).toString()); + + /// The path of the file. + final String path; +} + +/// Base mixin used to provide platform-specific features for implementations of +/// [PlatformFileSystemXFile]. +/// +/// When providing platform specific features, platform implementations are +/// expected to declare a mixin that implements this mixin and return an +/// instance with [PlatformFileSystemXFile.extension]. +/// +/// ```dart +/// base class AndroidFileSystemXFile extends PlatformFileSystemXFile with AndroidFileSystemXFileExtension { +/// // ... +/// @override +/// PlatformFileSystemXFileExtension? get extension => this; +/// +/// Future platformMethod() { +/// // ... +/// } +/// } +/// +/// mixin AndroidFileSystemXFileExtension implements PlatformFileSystemXFileExtension { +/// Future platformMethod(); +/// } +/// ``` +mixin PlatformFileSystemXFileExtension implements PlatformXFileExtension {} + +/// Interface for a reference to a local data resource on the file system. +abstract base class PlatformFileSystemXFile extends PlatformXFile { + /// Creates a new [PlatformFileSystemXFile] + factory PlatformFileSystemXFile(PlatformFileSystemXFileCreationParams params) { + assert( + CrossFilePlatform.instance != null, + 'A platform implementation for `cross_file` has not been set. Please ' + 'ensure that an implementation of `CrossFilePlatform` has been set to ' + '`CrossFilePlatform.instance` before use. For unit testing, ' + '`CrossFilePlatform.instance` can be set with your own test implementation.', + ); + return CrossFilePlatform.instance!.createPlatformFileSystemXFile(params); + } + + /// Used by the platform implementation to create a new + /// [PlatformFileSystemXFile]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformFileSystemXFile.implementation(PlatformFileSystemXFileCreationParams super.params); + + @override + PlatformFileSystemXFileCreationParams get params => + super.params as PlatformFileSystemXFileCreationParams; + + /// Writes a list of bytes to a file. + /// + /// Platforms may throw an exception if there is an error opening or writing + /// to the file. + Future writeAsBytes(PlatformWriteAsBytesParams params); +} + +/// Base class for parameters passed to [PlatformFileSystemXFile.writeAsBytes]. +@immutable +base class PlatformWriteAsBytesParams { + /// Constructs a [PlatformWriteAsBytesParams]. + const PlatformWriteAsBytesParams(this.bytes); + + /// List of bytes to write to the file. + final Uint8List bytes; +} diff --git a/packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_directory.dart b/packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_directory.dart new file mode 100644 index 000000000000..0dab8f86ccd5 --- /dev/null +++ b/packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_directory.dart @@ -0,0 +1,92 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/foundation.dart' show immutable, protected; + +import 'platform_cross_entity.dart'; + +/// Object specifying creation parameters for creating a [PlatformXDirectory]. +/// +/// Platform-specific implementations can add additional fields by extending +/// this class. +/// +/// This example demonstrates how to extend the [PlatformXDirectoryCreationParams] to +/// provide additional platform-specific parameters. +/// +/// When extending [PlatformXDirectoryCreationParams] additional parameters +/// should always accept `null` or have a default value to prevent breaking +/// changes. +/// +/// ```dart +/// base class AndroidXDirectoryCreationParams +/// extends PlatformXDirectoryCreationParams { +/// AndroidXDirectoryCreationParams({required super.uri, this.platformValue}); +/// +/// factory AndroidXDirectoryCreationParams.fromCreationParams( +/// PlatformXDirectoryCreationParams params, { +/// Object? platformValue, +/// }) { +/// return AndroidXDirectoryCreationParams( +/// uri: params.uri, +/// platformValue: platformValue, +/// ); +/// } +/// +/// final Object? platformValue; +/// } +/// ``` +@immutable +base class PlatformXDirectoryCreationParams extends PlatformXEntityCreationParams { + /// Constructs a [PlatformXDirectoryCreationParams]. + const PlatformXDirectoryCreationParams({required super.uri}); +} + +/// Base mixin used to provide platform-specific features for implementations of +/// [PlatformXDirectory]. +/// +/// When providing platform specific features, platform implementations are +/// expected to declare a mixin that implements this mixin and return an +/// instance with [PlatformXDirectory.extension]. +/// +/// ```dart +/// base class AndroidXDirectory extends PlatformXDirectory with AndroidXDirectoryExtension { +/// // ... +/// @override +/// PlatformXDirectoryExtension? get extension => this; +/// +/// Future platformMethod() { +/// // ... +/// } +/// } +/// +/// mixin AndroidXDirectoryExtension implements PlatformXDirectoryExtension { +/// Future platformMethod(); +/// } +/// ``` +mixin PlatformXDirectoryExtension implements PlatformXEntityExtension {} + +/// Interface for a reference to a container of local data resources. +abstract base class PlatformXDirectory extends PlatformXEntity { + /// Constructs a [PlatformXDirectory]. + @protected + PlatformXDirectory(PlatformXDirectoryCreationParams super.params); + + @override + PlatformXDirectoryCreationParams get params => super.params as PlatformXDirectoryCreationParams; + + /// Lists the sub-directories and files of this Directory. + /// + /// Platforms may throw an exception if there is an error listing entities in + /// the directory + Stream list(PlatformListParams params); +} + +/// Base class for parameters passed to [PlatformXDirectory.list]. +@immutable +base class PlatformListParams { + /// Constructs a [PlatformListParams]; + const PlatformListParams(); +} diff --git a/packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_entity.dart b/packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_entity.dart new file mode 100644 index 000000000000..707815b4cf43 --- /dev/null +++ b/packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_entity.dart @@ -0,0 +1,36 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart' show immutable, protected; + +/// The common superclass for [PlatformXFileCreationParams] and +/// [PlatformXDirectoryCreationParams]. +@immutable +abstract base class PlatformXEntityCreationParams { + /// Constructs a [PlatformXEntityCreationParams]. + const PlatformXEntityCreationParams({required this.uri}); + + /// A unique string used to identify the resource. + final String uri; +} + +/// The common superclass for [PlatformXFileExtension] and +/// [PlatformXDirectoryExtension]. +mixin PlatformXEntityExtension {} + +/// The common superclass for [PlatformXFile] and [PlatformXDirectory]. +abstract base class PlatformXEntity { + /// Constructs a [PlatformCrossFileEntity]. + @protected + PlatformXEntity(this.params); + + /// The parameters used to initialize the [PlatformXEntity]. + final PlatformXEntityCreationParams params; + + /// Extension for providing platform-specific features. + PlatformXEntityExtension? get extension => null; + + /// Whether the resource represented by this reference exists. + Future exists(); +} diff --git a/packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_file.dart b/packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_file.dart new file mode 100644 index 000000000000..7fe099516fdb --- /dev/null +++ b/packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_file.dart @@ -0,0 +1,122 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:flutter/foundation.dart' show immutable, protected; + +import 'platform_cross_entity.dart'; + +/// Object specifying creation parameters for creating a [PlatformXFile]. +/// +/// Platform-specific implementations can add additional fields by extending +/// this class. +/// +/// This example demonstrates how to extend the [PlatformXFileCreationParams] to +/// provide additional platform-specific parameters. +/// +/// When extending [PlatformXFileCreationParams] additional parameters +/// should always accept `null` or have a default value to prevent breaking +/// changes. +/// +/// ```dart +/// base class AndroidXFileCreationParams +/// extends PlatformXFileCreationParams { +/// AndroidXFileCreationParams({required super.uri, this.platformValue}); +/// +/// factory AndroidXFileCreationParams.fromCreationParams( +/// PlatformXFileCreationParams params, { +/// Object? platformValue, +/// }) { +/// return AndroidXFileCreationParams( +/// uri: params.uri, +/// platformValue: platformValue, +/// ); +/// } +/// +/// final Object? platformValue; +/// } +/// ``` +@immutable +base class PlatformXFileCreationParams extends PlatformXEntityCreationParams { + /// Constructs a [PlatformXFileCreationParams]. + const PlatformXFileCreationParams({required super.uri}); +} + +/// Base mixin used to provide platform-specific features for implementations of +/// [PlatformXFile]. +/// +/// When providing platform specific features, platform implementations are +/// expected to declare a mixin that implements this mixin and return an +/// instance with [PlatformXFile.extension]. +/// +/// ```dart +/// base class AndroidXFile extends PlatformXFile with AndroidXFileExtension { +/// // ... +/// @override +/// PlatformXFileExtension? get extension => this; +/// +/// Future platformMethod() { +/// // ... +/// } +/// } +/// +/// mixin AndroidXFileExtension implements PlatformXFileExtension { +/// Future platformMethod(); +/// } +/// ``` +mixin PlatformXFileExtension implements PlatformXEntityExtension {} + +/// Interface for a reference to a local data resource. +abstract base class PlatformXFile extends PlatformXEntity { + /// Constructs a [PlatformXFile]. + @protected + PlatformXFile(PlatformXFileCreationParams super.params); + + @override + PlatformXFileCreationParams get params => super.params as PlatformXFileCreationParams; + + /// Date and time when the resource was last modified, if the information is + /// available. + /// + /// Returns null if file doesn't exist or information is not available. + Future lastModified(); + + /// The length of the data represented by this uri, in bytes. + /// + /// Returns null if file doesn't exist or information is not available. + Future length(); + + /// Creates a new independent Stream for the contents of this resource. + /// + /// If start is present, the file will be read from byte-offset start. + /// Otherwise from the beginning (index 0). + /// + /// If end is present, only bytes up to byte-index end will be read. + /// Otherwise, until end of file. + /// + /// Platforms may throw an exception if there is an error opening or reading + /// the resource. + Stream openRead([int? start, int? end]); + + /// Reads the entire resource contents as a list of bytes. + /// + /// Platforms may throw an exception if there is an error opening or reading + /// the resource. + Future readAsBytes(); + + /// Reads the entire resource contents as a string using the given Encoding. + /// + /// Platforms may throw an exception if there is an error opening or reading + /// the resource. + Future readAsString({Encoding encoding = utf8}); + + /// The name of the resource represented by this object or null if the file + /// doesn't exist or information is not available. + /// + /// If the file is identified by a path, only the base name of the file will + /// be included in the name. + Future name(); +} diff --git a/packages/cross_file/cross_file_platform_interface/lib/src/scoped_storage/platform_scoped_storage_cross_directory.dart b/packages/cross_file/cross_file_platform_interface/lib/src/scoped_storage/platform_scoped_storage_cross_directory.dart new file mode 100644 index 000000000000..81018d0d6e39 --- /dev/null +++ b/packages/cross_file/cross_file_platform_interface/lib/src/scoped_storage/platform_scoped_storage_cross_directory.dart @@ -0,0 +1,104 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; + +import '../cross_file_platform.dart'; +import '../platform_cross_directory.dart'; + +/// Object specifying creation parameters for creating a [PlatformScopedStorageXDirectory]. +/// +/// Platform-specific implementations can add additional fields by extending +/// this class. +/// +/// This example demonstrates how to extend the [PlatformScopedStorageXDirectoryCreationParams] to +/// provide additional platform-specific parameters. +/// +/// When extending [PlatformScopedStorageXDirectoryCreationParams] additional parameters +/// should always accept `null` or have a default value to prevent breaking +/// changes. +/// +/// ```dart +/// base class AndroidScopedStorageXDirectoryCreationParams +/// extends PlatformScopedStorageXDirectoryCreationParams { +/// AndroidScopedStorageXDirectoryCreationParams({required super.uri, this.platformValue}); +/// +/// factory AndroidScopedStorageXDirectoryCreationParams.fromCreationParams( +/// PlatformScopedStorageXDirectoryCreationParams params, { +/// Object? platformValue, +/// }) { +/// return AndroidScopedStorageXDirectoryCreationParams( +/// uri: params.uri, +/// platformValue: platformValue, +/// ); +/// } +/// +/// final Object? platformValue; +/// } +/// ``` +@immutable +base class PlatformScopedStorageXDirectoryCreationParams extends PlatformXDirectoryCreationParams { + /// Constructs a [PlatformScopedStorageXDirectoryCreationParams]. + const PlatformScopedStorageXDirectoryCreationParams({required super.uri}); +} + +/// Base mixin used to provide platform-specific features for implementations of +/// [PlatformScopedStorageXDirectory]. +/// +/// When providing platform specific features, platform implementations are +/// expected to declare a mixin that implements this mixin and return an +/// instance with [PlatformScopedStorageXDirectory.extension]. +/// +/// ```dart +/// base class AndroidScopedStorageXDirectory extends PlatformScopedStorageXDirectory with AndroidScopedStorageXDirectoryExtension { +/// // ... +/// @override +/// PlatformScopedStorageXDirectoryExtension? get extension => this; +/// +/// Future platformMethod() { +/// // ... +/// } +/// } +/// +/// mixin AndroidScopedStorageXDirectoryExtension implements PlatformScopedStorageXDirectoryExtension { +/// Future platformMethod(); +/// } +/// ``` +mixin PlatformScopedStorageXDirectoryExtension implements PlatformXDirectoryExtension {} + +/// Interface for a reference to a directory (or folder) within a device's +/// scoped storage. +abstract base class PlatformScopedStorageXDirectory extends PlatformXDirectory { + /// Creates a new [PlatformScopedStorageXDirectory] + factory PlatformScopedStorageXDirectory(PlatformScopedStorageXDirectoryCreationParams params) { + assert( + CrossFilePlatform.instance != null, + 'A platform implementation for `cross_file` has not been set. Please ' + 'ensure that an implementation of `CrossFilePlatform` has been set to ' + '`CrossFilePlatform.instance` before use. For unit testing, ' + '`CrossFilePlatform.instance` can be set with your own test implementation.', + ); + return CrossFilePlatform.instance!.createPlatformScopedStorageXDirectory(params); + } + + /// Used by the platform implementation to create a new + /// [PlatformScopedStorageXDirectory]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + PlatformScopedStorageXDirectory.implementation( + PlatformScopedStorageXDirectoryCreationParams super.params, + ); + + @override + PlatformScopedStorageXDirectoryCreationParams get params => + super.params as PlatformScopedStorageXDirectoryCreationParams; + + /// Whether the files in this directory represented by this reference can be + /// viewed. + Future canRead(); + + /// Release the reference this represents. + Future dispose(); +} diff --git a/packages/cross_file/cross_file_platform_interface/lib/src/scoped_storage/platform_scoped_storage_cross_file.dart b/packages/cross_file/cross_file_platform_interface/lib/src/scoped_storage/platform_scoped_storage_cross_file.dart new file mode 100644 index 000000000000..fbb4984f7d60 --- /dev/null +++ b/packages/cross_file/cross_file_platform_interface/lib/src/scoped_storage/platform_scoped_storage_cross_file.dart @@ -0,0 +1,102 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; + +import '../cross_file_platform.dart'; +import '../platform_cross_file.dart'; + +/// Object specifying creation parameters for creating a [PlatformScopedStorageXFile]. +/// +/// Platform-specific implementations can add additional fields by extending +/// this class. +/// +/// This example demonstrates how to extend the [PlatformScopedStorageXFileCreationParams] to +/// provide additional platform-specific parameters. +/// +/// When extending [PlatformScopedStorageXFileCreationParams] additional parameters +/// should always accept `null` or have a default value to prevent breaking +/// changes. +/// +/// ```dart +/// base class AndroidScopedStorageXFileCreationParams +/// extends PlatformScopedStorageXFileCreationParams { +/// AndroidScopedStorageXFileCreationParams({required super.uri, this.platformValue}); +/// +/// factory AndroidScopedStorageXFileCreationParams.fromCreationParams( +/// PlatformScopedStorageXFileCreationParams params, { +/// Object? platformValue, +/// }) { +/// return AndroidScopedStorageXFileCreationParams( +/// uri: params.uri, +/// platformValue: platformValue, +/// ); +/// } +/// +/// final Object? platformValue; +/// } +/// ``` +@immutable +base class PlatformScopedStorageXFileCreationParams extends PlatformXFileCreationParams { + /// Constructs a [PlatformScopedStorageXFileCreationParams]. + const PlatformScopedStorageXFileCreationParams({required super.uri}); +} + +/// Base mixin used to provide platform-specific features for implementations of +/// [PlatformScopedStorageXFile]. +/// +/// When providing platform specific features, platform implementations are +/// expected to declare a mixin that implements this mixin and return an +/// instance with [PlatformScopedStorageXFile.extension]. +/// +/// ```dart +/// base class AndroidScopedStorageXFile extends PlatformScopedStorageXFile with AndroidScopedStorageXFileExtension { +/// // ... +/// @override +/// PlatformScopedStorageXFileExtension? get extension => this; +/// +/// Future platformMethod() { +/// // ... +/// } +/// } +/// +/// mixin AndroidScopedStorageXFileExtension implements PlatformScopedStorageXFileExtension { +/// Future platformMethod(); +/// } +/// ``` +mixin PlatformScopedStorageXFileExtension implements PlatformXFileExtension {} + +/// Interface for a reference to a data resource within a device's scoped +/// storage. +abstract base class PlatformScopedStorageXFile extends PlatformXFile { + /// Creates a new [PlatformScopedStorageXFile] + factory PlatformScopedStorageXFile(PlatformScopedStorageXFileCreationParams params) { + assert( + CrossFilePlatform.instance != null, + 'A platform implementation for `cross_file` has not been set. Please ' + 'ensure that an implementation of `CrossFilePlatform` has been set to ' + '`CrossFilePlatform.instance` before use. For unit testing, ' + '`CrossFilePlatform.instance` can be set with your own test implementation.', + ); + return CrossFilePlatform.instance!.createPlatformScopedStorageXFile(params); + } + + /// Used by the platform implementation to create a new + /// [PlatformScopedStorageXFile]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformScopedStorageXFile.implementation(PlatformScopedStorageXFileCreationParams super.params); + + @override + PlatformScopedStorageXFileCreationParams get params => + super.params as PlatformScopedStorageXFileCreationParams; + + /// Whether the resource represented by this reference can be read. + Future canRead(); + + /// Release the reference this represents. + Future dispose(); +} diff --git a/packages/cross_file/cross_file_platform_interface/pubspec.yaml b/packages/cross_file/cross_file_platform_interface/pubspec.yaml new file mode 100644 index 000000000000..58ecfd9b7a73 --- /dev/null +++ b/packages/cross_file/cross_file_platform_interface/pubspec.yaml @@ -0,0 +1,24 @@ +name: cross_file_platform_interface +description: A common platform interface for the cross_file plugin. +repository: https://github.com/flutter/packages/tree/main/packages/cross_file/cross_file_platform_interface +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+cross_file%22 +# NOTE: We strongly prefer non-breaking changes, even at the expense of a +# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes +version: 1.0.0 +publish_to: none + +environment: + sdk: ^3.10.0 + flutter: ">=3.38.0" + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + +topics: + - files + - cross-file diff --git a/packages/cross_file/cross_file_platform_interface/test/cross_file_platform_test.dart b/packages/cross_file/cross_file_platform_interface/test/cross_file_platform_test.dart new file mode 100644 index 000000000000..6bf6ac8bb66e --- /dev/null +++ b/packages/cross_file/cross_file_platform_interface/test/cross_file_platform_test.dart @@ -0,0 +1,117 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:cross_file_platform_interface/cross_file_platform_interface.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('CrossFilePlatform', () { + group('FileSystem', () { + test('_DefaultFileSystemXFile.exists() returns false', () async { + final platform = TestCrossFilePlatform(); + + expect( + await platform + .createPlatformFileSystemXFile(PlatformFileSystemXFileCreationParams('test')) + .exists(), + false, + ); + }); + + test('_DefaultFileSystemXFile.openRead should throw error by adding it to stream', () async { + final platform = TestCrossFilePlatform(); + + final PlatformFileSystemXFile file = platform.createPlatformFileSystemXFile( + PlatformFileSystemXFileCreationParams('test'), + ); + + // Ensures the error is caught and added to the stream. + await expectLater(file.openRead().drain, throwsUnsupportedError); + }); + + test('_DefaultFileSystemXDirectory.exists() returns false', () async { + final platform = TestCrossFilePlatform(); + + expect( + await platform + .createPlatformFileSystemXDirectory( + PlatformFileSystemXDirectoryCreationParams('test'), + ) + .exists(), + false, + ); + }); + + test('_DefaultFileSystemXDirectory.list should throw error by adding it to stream', () async { + final platform = TestCrossFilePlatform(); + + final PlatformFileSystemXDirectory dir = platform.createPlatformFileSystemXDirectory( + PlatformFileSystemXDirectoryCreationParams('test'), + ); + + // Ensures the error is caught and added to the stream. + await expectLater(dir.list(const PlatformListParams()).drain, throwsUnsupportedError); + }); + }); + + group('ScopedStorage', () { + test('_DefaultScopedStorageXFile.exists() returns false', () async { + final platform = TestCrossFilePlatform(); + + expect( + await platform + .createPlatformScopedStorageXFile( + const PlatformScopedStorageXFileCreationParams(uri: 'test'), + ) + .exists(), + false, + ); + }); + + test( + '_DefaultScopedStorageXFile.openRead should throw error by adding it to stream', + () async { + final platform = TestCrossFilePlatform(); + + final PlatformScopedStorageXFile file = platform.createPlatformScopedStorageXFile( + const PlatformScopedStorageXFileCreationParams(uri: 'test'), + ); + + // Ensures the error is caught and added to the stream. + await expectLater(file.openRead().drain, throwsUnsupportedError); + }, + ); + + test('_DefaultScopedStorageXDirectory.exists() returns false', () async { + final platform = TestCrossFilePlatform(); + + expect( + await platform + .createPlatformScopedStorageXDirectory( + const PlatformScopedStorageXDirectoryCreationParams(uri: 'test'), + ) + .exists(), + false, + ); + }); + + test( + '_DefaultScopedStorageXDirectory.list should throw error by adding it to stream', + () async { + final platform = TestCrossFilePlatform(); + + final PlatformScopedStorageXDirectory dir = platform + .createPlatformScopedStorageXDirectory( + const PlatformScopedStorageXDirectoryCreationParams(uri: 'test'), + ); + + // Ensures the error is caught and added to the stream. + await expectLater(dir.list(const PlatformListParams()).drain, throwsUnsupportedError); + }, + ); + }); + }); +} + +final class TestCrossFilePlatform extends CrossFilePlatform {} From 7068e73c8f917274d9fb88bc092cd5af9e772338 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 10 Sep 2026 14:25:03 -0400 Subject: [PATCH 2/6] remove publish to --- packages/cross_file/cross_file_platform_interface/pubspec.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cross_file/cross_file_platform_interface/pubspec.yaml b/packages/cross_file/cross_file_platform_interface/pubspec.yaml index 58ecfd9b7a73..ec9eb9ec277a 100644 --- a/packages/cross_file/cross_file_platform_interface/pubspec.yaml +++ b/packages/cross_file/cross_file_platform_interface/pubspec.yaml @@ -5,7 +5,6 @@ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+ # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes version: 1.0.0 -publish_to: none environment: sdk: ^3.10.0 From 3bacba8a27af1c432955f6b9ec35d114dd1d4d6c Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 10 Sep 2026 14:29:22 -0400 Subject: [PATCH 3/6] fix doc comments --- .../lib/src/cross_file_platform.dart | 12 ++++++------ .../platform_file_system_cross_directory.dart | 1 + .../lib/src/platform_cross_entity.dart | 2 +- .../platform_scoped_storage_cross_directory.dart | 1 + 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/cross_file/cross_file_platform_interface/lib/src/cross_file_platform.dart b/packages/cross_file/cross_file_platform_interface/lib/src/cross_file_platform.dart index 8500c179626e..80d2c9e2e4e4 100644 --- a/packages/cross_file/cross_file_platform_interface/lib/src/cross_file_platform.dart +++ b/packages/cross_file/cross_file_platform_interface/lib/src/cross_file_platform.dart @@ -21,7 +21,7 @@ abstract base class CrossFilePlatform { /// implementation of [CrossFilePlatform] when they register themselves. static CrossFilePlatform? instance; - /// Creates a new [PlatformXFile]. + /// Creates a new [PlatformFileSystemXFile]. PlatformFileSystemXFile createPlatformFileSystemXFile( PlatformFileSystemXFileCreationParams params, ) { @@ -35,7 +35,7 @@ abstract base class CrossFilePlatform { return _DefaultFileSystemXDirectory(params); } - /// Creates a new [PlatformScopedStorageXDirectory]. + /// Creates a new [PlatformScopedStorageXFile]. PlatformScopedStorageXFile createPlatformScopedStorageXFile( PlatformScopedStorageXFileCreationParams params, ) { @@ -50,7 +50,7 @@ abstract base class CrossFilePlatform { } } -/// Implementation of [PlatformFileSystemXFile} that represents a resource that +/// Implementation of [PlatformFileSystemXFile] that represents a resource that /// does not exist. final class _DefaultFileSystemXFile extends PlatformFileSystemXFile { _DefaultFileSystemXFile(super.params) : super.implementation(); @@ -88,7 +88,7 @@ final class _DefaultFileSystemXFile extends PlatformFileSystemXFile { } } -/// Implementation of [PlatformFileSystemXDirectory} that represents a directory +/// Implementation of [PlatformFileSystemXDirectory] that represents a directory /// that does not exist. final class _DefaultFileSystemXDirectory extends PlatformFileSystemXDirectory { _DefaultFileSystemXDirectory(super.params) : super.implementation(); @@ -102,7 +102,7 @@ final class _DefaultFileSystemXDirectory extends PlatformFileSystemXDirectory { } } -/// Implementation of [PlatformScopedStorageXFile} that represents a resource +/// Implementation of [PlatformScopedStorageXFile] that represents a resource /// that does not exist. final class _DefaultScopedStorageXFile extends PlatformScopedStorageXFile { _DefaultScopedStorageXFile(super.params) : super.implementation(); @@ -141,7 +141,7 @@ final class _DefaultScopedStorageXFile extends PlatformScopedStorageXFile { Future dispose() async {} } -/// Implementation of [PlatformScopedStorageXDirectory} that represents a +/// Implementation of [PlatformScopedStorageXDirectory] that represents a /// directory that does not exist. final class _DefaultScopedStorageXDirectory extends PlatformScopedStorageXDirectory { _DefaultScopedStorageXDirectory(super.params) : super.implementation(); diff --git a/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_directory.dart b/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_directory.dart index fb856bed4d1a..004dde9628a1 100644 --- a/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_directory.dart +++ b/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_directory.dart @@ -89,6 +89,7 @@ abstract base class PlatformFileSystemXDirectory extends PlatformXDirectory { /// /// Should only be used by platform implementations because they can't extend /// a class that only contains a factory constructor. + @protected PlatformFileSystemXDirectory.implementation( PlatformFileSystemXDirectoryCreationParams super.params, ); diff --git a/packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_entity.dart b/packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_entity.dart index 707815b4cf43..c9613ae0932e 100644 --- a/packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_entity.dart +++ b/packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_entity.dart @@ -21,7 +21,7 @@ mixin PlatformXEntityExtension {} /// The common superclass for [PlatformXFile] and [PlatformXDirectory]. abstract base class PlatformXEntity { - /// Constructs a [PlatformCrossFileEntity]. + /// Constructs a [PlatformXEntity]. @protected PlatformXEntity(this.params); diff --git a/packages/cross_file/cross_file_platform_interface/lib/src/scoped_storage/platform_scoped_storage_cross_directory.dart b/packages/cross_file/cross_file_platform_interface/lib/src/scoped_storage/platform_scoped_storage_cross_directory.dart index 81018d0d6e39..ca75668011d7 100644 --- a/packages/cross_file/cross_file_platform_interface/lib/src/scoped_storage/platform_scoped_storage_cross_directory.dart +++ b/packages/cross_file/cross_file_platform_interface/lib/src/scoped_storage/platform_scoped_storage_cross_directory.dart @@ -87,6 +87,7 @@ abstract base class PlatformScopedStorageXDirectory extends PlatformXDirectory { /// /// Should only be used by platform implementations because they can't extend /// a class that only contains a factory constructor. + @protected PlatformScopedStorageXDirectory.implementation( PlatformScopedStorageXDirectoryCreationParams super.params, ); From 05f83268610e5e90150a97456e21104191fca31f Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 10 Sep 2026 14:49:04 -0400 Subject: [PATCH 4/6] fix uri creation for file system --- .../file_system/platform_file_system_cross_directory.dart | 8 +++++++- .../src/file_system/platform_file_system_cross_file.dart | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_directory.dart b/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_directory.dart index 004dde9628a1..f30a6cb2e61f 100644 --- a/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_directory.dart +++ b/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_directory.dart @@ -40,7 +40,13 @@ import '../platform_cross_directory.dart'; @immutable base class PlatformFileSystemXDirectoryCreationParams extends PlatformXDirectoryCreationParams { /// Constructs a [PlatformFileSystemXDirectoryCreationParams]. - PlatformFileSystemXDirectoryCreationParams(this.path) : super(uri: Uri.file(path).toString()); + PlatformFileSystemXDirectoryCreationParams(this.path) + : super( + uri: Uri.directory( + path, + windows: defaultTargetPlatform == TargetPlatform.windows, + ).toString(), + ); /// The path of the directory. final String path; diff --git a/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_file.dart b/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_file.dart index 07432a08a5aa..7a3fa0ac934e 100644 --- a/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_file.dart +++ b/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_file.dart @@ -40,7 +40,10 @@ import '../platform_cross_file.dart'; @immutable base class PlatformFileSystemXFileCreationParams extends PlatformXFileCreationParams { /// Constructs a [PlatformFileSystemXFileCreationParams]. - PlatformFileSystemXFileCreationParams(this.path) : super(uri: Uri.file(path).toString()); + PlatformFileSystemXFileCreationParams(this.path) + : super( + uri: Uri.file(path, windows: defaultTargetPlatform == TargetPlatform.windows).toString(), + ); /// The path of the file. final String path; From d8cbef4b67bffb6127707dbd6b181edb7f08a157 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 10 Sep 2026 15:23:23 -0400 Subject: [PATCH 5/6] tests and improved extension requirements --- .../platform_file_system_cross_directory.dart | 4 ++ .../platform_file_system_cross_file.dart | 4 ++ .../lib/src/platform_cross_directory.dart | 4 ++ .../lib/src/platform_cross_file.dart | 4 ++ ...atform_scoped_storage_cross_directory.dart | 4 ++ .../platform_scoped_storage_cross_file.dart | 4 ++ .../test/cross_file_platform_test.dart | 42 +++++++++++++++++++ 7 files changed, 66 insertions(+) diff --git a/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_directory.dart b/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_directory.dart index f30a6cb2e61f..bca36a6ae026 100644 --- a/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_directory.dart +++ b/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_directory.dart @@ -103,4 +103,8 @@ abstract base class PlatformFileSystemXDirectory extends PlatformXDirectory { @override PlatformFileSystemXDirectoryCreationParams get params => super.params as PlatformFileSystemXDirectoryCreationParams; + + /// Extension for providing platform-specific features. + @override + PlatformFileSystemXDirectoryExtension? get extension => null; } diff --git a/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_file.dart b/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_file.dart index 7a3fa0ac934e..156aeda9aac6 100644 --- a/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_file.dart +++ b/packages/cross_file/cross_file_platform_interface/lib/src/file_system/platform_file_system_cross_file.dart @@ -99,6 +99,10 @@ abstract base class PlatformFileSystemXFile extends PlatformXFile { PlatformFileSystemXFileCreationParams get params => super.params as PlatformFileSystemXFileCreationParams; + /// Extension for providing platform-specific features. + @override + PlatformFileSystemXFileExtension? get extension => null; + /// Writes a list of bytes to a file. /// /// Platforms may throw an exception if there is an error opening or writing diff --git a/packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_directory.dart b/packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_directory.dart index 0dab8f86ccd5..ed1097522b3a 100644 --- a/packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_directory.dart +++ b/packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_directory.dart @@ -77,6 +77,10 @@ abstract base class PlatformXDirectory extends PlatformXEntity { @override PlatformXDirectoryCreationParams get params => super.params as PlatformXDirectoryCreationParams; + /// Extension for providing platform-specific features. + @override + PlatformXDirectoryExtension? get extension => null; + /// Lists the sub-directories and files of this Directory. /// /// Platforms may throw an exception if there is an error listing entities in diff --git a/packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_file.dart b/packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_file.dart index 7fe099516fdb..34c8657b4369 100644 --- a/packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_file.dart +++ b/packages/cross_file/cross_file_platform_interface/lib/src/platform_cross_file.dart @@ -78,6 +78,10 @@ abstract base class PlatformXFile extends PlatformXEntity { @override PlatformXFileCreationParams get params => super.params as PlatformXFileCreationParams; + /// Extension for providing platform-specific features. + @override + PlatformXFileExtension? get extension => null; + /// Date and time when the resource was last modified, if the information is /// available. /// diff --git a/packages/cross_file/cross_file_platform_interface/lib/src/scoped_storage/platform_scoped_storage_cross_directory.dart b/packages/cross_file/cross_file_platform_interface/lib/src/scoped_storage/platform_scoped_storage_cross_directory.dart index ca75668011d7..10accdcbfbb7 100644 --- a/packages/cross_file/cross_file_platform_interface/lib/src/scoped_storage/platform_scoped_storage_cross_directory.dart +++ b/packages/cross_file/cross_file_platform_interface/lib/src/scoped_storage/platform_scoped_storage_cross_directory.dart @@ -96,6 +96,10 @@ abstract base class PlatformScopedStorageXDirectory extends PlatformXDirectory { PlatformScopedStorageXDirectoryCreationParams get params => super.params as PlatformScopedStorageXDirectoryCreationParams; + /// Extension for providing platform-specific features. + @override + PlatformScopedStorageXDirectoryExtension? get extension => null; + /// Whether the files in this directory represented by this reference can be /// viewed. Future canRead(); diff --git a/packages/cross_file/cross_file_platform_interface/lib/src/scoped_storage/platform_scoped_storage_cross_file.dart b/packages/cross_file/cross_file_platform_interface/lib/src/scoped_storage/platform_scoped_storage_cross_file.dart index fbb4984f7d60..edab5cfbb566 100644 --- a/packages/cross_file/cross_file_platform_interface/lib/src/scoped_storage/platform_scoped_storage_cross_file.dart +++ b/packages/cross_file/cross_file_platform_interface/lib/src/scoped_storage/platform_scoped_storage_cross_file.dart @@ -90,6 +90,10 @@ abstract base class PlatformScopedStorageXFile extends PlatformXFile { @protected PlatformScopedStorageXFile.implementation(PlatformScopedStorageXFileCreationParams super.params); + /// Extension for providing platform-specific features. + @override + PlatformScopedStorageXFileExtension? get extension => null; + @override PlatformScopedStorageXFileCreationParams get params => super.params as PlatformScopedStorageXFileCreationParams; diff --git a/packages/cross_file/cross_file_platform_interface/test/cross_file_platform_test.dart b/packages/cross_file/cross_file_platform_interface/test/cross_file_platform_test.dart index 6bf6ac8bb66e..023b6cdbdcf6 100644 --- a/packages/cross_file/cross_file_platform_interface/test/cross_file_platform_test.dart +++ b/packages/cross_file/cross_file_platform_interface/test/cross_file_platform_test.dart @@ -8,6 +8,48 @@ import 'package:flutter_test/flutter_test.dart'; void main() { group('CrossFilePlatform', () { group('FileSystem', () { + test('PlatformFileSystemXFile returns correct uri', () { + final platform = TestCrossFilePlatform(); + + expect( + platform + .createPlatformFileSystemXFile(PlatformFileSystemXFileCreationParams('my/path.txt')) + .params + .uri, + 'my/path.txt', + ); + + expect( + platform + .createPlatformFileSystemXFile(PlatformFileSystemXFileCreationParams('/my/path.txt')) + .params + .uri, + 'file:///my/path.txt', + ); + }); + + test('PlatformFileSystemXDirectory returns correct URI', () { + final platform = TestCrossFilePlatform(); + + expect( + platform + .createPlatformFileSystemXDirectory( + PlatformFileSystemXDirectoryCreationParams('my/path'), + ) + .params + .uri, + 'my/path/', + ); + + expect( + platform + .createPlatformFileSystemXFile(PlatformFileSystemXFileCreationParams('/my/path/')) + .params + .uri, + 'file:///my/path/', + ); + }); + test('_DefaultFileSystemXFile.exists() returns false', () async { final platform = TestCrossFilePlatform(); From 2403174e26eb019814fc62b8d032f9e7f2d964a0 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Fri, 11 Sep 2026 14:51:41 -0400 Subject: [PATCH 6/6] unused import --- .../lib/src/cross_file_platform.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cross_file/cross_file_platform_interface/lib/src/cross_file_platform.dart b/packages/cross_file/cross_file_platform_interface/lib/src/cross_file_platform.dart index 80d2c9e2e4e4..244c48ab8aad 100644 --- a/packages/cross_file/cross_file_platform_interface/lib/src/cross_file_platform.dart +++ b/packages/cross_file/cross_file_platform_interface/lib/src/cross_file_platform.dart @@ -9,7 +9,6 @@ import 'file_system/platform_file_system_cross_directory.dart'; import 'file_system/platform_file_system_cross_file.dart'; import 'platform_cross_directory.dart'; import 'platform_cross_entity.dart'; -import 'platform_cross_file.dart'; import 'scoped_storage/platform_scoped_storage_cross_directory.dart'; import 'scoped_storage/platform_scoped_storage_cross_file.dart';