[file_selector_platform_interface] Migrate to cross_file package (#3286)

diff --git a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md
index 3663064..7f1d573 100644
--- a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md
+++ b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.0.2
+
+* Replace locally defined `XFile` types with the versions from the [cross_file](https://pub.dev/packages/cross_file) package.
+
 ## 1.0.1
 
 * Allow type groups that allow any file.
diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart
index 8681a1d..586b1ab 100644
--- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart
+++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'package:cross_file/cross_file.dart';
 import 'package:flutter/services.dart';
 
 import 'package:file_selector_platform_interface/file_selector_platform_interface.dart';
diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart
index cf23d5f..e7b3263 100644
--- a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart
+++ b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart
@@ -4,6 +4,7 @@
 
 import 'dart:async';
 
+import 'package:cross_file/cross_file.dart';
 import 'package:file_selector_platform_interface/file_selector_platform_interface.dart';
 import 'package:plugin_platform_interface/plugin_platform_interface.dart';
 
diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart
index 8848c67..88dc3c2 100644
--- a/packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart
+++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart
@@ -1,3 +1,2 @@
-export 'x_file/x_file.dart';
-
+export 'package:cross_file/cross_file.dart';
 export 'x_type_group/x_type_group.dart';
diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart
deleted file mode 100644
index 7ea050f..0000000
--- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart
+++ /dev/null
@@ -1,82 +0,0 @@
-import 'dart:convert';
-import 'dart:typed_data';
-
-/// The interface for a XFile.
-///
-/// A XFile is a container that wraps the path of a selected
-/// file by the user and (in some platforms, like web) the bytes
-/// with the contents of the file.
-///
-/// This class is a very limited subset of dart:io [File], so all
-/// the methods should seem familiar.
-abstract class XFileBase {
-  /// Construct a XFile
-  XFileBase(String path);
-
-  /// Save the XFile at the indicated file path.
-  void saveTo(String path) async {
-    throw UnimplementedError('saveTo has not been implemented.');
-  }
-
-  /// Get the path of the picked file.
-  ///
-  /// This should only be used as a backwards-compatibility clutch
-  /// for mobile apps, or cosmetic reasons only (to show the user
-  /// the path they've picked).
-  ///
-  /// Accessing the data contained in the picked file by its path
-  /// is platform-dependant (and won't work on web), so use the
-  /// byte getters in the XFile instance instead.
-  String get path {
-    throw UnimplementedError('.path has not been implemented.');
-  }
-
-  /// The name of the file as it was selected by the user in their device.
-  ///
-  /// Use only for cosmetic reasons, do not try to use this as a path.
-  String get name {
-    throw UnimplementedError('.name has not been implemented.');
-  }
-
-  /// For web, it may be necessary for a file to know its MIME type.
-  String get mimeType {
-    throw UnimplementedError('.mimeType has not been implemented.');
-  }
-
-  /// Get the length of the file. Returns a `Future<int>` that completes with the length in bytes.
-  Future<int> length() {
-    throw UnimplementedError('.length() has not been implemented.');
-  }
-
-  /// Synchronously read the entire file contents as a string using the given [Encoding].
-  ///
-  /// By default, `encoding` is [utf8].
-  ///
-  /// Throws Exception if the operation fails.
-  Future<String> readAsString({Encoding encoding = utf8}) {
-    throw UnimplementedError('readAsString() has not been implemented.');
-  }
-
-  /// Synchronously read the entire file contents as a list of bytes.
-  ///
-  /// Throws Exception if the operation fails.
-  Future<Uint8List> readAsBytes() {
-    throw UnimplementedError('readAsBytes() has not been implemented.');
-  }
-
-  /// Create a new independent [Stream] for the contents of this file.
-  ///
-  /// If `start` is present, the file will be read from byte-offset `start`. Otherwise from the beginning (index 0).
-  ///
-  /// If `end` is present, only up to byte-index `end` will be read. Otherwise, until end of file.
-  ///
-  /// In order to make sure that system resources are freed, the stream must be read to completion or the subscription on the stream must be cancelled.
-  Stream<Uint8List> openRead([int start, int end]) {
-    throw UnimplementedError('openRead() has not been implemented.');
-  }
-
-  /// Get the last-modified time for the XFile
-  Future<DateTime> lastModified() {
-    throw UnimplementedError('openRead() has not been implemented.');
-  }
-}
diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart
deleted file mode 100644
index fe898eb..0000000
--- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart
+++ /dev/null
@@ -1,132 +0,0 @@
-import 'dart:convert';
-import 'dart:typed_data';
-
-import 'package:http/http.dart' as http show readBytes;
-import 'package:meta/meta.dart';
-import 'dart:html';
-
-import '../../web_helpers/web_helpers.dart';
-import './base.dart';
-
-/// A XFile that works on web.
-///
-/// It wraps the bytes of a selected file.
-class XFile extends XFileBase {
-  String path;
-
-  final String mimeType;
-  final Uint8List _data;
-  final int _length;
-  final String name;
-  final DateTime _lastModified;
-  Element _target;
-
-  final XFileTestOverrides _overrides;
-
-  bool get _hasTestOverrides => _overrides != null;
-
-  /// Construct a XFile object from its ObjectUrl.
-  ///
-  /// Optionally, this can be initialized with `bytes` and `length`
-  /// so no http requests are performed to retrieve files later.
-  ///
-  /// `name` needs to be passed from the outside, since we only have
-  /// access to it while we create the ObjectUrl.
-  XFile(
-    this.path, {
-    this.mimeType,
-    this.name,
-    int length,
-    Uint8List bytes,
-    DateTime lastModified,
-    @visibleForTesting XFileTestOverrides overrides,
-  })  : _data = bytes,
-        _length = length,
-        _overrides = overrides,
-        _lastModified = lastModified,
-        super(path);
-
-  /// Construct an XFile from its data
-  XFile.fromData(
-    Uint8List bytes, {
-    this.mimeType,
-    this.name,
-    int length,
-    DateTime lastModified,
-    this.path,
-    @visibleForTesting XFileTestOverrides overrides,
-  })  : _data = bytes,
-        _length = length,
-        _overrides = overrides,
-        _lastModified = lastModified,
-        super(path) {
-    if (path == null) {
-      final blob = (mimeType == null) ? Blob([bytes]) : Blob([bytes], mimeType);
-      this.path = Url.createObjectUrl(blob);
-    }
-  }
-
-  @override
-  Future<DateTime> lastModified() async {
-    if (_lastModified != null) {
-      return Future.value(_lastModified);
-    }
-    return null;
-  }
-
-  Future<Uint8List> get _bytes async {
-    if (_data != null) {
-      return Future.value(UnmodifiableUint8ListView(_data));
-    }
-    return http.readBytes(path);
-  }
-
-  @override
-  Future<int> length() async {
-    return _length ?? (await _bytes).length;
-  }
-
-  @override
-  Future<String> readAsString({Encoding encoding = utf8}) async {
-    return encoding.decode(await _bytes);
-  }
-
-  @override
-  Future<Uint8List> readAsBytes() async {
-    return Future.value(await _bytes);
-  }
-
-  @override
-  Stream<Uint8List> openRead([int start, int end]) async* {
-    final bytes = await _bytes;
-    yield bytes.sublist(start ?? 0, end ?? bytes.length);
-  }
-
-  /// Saves the data of this XFile at the location indicated by path.
-  /// For the web implementation, the path variable is ignored.
-  void saveTo(String path) async {
-    // Create a DOM container where we can host the anchor.
-    _target = ensureInitialized('__x_file_dom_element');
-
-    // Create an <a> tag with the appropriate download attributes and click it
-    // May be overridden with XFileTestOverrides
-    final AnchorElement element =
-        (_hasTestOverrides && _overrides.createAnchorElement != null)
-            ? _overrides.createAnchorElement(this.path, this.name)
-            : createAnchorElement(this.path, this.name);
-
-    // Clear the children in our container so we can add an element to click
-    _target.children.clear();
-    addElementToContainerAndClick(_target, element);
-  }
-}
-
-/// Overrides some functions to allow testing
-@visibleForTesting
-class XFileTestOverrides {
-  /// For overriding the creation of the file input element.
-  Element Function(String href, String suggestedName) createAnchorElement;
-
-  /// Default constructor for overrides
-  XFileTestOverrides({this.createAnchorElement});
-}
diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/interface.dart
deleted file mode 100644
index f5fe388..0000000
--- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/interface.dart
+++ /dev/null
@@ -1,54 +0,0 @@
-import 'dart:typed_data';
-import 'package:meta/meta.dart';
-
-import './base.dart';
-
-/// A XFile is a cross-platform, simplified File abstraction.
-///
-/// It wraps the bytes of a selected file, and its (platform-dependant) path.
-class XFile extends XFileBase {
-  /// Construct a XFile object from its path.
-  ///
-  /// Optionally, this can be initialized with `bytes` and `length`
-  /// so no http requests are performed to retrieve data later.
-  ///
-  /// `name` may be passed from the outside, for those cases where the effective
-  /// `path` of the file doesn't match what the user sees when selecting it
-  /// (like in web)
-  XFile(
-    String path, {
-    String mimeType,
-    String name,
-    int length,
-    Uint8List bytes,
-    DateTime lastModified,
-    @visibleForTesting XFileTestOverrides overrides,
-  }) : super(path) {
-    throw UnimplementedError(
-        'XFile is not available in your current platform.');
-  }
-
-  /// Construct a XFile object from its data
-  XFile.fromData(
-    Uint8List bytes, {
-    String mimeType,
-    String name,
-    int length,
-    DateTime lastModified,
-    String path,
-    @visibleForTesting XFileTestOverrides overrides,
-  }) : super(path) {
-    throw UnimplementedError(
-        'XFile is not available in your current platform.');
-  }
-}
-
-/// Overrides some functions of XFile for testing purposes
-@visibleForTesting
-class XFileTestOverrides {
-  /// For overriding the creation of the file input element.
-  dynamic Function(String href, String suggestedName) createAnchorElement;
-
-  /// Default constructor for overrides
-  XFileTestOverrides({this.createAnchorElement});
-}
diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart
deleted file mode 100644
index 753732d..0000000
--- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart
+++ /dev/null
@@ -1,111 +0,0 @@
-import 'dart:convert';
-import 'dart:io';
-import 'dart:typed_data';
-
-import './base.dart';
-
-/// A XFile backed by a dart:io File.
-class XFile extends XFileBase {
-  final File _file;
-  final String mimeType;
-  final DateTime _lastModified;
-  int _length;
-
-  final Uint8List _bytes;
-
-  /// Construct a XFile object backed by a dart:io File.
-  XFile(
-    String path, {
-    this.mimeType,
-    String name,
-    int length,
-    Uint8List bytes,
-    DateTime lastModified,
-  })  : _file = File(path),
-        _bytes = null,
-        _lastModified = lastModified,
-        super(path);
-
-  /// Construct an XFile from its data
-  XFile.fromData(
-    Uint8List bytes, {
-    this.mimeType,
-    String path,
-    String name,
-    int length,
-    DateTime lastModified,
-  })  : _bytes = bytes,
-        _file = File(path ?? ''),
-        _length = length,
-        _lastModified = lastModified,
-        super(path) {
-    if (length == null) {
-      _length = bytes.length;
-    }
-  }
-
-  @override
-  Future<DateTime> lastModified() {
-    if (_lastModified != null) {
-      return Future.value(_lastModified);
-    }
-    return _file.lastModified();
-  }
-
-  @override
-  void saveTo(String path) async {
-    File fileToSave = File(path);
-    await fileToSave.writeAsBytes(_bytes ?? (await readAsBytes()));
-    await fileToSave.create();
-  }
-
-  @override
-  String get path {
-    return _file.path;
-  }
-
-  @override
-  String get name {
-    return _file.path.split(Platform.pathSeparator).last;
-  }
-
-  @override
-  Future<int> length() {
-    if (_length != null) {
-      return Future.value(_length);
-    }
-    return _file.length();
-  }
-
-  @override
-  Future<String> readAsString({Encoding encoding = utf8}) {
-    if (_bytes != null) {
-      return Future.value(String.fromCharCodes(_bytes));
-    }
-    return _file.readAsString(encoding: encoding);
-  }
-
-  @override
-  Future<Uint8List> readAsBytes() {
-    if (_bytes != null) {
-      return Future.value(_bytes);
-    }
-    return _file.readAsBytes();
-  }
-
-  Stream<Uint8List> _getBytes(int start, int end) async* {
-    final bytes = _bytes;
-    yield bytes.sublist(start ?? 0, end ?? bytes.length);
-  }
-
-  @override
-  Stream<Uint8List> openRead([int start, int end]) {
-    if (_bytes != null) {
-      return _getBytes(start, end);
-    } else {
-      return _file
-          .openRead(start ?? 0, end)
-          .map((chunk) => Uint8List.fromList(chunk));
-    }
-  }
-}
diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/x_file.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/x_file.dart
deleted file mode 100644
index 4545c60..0000000
--- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/x_file.dart
+++ /dev/null
@@ -1,3 +0,0 @@
-export 'interface.dart'
-    if (dart.library.html) 'html.dart'
-    if (dart.library.io) 'io.dart';
diff --git a/packages/file_selector/file_selector_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml
index ca5c2bd..a0a3d28 100644
--- a/packages/file_selector/file_selector_platform_interface/pubspec.yaml
+++ b/packages/file_selector/file_selector_platform_interface/pubspec.yaml
@@ -3,7 +3,7 @@
 homepage: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_platform_interface
 # 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.1
+version: 1.0.2
 
 dependencies:
   flutter:
@@ -11,6 +11,7 @@
   meta: ^1.0.5
   http: ^0.12.0+1
   plugin_platform_interface: ^1.0.1
+  cross_file: ^0.1.0
 
 dev_dependencies:
   test: ^1.15.0
diff --git a/packages/file_selector/file_selector_platform_interface/test/assets/hello.txt b/packages/file_selector/file_selector_platform_interface/test/assets/hello.txt
deleted file mode 100644
index 5dd01c1..0000000
--- a/packages/file_selector/file_selector_platform_interface/test/assets/hello.txt
+++ /dev/null
@@ -1 +0,0 @@
-Hello, world!
\ No newline at end of file
diff --git a/packages/file_selector/file_selector_platform_interface/test/x_file_html_test.dart b/packages/file_selector/file_selector_platform_interface/test/x_file_html_test.dart
deleted file mode 100644
index f888a04..0000000
--- a/packages/file_selector/file_selector_platform_interface/test/x_file_html_test.dart
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2020 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-@TestOn('chrome') // Uses web-only Flutter SDK
-
-import 'dart:convert';
-import 'dart:html' as html;
-import 'dart:typed_data';
-
-import 'package:flutter_test/flutter_test.dart';
-import 'package:file_selector_platform_interface/file_selector_platform_interface.dart';
-
-import 'dart:html';
-
-final String expectedStringContents = 'Hello, world!';
-final Uint8List bytes = utf8.encode(expectedStringContents);
-final html.File textFile = html.File([bytes], 'hello.txt');
-final String textFileUrl = html.Url.createObjectUrl(textFile);
-
-void main() {
-  group('Create with an objectUrl', () {
-    final file = XFile(textFileUrl);
-
-    test('Can be read as a string', () async {
-      expect(await file.readAsString(), equals(expectedStringContents));
-    });
-    test('Can be read as bytes', () async {
-      expect(await file.readAsBytes(), equals(bytes));
-    });
-
-    test('Can be read as a stream', () async {
-      expect(await file.openRead().first, equals(bytes));
-    });
-
-    test('Stream can be sliced', () async {
-      expect(await file.openRead(2, 5).first, equals(bytes.sublist(2, 5)));
-    });
-  });
-
-  group('Create from data', () {
-    final file = XFile.fromData(bytes);
-
-    test('Can be read as a string', () async {
-      expect(await file.readAsString(), equals(expectedStringContents));
-    });
-    test('Can be read as bytes', () async {
-      expect(await file.readAsBytes(), equals(bytes));
-    });
-
-    test('Can be read as a stream', () async {
-      expect(await file.openRead().first, equals(bytes));
-    });
-
-    test('Stream can be sliced', () async {
-      expect(await file.openRead(2, 5).first, equals(bytes.sublist(2, 5)));
-    });
-  });
-
-  group('saveTo(..)', () {
-    final String xFileDomElementId = '__x_file_dom_element';
-
-    group('XFile saveTo(..)', () {
-      test('creates a DOM container', () async {
-        XFile file = XFile.fromData(bytes);
-
-        await file.saveTo('');
-
-        final container = querySelector('#${xFileDomElementId}');
-
-        expect(container, isNotNull);
-      });
-
-      test('create anchor element', () async {
-        XFile file = XFile.fromData(bytes, name: textFile.name);
-
-        await file.saveTo('path');
-
-        final container = querySelector('#${xFileDomElementId}');
-        final AnchorElement element = container?.children?.firstWhere(
-            (element) => element.tagName == 'A',
-            orElse: () => null);
-
-        expect(element, isNotNull);
-        expect(element.href, file.path);
-        expect(element.download, file.name);
-      });
-
-      test('anchor element is clicked', () async {
-        final mockAnchor = AnchorElement();
-
-        XFileTestOverrides overrides = XFileTestOverrides(
-          createAnchorElement: (_, __) => mockAnchor,
-        );
-
-        XFile file =
-            XFile.fromData(bytes, name: textFile.name, overrides: overrides);
-
-        bool clicked = false;
-        mockAnchor.onClick.listen((event) => clicked = true);
-
-        await file.saveTo('path');
-
-        expect(clicked, true);
-      });
-    });
-  });
-}
diff --git a/packages/file_selector/file_selector_platform_interface/test/x_file_io_test.dart b/packages/file_selector/file_selector_platform_interface/test/x_file_io_test.dart
deleted file mode 100644
index b669324..0000000
--- a/packages/file_selector/file_selector_platform_interface/test/x_file_io_test.dart
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2020 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-@TestOn('vm') // Uses dart:io
-
-import 'dart:convert';
-import 'dart:io';
-import 'dart:typed_data';
-
-import 'package:flutter_test/flutter_test.dart';
-import 'package:file_selector_platform_interface/file_selector_platform_interface.dart';
-
-// Please note that executing this test with command
-// `flutter test test/x_file_io_test.dart` will set the directory
-// to ./file_selector_platform_interface.
-//
-// This will cause our hello.txt file to be not be found. Please
-// execute this test with `flutter test` or change the path prefix
-// to ./test/assets/
-//
-// https://github.com/flutter/flutter/issues/20907
-
-final pathPrefix = './assets/';
-final path = pathPrefix + 'hello.txt';
-final String expectedStringContents = 'Hello, world!';
-final Uint8List bytes = utf8.encode(expectedStringContents);
-final File textFile = File(path);
-final String textFilePath = textFile.path;
-
-void main() {
-  group('Create with a path', () {
-    final file = XFile(textFilePath);
-
-    test('Can be read as a string', () async {
-      expect(await file.readAsString(), equals(expectedStringContents));
-    });
-    test('Can be read as bytes', () async {
-      expect(await file.readAsBytes(), equals(bytes));
-    });
-
-    test('Can be read as a stream', () async {
-      expect(await file.openRead().first, equals(bytes));
-    });
-
-    test('Stream can be sliced', () async {
-      expect(await file.openRead(2, 5).first, equals(bytes.sublist(2, 5)));
-    });
-
-    test('saveTo(..) creates file', () async {
-      File removeBeforeTest = File(pathPrefix + 'newFilePath.txt');
-      if (removeBeforeTest.existsSync()) {
-        await removeBeforeTest.delete();
-      }
-
-      await file.saveTo(pathPrefix + 'newFilePath.txt');
-      File newFile = File(pathPrefix + 'newFilePath.txt');
-
-      expect(newFile.existsSync(), isTrue);
-      expect(newFile.readAsStringSync(), 'Hello, world!');
-
-      await newFile.delete();
-    });
-  });
-
-  group('Create with data', () {
-    final file = XFile.fromData(bytes);
-
-    test('Can be read as a string', () async {
-      expect(await file.readAsString(), equals(expectedStringContents));
-    });
-    test('Can be read as bytes', () async {
-      expect(await file.readAsBytes(), equals(bytes));
-    });
-
-    test('Can be read as a stream', () async {
-      expect(await file.openRead().first, equals(bytes));
-    });
-
-    test('Stream can be sliced', () async {
-      expect(await file.openRead(2, 5).first, equals(bytes.sublist(2, 5)));
-    });
-
-    test('Function saveTo(..) creates file', () async {
-      File removeBeforeTest = File(pathPrefix + 'newFileData.txt');
-      if (removeBeforeTest.existsSync()) {
-        await removeBeforeTest.delete();
-      }
-
-      await file.saveTo(pathPrefix + 'newFileData.txt');
-      File newFile = File(pathPrefix + 'newFileData.txt');
-
-      expect(newFile.existsSync(), isTrue);
-      expect(newFile.readAsStringSync(), 'Hello, world!');
-
-      await newFile.delete();
-    });
-  });
-}