[video_player] Fix file URI construction (#6803)

* Fix bug in file constructor

* Fix comment in interface package

* Fix minicontroller for examples/tests/

* Version bumps

* Skip file tests on web

* Don't use file source in non-file tests, since web doesn't support it
diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md
index 1c7da48..ef35127 100644
--- a/packages/video_player/video_player/CHANGELOG.md
+++ b/packages/video_player/video_player/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.4.9
+
+* Fixes file URI construction.
+
 ## 2.4.8
 
 * Updates code for new analysis options.
diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart
index 2f84b82..76f865a 100644
--- a/packages/video_player/video_player/lib/video_player.dart
+++ b/packages/video_player/video_player/lib/video_player.dart
@@ -245,12 +245,11 @@
 
   /// Constructs a [VideoPlayerController] playing a video from a file.
   ///
-  /// This will load the file from the file-URI given by:
-  /// `'file://${file.path}'`.
+  /// This will load the file from a file:// URI constructed from [file]'s path.
   VideoPlayerController.file(File file,
       {Future<ClosedCaptionFile>? closedCaptionFile, this.videoPlayerOptions})
       : _closedCaptionFileFuture = closedCaptionFile,
-        dataSource = 'file://${file.path}',
+        dataSource = Uri.file(file.absolute.path).toString(),
         dataSourceType = DataSourceType.file,
         package = null,
         formatHint = null,
diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml
index 1f5fed2..ace50f9 100644
--- a/packages/video_player/video_player/pubspec.yaml
+++ b/packages/video_player/video_player/pubspec.yaml
@@ -3,7 +3,7 @@
   widgets on Android, iOS, and web.
 repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22
-version: 2.4.8
+version: 2.4.9
 
 environment:
   sdk: ">=2.14.0 <3.0.0"
diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart
index 973fab4..a35264f 100644
--- a/packages/video_player/video_player/test/video_player_test.dart
+++ b/packages/video_player/video_player/test/video_player_test.dart
@@ -7,6 +7,7 @@
 import 'dart:math' as math;
 import 'dart:typed_data';
 
+import 'package:flutter/foundation.dart' show kIsWeb;
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter_test/flutter_test.dart';
@@ -341,8 +342,21 @@
             VideoPlayerController.file(File('a.avi'));
         await controller.initialize();
 
-        expect(fakeVideoPlayerPlatform.dataSources[0].uri, 'file://a.avi');
-      });
+        final String uri = fakeVideoPlayerPlatform.dataSources[0].uri!;
+        expect(uri.startsWith('file:///'), true, reason: 'Actual string: $uri');
+        expect(uri.endsWith('/a.avi'), true, reason: 'Actual string: $uri');
+      }, skip: kIsWeb /* Web does not support file assets. */);
+
+      test('file with special characters', () async {
+        final VideoPlayerController controller =
+            VideoPlayerController.file(File('A #1 Hit?.avi'));
+        await controller.initialize();
+
+        final String uri = fakeVideoPlayerPlatform.dataSources[0].uri!;
+        expect(uri.startsWith('file:///'), true, reason: 'Actual string: $uri');
+        expect(uri.endsWith('/A%20%231%20Hit%3F.avi'), true,
+            reason: 'Actual string: $uri');
+      }, skip: kIsWeb /* Web does not support file assets. */);
 
       test('successful initialize on controller with error clears error',
           () async {
@@ -1009,16 +1023,16 @@
     });
 
     test('setMixWithOthers', () async {
-      final VideoPlayerController controller = VideoPlayerController.file(
-          File(''),
+      final VideoPlayerController controller = VideoPlayerController.network(
+          'https://127.0.0.1',
           videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true));
       await controller.initialize();
       expect(controller.videoPlayerOptions!.mixWithOthers, true);
     });
 
     test('true allowBackgroundPlayback continues playback', () async {
-      final VideoPlayerController controller = VideoPlayerController.file(
-        File(''),
+      final VideoPlayerController controller = VideoPlayerController.network(
+        'https://127.0.0.1',
         videoPlayerOptions: VideoPlayerOptions(
           allowBackgroundPlayback: true,
         ),
@@ -1032,8 +1046,8 @@
     });
 
     test('false allowBackgroundPlayback pauses playback', () async {
-      final VideoPlayerController controller = VideoPlayerController.file(
-        File(''),
+      final VideoPlayerController controller = VideoPlayerController.network(
+        'https://127.0.0.1',
         videoPlayerOptions: VideoPlayerOptions(),
       );
       await controller.initialize();
diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md
index 7137e24..bad1fb4 100644
--- a/packages/video_player/video_player_android/CHANGELOG.md
+++ b/packages/video_player/video_player_android/CHANGELOG.md
@@ -1,5 +1,6 @@
 ## NEXT
 
+* Fixes file URI construction in example.
 * Updates code for new analysis options.
 * Updates code for `no_leading_underscores_for_local_identifiers` lint.
 * Updates minimum Flutter version to 2.10.
diff --git a/packages/video_player/video_player_android/example/lib/mini_controller.dart b/packages/video_player/video_player_android/example/lib/mini_controller.dart
index 26c60b7..fb79a77 100644
--- a/packages/video_player/video_player_android/example/lib/mini_controller.dart
+++ b/packages/video_player/video_player_android/example/lib/mini_controller.dart
@@ -150,7 +150,7 @@
 
   /// Constructs a [MiniController] playing a video from obtained from a file.
   MiniController.file(File file)
-      : dataSource = 'file://${file.path}',
+      : dataSource = Uri.file(file.absolute.path).toString(),
         dataSourceType = DataSourceType.file,
         package = null,
         super(VideoPlayerValue(duration: Duration.zero));
diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md
index 1198599..d184372 100644
--- a/packages/video_player/video_player_avfoundation/CHANGELOG.md
+++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md
@@ -1,5 +1,6 @@
 ## NEXT
 
+* Fixes file URI construction in example.
 * Updates code for new analysis options.
 * Adds an integration test for a bug where the aspect ratios of some HLS videos are incorrectly inverted.
 * Removes an unnecessary override in example code.
@@ -11,7 +12,7 @@
 
 ## 2.3.6
 
-* Fixes a bug in iOS 16 where videos from protected live streams are not shown. 
+* Fixes a bug in iOS 16 where videos from protected live streams are not shown.
 * Updates minimum Flutter version to 2.10.
 * Fixes violations of new analysis option use_named_constants.
 * Fixes avoid_redundant_argument_values lint warnings and minor typos.
diff --git a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart
index 26c60b7..fb79a77 100644
--- a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart
+++ b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart
@@ -150,7 +150,7 @@
 
   /// Constructs a [MiniController] playing a video from obtained from a file.
   MiniController.file(File file)
-      : dataSource = 'file://${file.path}',
+      : dataSource = Uri.file(file.absolute.path).toString(),
         dataSourceType = DataSourceType.file,
         package = null,
         super(VideoPlayerValue(duration: Duration.zero));
diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md
index 05cb638..fb7a3b3 100644
--- a/packages/video_player/video_player_platform_interface/CHANGELOG.md
+++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 6.0.1
+
+* Fixes comment describing file URI construction.
+
 ## 6.0.0
 
 * **BREAKING CHANGE**: Removes `MethodChannelVideoPlayer`. The default
diff --git a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart
index 92099eb..d3df9b2 100644
--- a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart
+++ b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart
@@ -114,7 +114,7 @@
   /// The [sourceType] is always required.
   ///
   /// The [uri] argument takes the form of `'https://example.com/video.mp4'` or
-  /// `'file://${file.path}'`.
+  /// `'file:///absolute/path/to/local/video.mp4`.
   ///
   /// The [formatHint] argument can be null.
   ///
diff --git a/packages/video_player/video_player_platform_interface/pubspec.yaml b/packages/video_player/video_player_platform_interface/pubspec.yaml
index 56e132d..36c0a71 100644
--- a/packages/video_player/video_player_platform_interface/pubspec.yaml
+++ b/packages/video_player/video_player_platform_interface/pubspec.yaml
@@ -4,7 +4,7 @@
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%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: 6.0.0
+version: 6.0.1
 
 environment:
   sdk: ">=2.12.0 <3.0.0"