Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 03b29bb

Browse files
authoredApr 1, 2025··
Merge branch 'fluttercommunity:master' into pause-on-tap
2 parents 1937a7c + 483e22b commit 03b29bb

20 files changed

+350
-154
lines changed
 

‎.github/workflows/ci.yml

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,46 +11,51 @@ on:
1111
- '**.md'
1212
workflow_dispatch:
1313

14+
concurrency:
15+
group: ${{ github.workflow }}-${{ github.ref }}
16+
cancel-in-progress: true
17+
1418
jobs:
15-
check-format:
16-
name: Check format using dart format.
17-
runs-on: ubuntu-latest
18-
19-
steps:
20-
- name: Checkout code
21-
uses: actions/checkout@v4
22-
- name: Flutter Action
23-
uses: subosito/flutter-action@v2
24-
- name: Check format
25-
run: dart format . --set-exit-if-changed
26-
27-
lint:
28-
name: Lint
19+
# Does a sanity check that packages at least pass analysis on the N-1
20+
# versions of Flutter stable if the package claims to support that version.
21+
# This is to minimize accidentally making changes that break old versions
22+
# (which we don't commit to supporting, but don't want to actively break)
23+
# without updating the constraints.
24+
lint_and_build:
25+
name: Flutter Version ${{ matrix.flutter-version }} Lint and Build.
2926
runs-on: ubuntu-latest
30-
27+
strategy:
28+
matrix:
29+
flutter-version:
30+
# The version of Flutter to use should use the minimum Dart SDK version supported by the package,
31+
# refer to https://docs.flutter.dev/development/tools/sdk/releases.
32+
# Note: The version below should be manually updated to the latest second most recent version
33+
# after a new stable version comes out.
34+
- "3.27.4"
35+
- "3.x"
3136
steps:
32-
- name: Checkout code
37+
- name: 📚 Git Checkout
3338
uses: actions/checkout@v4
34-
- name: Flutter Action
39+
40+
- name: 🐦 Setup Flutter
3541
uses: subosito/flutter-action@v2
36-
- name: Install Package Dependencies
42+
with:
43+
flutter-version: ${{ matrix.flutter-version }}
44+
channel: stable
45+
cache: true
46+
cache-key: flutter-:os:-:channel:-:version:-:arch:-:hash:-${{ hashFiles('**/pubspec.lock') }}
47+
48+
- name: 📦 Install Dependencies
3749
run: flutter packages get
38-
- name: Get dependencies for example
39-
run: flutter pub get
40-
working-directory: example
41-
- name: Lint using flutter analyze
42-
run: flutter analyze .
43-
44-
test:
45-
name: Run tests.
46-
runs-on: ubuntu-latest
4750

48-
steps:
49-
- name: Checkout code
50-
uses: actions/checkout@v4
51-
- name: Flutter Action
52-
uses: subosito/flutter-action@v2
53-
- name: Run flutter test
54-
run: |
55-
flutter pub get
56-
flutter test
51+
- name: ✨ Check Formatting
52+
run: dart format --set-exit-if-changed lib
53+
54+
- name: 🕵️ Analyze
55+
run: flutter analyze lib
56+
57+
- name: 🧪 Run Tests
58+
run: flutter test --no-pub --coverage --test-randomize-ordering-seed random
59+
60+
- name: 📁 Upload coverage to Codecov
61+
uses: codecov/codecov-action@v5

‎CHANGELOG.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,35 @@
1+
## [1.11.0]
2+
* ⬆️ [#900](https://github.com/fluttercommunity/chewie/pull/900): Flutter `3.29` upgrade. Thanks [diegotori](https://github.com/diegotori).
3+
* **BREAKING CHANGE**: Library now requires at least Flutter version `3.27.0`, for real this time.
4+
5+
## [1.10.0]
6+
* 🛠️ [#871](https://github.com/fluttercommunity/chewie/pull/871): Fixed pop the wrong page when changing the speed. Thanks [akmalova](https://github.com/akmalova).
7+
* **BREAKING CHANGES**:
8+
* `OptionItem.onTap` now takes in a `BuildContext`.
9+
* `OptionItem`'s properties are now marked as `final`. Use `copyWith` to mutate its properties into
10+
a new instance.
11+
12+
## [1.9.2]
13+
* Fixed broken Table of Contents links in `README.md`, take two.
14+
15+
## [1.9.1+1]
16+
* Fixed broken Table of Contents links in `README.md`.
17+
18+
## [1.9.1]
19+
* [#872](https://github.com/fluttercommunity/chewie/pull/872): feat: Add showSubtitles flag to control subtitles (#648). Thanks [floodoo](https://github.com/floodoo).
20+
* [#890](https://github.com/fluttercommunity/chewie/pull/890): Fix issue 888. Thanks [diegotori](https://github.com/diegotori).
21+
* **IMPORTANT**: Relaxed the minimum supported Flutter version to `3.24`.
22+
From now on, this library will make a best effort to support the latest `N-1` Flutter version at the minimum.
23+
24+
## [1.9.0]
25+
* **BREAKING CHANGE**: Library now requires at least Flutter version `3.27.0`.
26+
27+
## [1.8.7]
28+
* ⬆️ [#876](https://github.com/fluttercommunity/chewie/pull/876): Add keyboard controls seek forward and backward and fullscreen escape on desktop. Thanks [Ortes](https://github.com/Ortes).
29+
30+
## [1.8.6]
31+
* ⬆️ [#874](https://github.com/fluttercommunity/chewie/pull/874): Add `devtools_options.yaml` configuration files. Thanks [MoRmdn](https://github.com/MoRmdn).
32+
133
## [1.8.5]
234
* ⬆️ [#703](https://github.com/fluttercommunity/chewie/pull/703): Adding Seek buttons for Android. Thanks [GyanendroKh](https://github.com/GyanendroKh).
335
* Upgraded `wakelock_plus` to version `1.2.8`, which uses `web` version `1.0.0`. Thanks [diegotori](https://github.com/diegotori).

‎README.md

Lines changed: 74 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,53 @@
88

99
The video player for Flutter with a heart of gold.
1010

11-
The [`video_player`](https://pub.dartlang.org/packages/video_player) plugin provides low-level access to video playback. Chewie uses the `video_player` under the hood and wraps it in a friendly Material or Cupertino UI!
11+
The [`video_player`](https://pub.dartlang.org/packages/video_player) plugin provides low-level
12+
access to video playback.
1213

13-
## Preview
14+
Chewie uses the `video_player` under the hood and wraps it in a friendly Material or Cupertino UI!
1415

15-
| MaterialControls | MaterialDesktopControls |
16-
| :--------------: | :---------------------: |
17-
| ![](https://github.com/brianegan/chewie/raw/master/assets/MaterialControls.png) | ![](https://github.com/brianegan/chewie/raw/master/assets/MaterialDesktopControls.png) |
16+
## Table of Contents
17+
1. 🚨 [IMPORTANT!!! (READ THIS FIRST)](#-important-read-this-first)
18+
2. 🔀 [Flutter Version Compatibility](#-flutter-version-compatibility)
19+
3. 🖼️ [Preview](#%EF%B8%8F-preview)
20+
4. ⬇️ [Installation](#%EF%B8%8F-installation)
21+
5. 🕹️ [Using it](#%EF%B8%8F-using-it)
22+
6. ⚙️ [Options](#%EF%B8%8F-options)
23+
7. 🔡 [Subtitles](#-subtitles)
24+
8. 🧪 [Example](#-example)
25+
9.[Migrating from Chewie < 0.9.0](#-migrating-from-chewie--090)
26+
10. 🗺️ [Roadmap](#%EF%B8%8F-roadmap)
27+
11. 📱 [iOS warning](#-ios-warning-)
28+
29+
30+
## 🚨 IMPORTANT!!! (READ THIS FIRST)
31+
This library is __NOT__ responsible for any issues caused by `video_player`, since it's merely a UI
32+
layer on top of it.
33+
34+
In other words, if you see any `PlatformException`s being thrown in your app due to video playback,
35+
they are exclusive to the `video_player` library.
36+
37+
Instead, please raise an issue related to it with the [Flutter Team](https://github.com/flutter/flutter/issues/new/choose).
38+
39+
## 🔀 Flutter Version Compatibility
40+
41+
This library will at the very least make a solid effort to support the second most recent version
42+
of Flutter released. In other words, it will adopt `N-1` version support at
43+
the bare minimum.
44+
45+
However, this cannot be guaranteed due to major changes between Flutter versions. Should that occur,
46+
future updates will be released as major or minor versions as needed.
47+
48+
## 🖼️ Preview
49+
50+
| MaterialControls | MaterialDesktopControls |
51+
|:-------------------------------------------------------------------------------:|:--------------------------------------------------------------------------------------:|
52+
| ![](https://github.com/brianegan/chewie/raw/master/assets/MaterialControls.png) | ![](https://github.com/brianegan/chewie/raw/master/assets/MaterialDesktopControls.png) |
1853

1954
### CupertinoControls
2055
![](https://github.com/brianegan/chewie/raw/master/assets/CupertinoControls.png)
2156

22-
## Installation
57+
## ⬇️ Installation
2358

2459
In your `pubspec.yaml` file within your Flutter Project add `chewie` and `video_player` under dependencies:
2560

@@ -29,10 +64,12 @@ dependencies:
2964
video_player: <latest_version>
3065
```
3166
32-
## Using it
67+
## 🕹️ Using it
3368
3469
```dart
3570
import 'package:chewie/chewie.dart';
71+
import 'package:video_player/video_player.dart';
72+
3673
final videoPlayerController = VideoPlayerController.networkUrl(Uri.parse(
3774
'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4'));
3875

@@ -59,7 +96,7 @@ void dispose() {
5996
}
6097
```
6198

62-
## Options
99+
## ⚙️ Options
63100

64101
![](https://github.com/brianegan/chewie/raw/master/assets/Options.png)
65102

@@ -125,13 +162,19 @@ optionsTranslation: OptionsTranslation(
125162
),
126163
```
127164

128-
## Subtitles
165+
## 🔡 Subtitles
166+
167+
> Since version 1.1.0, Chewie supports subtitles.
168+
169+
Chewie allows you to enhance the video playback experience with text overlays. You can add a `List<Subtitle>` to your `ChewieController` and fully customize their appearance using the `subtitleBuilder` function.
170+
171+
### Showing Subtitles by Default
129172

130-
> Since version 1.1.0 chewie supports subtitles. Here you can see how to use them.
173+
Chewie provides the `showSubtitles` flag, allowing you to control whether subtitles are displayed automatically when the video starts. By default, this flag is set to `false`.
131174

132-
You can provide an `List<Subtitle>` and customize your subtitles with the `subtitleBuilder` function.
175+
### Adding Subtitles
133176

134-
Add subtitles to your `ChewieController` like the following example:
177+
Here’s an example of how to add subtitles to your `ChewieController`:
135178

136179
```dart
137180
ChewieController(
@@ -149,9 +192,10 @@ ChewieController(
149192
index: 1,
150193
start: const Duration(seconds: 10),
151194
end: const Duration(seconds: 20),
152-
text: 'Whats up? :)',
195+
text: 'What’s up? :)',
153196
),
154197
]),
198+
showSubtitles: true, // Automatically display subtitles
155199
subtitleBuilder: (context, subtitle) => Container(
156200
padding: const EdgeInsets.all(10.0),
157201
child: Text(
@@ -162,9 +206,16 @@ ChewieController(
162206
);
163207
```
164208

165-
The `index` attribute is for if you want to structure your subtitles in your database and provide your indexes here. `end` and `text` are the key attributes.
209+
### Subtitle Structure
166210

167-
The Duration defines which part of your video your subtitles should start and end. For example, if your video is 10 minutes long and you want to add a subtitle between: `00:00` and `00:10`'th of a second:
211+
The `Subtitle` model contains the following key attributes:
212+
213+
- **`index`**: A unique identifier for the subtitle, useful for database integration.
214+
- **`start`**: The starting point of the subtitle, defined as a `Duration`.
215+
- **`end`**: The ending point of the subtitle, defined as a `Duration`.
216+
- **`text`**: The subtitle text that will be displayed.
217+
218+
For example, if your video is 10 minutes long and you want to add a subtitle that appears between `00:00` and `00:10`, you can define it like this:
168219

169220
```dart
170221
Subtitle(
@@ -175,11 +226,15 @@ Subtitle(
175226
),
176227
```
177228

178-
## Example
229+
### Customizing Subtitles
230+
231+
Use the `subtitleBuilder` function to customize how subtitles are rendered, allowing you to modify text styles, add padding, or apply other customizations to your subtitles.
232+
233+
## 🧪 Example
179234

180235
Please run the app in the [`example/`](https://github.com/brianegan/chewie/tree/master/example) folder to start playing!
181236

182-
## Migrating from Chewie < 0.9.0
237+
## Migrating from Chewie < 0.9.0
183238

184239
Instead of passing the `VideoPlayerController` and your options to the `Chewie` widget you now pass them to the `ChewieController` and pass that later to the `Chewie` widget.
185240

@@ -205,7 +260,7 @@ final playerWidget = Chewie(
205260
);
206261
```
207262

208-
## Roadmap
263+
## 🗺️ Roadmap
209264

210265
- [x] MaterialUI
211266
- [x] MaterialDesktopUI
@@ -231,7 +286,7 @@ final playerWidget = Chewie(
231286
- [ ] Screen-Mirroring / Casting (Google Chromecast)
232287

233288

234-
## iOS warning
289+
## 📱 iOS warning
235290

236291
The video_player plugin used by chewie will only work in iOS simulators if you are on flutter 1.26.0 or above. You may need to switch to the beta channel `flutter channel beta`
237292
Please refer to this [issue](https://github.com/flutter/flutter/issues/14647).

‎devtools_options.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
description: This file stores settings for Dart & Flutter DevTools.
2+
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
3+
extensions:

‎example/devtools_options.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
description: This file stores settings for Dart & Flutter DevTools.
2+
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
3+
extensions:

‎example/ios/Runner/AppDelegate.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Flutter
22
import UIKit
33

4-
@UIApplicationMain
4+
@main
55
@objc class AppDelegate: FlutterAppDelegate {
66
override func application(
77
_ application: UIApplication,

‎example/lib/app/app.dart

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@ import 'package:flutter/material.dart';
66
import 'package:video_player/video_player.dart';
77

88
class ChewieDemo extends StatefulWidget {
9-
const ChewieDemo({
10-
super.key,
11-
this.title = 'Chewie Demo',
12-
});
9+
const ChewieDemo({super.key, this.title = 'Chewie Demo'});
1310

1411
final String title;
1512

@@ -41,19 +38,21 @@ class _ChewieDemoState extends State<ChewieDemo> {
4138
}
4239

4340
List<String> srcs = [
44-
"https://assets.mixkit.co/videos/preview/mixkit-spinning-around-the-earth-29351-large.mp4",
45-
"https://assets.mixkit.co/videos/preview/mixkit-daytime-city-traffic-aerial-view-56-large.mp4",
46-
"https://assets.mixkit.co/videos/preview/mixkit-a-girl-blowing-a-bubble-gum-at-an-amusement-park-1226-large.mp4"
41+
"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
42+
"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4",
43+
"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4",
4744
];
4845

4946
Future<void> initializePlayer() async {
50-
_videoPlayerController1 =
51-
VideoPlayerController.networkUrl(Uri.parse(srcs[currPlayIndex]));
52-
_videoPlayerController2 =
53-
VideoPlayerController.networkUrl(Uri.parse(srcs[currPlayIndex]));
47+
_videoPlayerController1 = VideoPlayerController.networkUrl(
48+
Uri.parse(srcs[currPlayIndex]),
49+
);
50+
_videoPlayerController2 = VideoPlayerController.networkUrl(
51+
Uri.parse(srcs[currPlayIndex]),
52+
);
5453
await Future.wait([
5554
_videoPlayerController1.initialize(),
56-
_videoPlayerController2.initialize()
55+
_videoPlayerController2.initialize(),
5756
]);
5857
_createChewieController();
5958
setState(() {});
@@ -93,7 +92,7 @@ class _ChewieDemoState extends State<ChewieDemo> {
9392
TextSpan(
9493
text: 'subtitles',
9594
style: TextStyle(color: Colors.blue, fontSize: 18),
96-
)
95+
),
9796
],
9897
),
9998
),
@@ -119,19 +118,18 @@ class _ChewieDemoState extends State<ChewieDemo> {
119118
additionalOptions: (context) {
120119
return <OptionItem>[
121120
OptionItem(
122-
onTap: toggleVideo,
121+
onTap: (context) => toggleVideo(),
123122
iconData: Icons.live_tv_sharp,
124123
title: 'Toggle Video Src',
125124
),
126125
];
127126
},
128127
subtitle: Subtitles(subtitles),
128+
showSubtitles: true,
129129
subtitleBuilder: (context, dynamic subtitle) => Container(
130130
padding: const EdgeInsets.all(10.0),
131131
child: subtitle is InlineSpan
132-
? RichText(
133-
text: subtitle,
134-
)
132+
? RichText(text: subtitle)
135133
: Text(
136134
subtitle.toString(),
137135
style: const TextStyle(color: Colors.black),
@@ -175,19 +173,15 @@ class _ChewieDemoState extends State<ChewieDemo> {
175173
platform: _platform ?? Theme.of(context).platform,
176174
),
177175
home: Scaffold(
178-
appBar: AppBar(
179-
title: Text(widget.title),
180-
),
176+
appBar: AppBar(title: Text(widget.title)),
181177
body: Column(
182178
children: <Widget>[
183179
Expanded(
184180
child: Center(
185181
child: _chewieController != null &&
186182
_chewieController!
187183
.videoPlayerController.value.isInitialized
188-
? Chewie(
189-
controller: _chewieController!,
190-
)
184+
? Chewie(controller: _chewieController!)
191185
: const Column(
192186
mainAxisAlignment: MainAxisAlignment.center,
193187
children: [
@@ -260,7 +254,7 @@ class _ChewieDemoState extends State<ChewieDemo> {
260254
child: Text("Portrait Video"),
261255
),
262256
),
263-
)
257+
),
264258
],
265259
),
266260
Row(
@@ -290,7 +284,7 @@ class _ChewieDemoState extends State<ChewieDemo> {
290284
child: Text("iOS controls"),
291285
),
292286
),
293-
)
287+
),
294288
],
295289
),
296290
Row(
@@ -323,7 +317,7 @@ class _ChewieDemoState extends State<ChewieDemo> {
323317
}
324318
},
325319
),
326-
)
320+
),
327321
],
328322
),
329323
),

‎example/lib/main.dart

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,5 @@ import 'package:chewie_example/app/app.dart';
22
import 'package:flutter/material.dart';
33

44
void main() {
5-
runApp(
6-
const ChewieDemo(),
7-
);
5+
runApp(const ChewieDemo());
86
}

‎example/pubspec.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@ version: 1.0.0
44
publish_to: none
55

66
environment:
7-
sdk: '>=3.3.0 <4.0.0'
8-
flutter: ">=3.19.0"
7+
sdk: '>=3.6.0 <4.0.0'
8+
flutter: ">=3.27.0"
99

1010
dependencies:
1111
chewie:
1212
path: ../
1313
flutter:
1414
sdk: flutter
15+
video_player: ^2.9.3
1516

16-
video_player: ^2.9.1
1717
dev_dependencies:
1818
flutter_test:
1919
sdk: flutter
20-
flutter_lints: ^4.0.0
20+
flutter_lints: ^5.0.0
2121

2222
# For information on the generic Dart part of this file, see the
2323
# following page: https://www.dartlang.org/tools/pub/pubspec

‎lib/chewie.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
library chewie;
1+
library;
22

33
export 'src/chewie_player.dart';
44
export 'src/chewie_progress_colors.dart';
55
export 'src/cupertino/cupertino_controls.dart';
66
export 'src/material/material_controls.dart';
7-
export 'src/material/material_progress_bar.dart';
87
export 'src/material/material_desktop_controls.dart';
8+
export 'src/material/material_progress_bar.dart';
99
export 'src/models/index.dart';

‎lib/src/chewie_player.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ class ChewieController extends ChangeNotifier {
288288
this.zoomAndPan = false,
289289
this.maxScale = 2.5,
290290
this.subtitle,
291+
this.showSubtitles = false,
291292
this.subtitleBuilder,
292293
this.customControls,
293294
this.errorBuilder,
@@ -340,6 +341,7 @@ class ChewieController extends ChangeNotifier {
340341
bool? zoomAndPan,
341342
double? maxScale,
342343
Subtitles? subtitle,
344+
bool? showSubtitles,
343345
Widget Function(BuildContext, dynamic)? subtitleBuilder,
344346
Widget? customControls,
345347
WidgetBuilder? bufferingBuilder,
@@ -393,6 +395,7 @@ class ChewieController extends ChangeNotifier {
393395
optionsBuilder: optionsBuilder ?? this.optionsBuilder,
394396
additionalOptions: additionalOptions ?? this.additionalOptions,
395397
showControls: showControls ?? this.showControls,
398+
showSubtitles: showSubtitles ?? this.showSubtitles,
396399
subtitle: subtitle ?? this.subtitle,
397400
subtitleBuilder: subtitleBuilder ?? this.subtitleBuilder,
398401
customControls: customControls ?? this.customControls,
@@ -457,6 +460,12 @@ class ChewieController extends ChangeNotifier {
457460
/// Add a List of Subtitles here in `Subtitles.subtitle`
458461
Subtitles? subtitle;
459462

463+
/// Determines whether subtitles should be shown by default when the video starts.
464+
///
465+
/// If set to `true`, subtitles will be displayed automatically when the video
466+
/// begins playing. If set to `false`, subtitles will be hidden by default.
467+
bool showSubtitles;
468+
460469
/// The controller for the video you want to play
461470
final VideoPlayerController videoPlayerController;
462471

‎lib/src/cupertino/cupertino_controls.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ class _CupertinoControlsState extends State<CupertinoControls>
468468
final position = _latestValue.duration - _latestValue.position;
469469

470470
return Padding(
471-
padding: const EdgeInsets.only(right: 12.0),
471+
padding: const EdgeInsets.symmetric(horizontal: 12.0),
472472
child: Text(
473473
'-${formatDuration(position)}',
474474
style: TextStyle(color: iconColor, fontSize: 12.0),
@@ -649,7 +649,8 @@ class _CupertinoControlsState extends State<CupertinoControls>
649649
}
650650

651651
Future<void> _initialize() async {
652-
_subtitleOn = chewieController.subtitle?.isNotEmpty ?? false;
652+
chewieController.showSubtitles &&
653+
(chewieController.subtitle?.isNotEmpty ?? false);
653654
controller.addListener(_updateState);
654655

655656
_updateState();

‎lib/src/cupertino/widgets/cupertino_options_dialog.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class _CupertinoOptionsDialogState extends State<CupertinoOptionsDialog> {
2424
actions: widget.options
2525
.map(
2626
(option) => CupertinoActionSheetAction(
27-
onPressed: () => option.onTap!(),
27+
onPressed: () => option.onTap(context),
2828
child: Text(option.title),
2929
),
3030
)

‎lib/src/helpers/adaptive_controls.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ class AdaptiveControls extends StatelessWidget {
2323
backgroundColor: Color.fromRGBO(41, 41, 41, 0.7),
2424
iconColor: Color.fromARGB(255, 200, 200, 200),
2525
);
26-
default:
27-
return const MaterialControls();
2826
}
2927
}
3028
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import 'package:flutter/material.dart';
2+
3+
//ignore_for_file: deprecated_member_use
4+
extension ColorCompatExtensions on Color {
5+
/// Returns a new color that matches this color with the given opacity.
6+
///
7+
/// This is a compatibility layer that ensures compatibility with Flutter
8+
/// versions below 3.27. In Flutter 3.27 and later, `Color.withOpacity`
9+
/// has been deprecated in favor of `Color.withValues`.
10+
///
11+
/// This method bridges the gap by providing a consistent way to adjust
12+
/// the opacity of a color across different Flutter versions.
13+
///
14+
/// **Important:** Once the minimum supported Flutter version is bumped
15+
/// to 3.27 or higher, this method should be removed and replaced with
16+
/// `withValues(alpha: opacity)`.
17+
///
18+
/// See also:
19+
/// * [Color.withOpacity], which is deprecated in Flutter 3.27 and later.
20+
/// * [Color.withValues], the recommended replacement for `withOpacity`.
21+
Color withOpacityCompat(double opacity) {
22+
// Compatibility layer that uses the legacy withOpacity method, while
23+
// ignoring the deprecation for now (in order to guarantee N-1 minimum
24+
// version compatibility).
25+
// Once it's removed from a future update, we'll have to replace uses of
26+
// this method with withValues(alpha: opacity).
27+
// TODO: Replace this bridge method once the above holds true.
28+
return withOpacity(opacity);
29+
}
30+
}

‎lib/src/material/material_controls.dart

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:chewie/src/center_seek_button.dart';
55
import 'package:chewie/src/chewie_player.dart';
66
import 'package:chewie/src/chewie_progress_colors.dart';
77
import 'package:chewie/src/helpers/utils.dart';
8+
import 'package:chewie/src/material/color_compat_extensions.dart';
89
import 'package:chewie/src/material/material_progress_bar.dart';
910
import 'package:chewie/src/material/widgets/options_dialog.dart';
1011
import 'package:chewie/src/material/widgets/playback_speed_dialog.dart';
@@ -161,10 +162,10 @@ class _MaterialControlsState extends State<MaterialControls>
161162
);
162163
}
163164

164-
Widget _buildOptionsButton() {
165+
List<OptionItem> _buildOptions(BuildContext context) {
165166
final options = <OptionItem>[
166167
OptionItem(
167-
onTap: () async {
168+
onTap: (context) async {
168169
Navigator.pop(context);
169170
_onSpeedButtonTap();
170171
},
@@ -178,7 +179,10 @@ class _MaterialControlsState extends State<MaterialControls>
178179
chewieController.additionalOptions!(context).isNotEmpty) {
179180
options.addAll(chewieController.additionalOptions!(context));
180181
}
182+
return options;
183+
}
181184

185+
Widget _buildOptionsButton() {
182186
return AnimatedOpacity(
183187
opacity: notifier.hideStuff ? 0.0 : 1.0,
184188
duration: const Duration(milliseconds: 250),
@@ -187,14 +191,15 @@ class _MaterialControlsState extends State<MaterialControls>
187191
_hideTimer?.cancel();
188192

189193
if (chewieController.optionsBuilder != null) {
190-
await chewieController.optionsBuilder!(context, options);
194+
await chewieController.optionsBuilder!(
195+
context, _buildOptions(context));
191196
} else {
192197
await showModalBottomSheet<OptionItem>(
193198
context: context,
194199
isScrollControlled: true,
195200
useRootNavigator: chewieController.useRootNavigator,
196201
builder: (context) => OptionsDialog(
197-
options: options,
202+
options: _buildOptions(context),
198203
cancelButtonText:
199204
chewieController.optionsTranslation?.cancelButtonText,
200205
),
@@ -260,6 +265,7 @@ class _MaterialControlsState extends State<MaterialControls>
260265
height: barHeight + (chewieController.isFullScreen ? 10.0 : 0),
261266
padding: EdgeInsets.only(
262267
left: 20,
268+
right: 20,
263269
bottom: !chewieController.isFullScreen ? 10.0 : 0,
264270
),
265271
child: SafeArea(
@@ -291,7 +297,7 @@ class _MaterialControlsState extends State<MaterialControls>
291297
if (!chewieController.isLive)
292298
Expanded(
293299
child: Container(
294-
padding: const EdgeInsets.only(right: 20),
300+
padding: const EdgeInsets.symmetric(horizontal: 20),
295301
child: Row(
296302
children: [
297303
_buildProgressBar(),
@@ -474,7 +480,7 @@ class _MaterialControlsState extends State<MaterialControls>
474480
text: '/ ${formatDuration(duration)}',
475481
style: TextStyle(
476482
fontSize: 14.0,
477-
color: Colors.white.withOpacity(.75),
483+
color: Colors.white.withOpacityCompat(.75),
478484
fontWeight: FontWeight.normal,
479485
),
480486
)
@@ -529,7 +535,8 @@ class _MaterialControlsState extends State<MaterialControls>
529535
}
530536

531537
Future<void> _initialize() async {
532-
_subtitleOn = chewieController.subtitle?.isNotEmpty ?? false;
538+
_subtitleOn = chewieController.showSubtitles &&
539+
(chewieController.subtitle?.isNotEmpty ?? false);
533540
controller.addListener(_updateState);
534541

535542
_updateState();
@@ -686,8 +693,9 @@ class _MaterialControlsState extends State<MaterialControls>
686693
playedColor: Theme.of(context).colorScheme.secondary,
687694
handleColor: Theme.of(context).colorScheme.secondary,
688695
bufferedColor:
689-
Theme.of(context).colorScheme.surface.withOpacity(0.5),
690-
backgroundColor: Theme.of(context).disabledColor.withOpacity(.5),
696+
Theme.of(context).colorScheme.surface.withOpacityCompat(0.5),
697+
backgroundColor:
698+
Theme.of(context).disabledColor.withOpacityCompat(.5),
691699
),
692700
draggableProgressBar: chewieController.draggableProgressBar,
693701
),

‎lib/src/material/material_desktop_controls.dart

Lines changed: 96 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ import 'package:chewie/src/center_play_button.dart';
55
import 'package:chewie/src/chewie_player.dart';
66
import 'package:chewie/src/chewie_progress_colors.dart';
77
import 'package:chewie/src/helpers/utils.dart';
8+
import 'package:chewie/src/material/color_compat_extensions.dart';
89
import 'package:chewie/src/material/material_progress_bar.dart';
910
import 'package:chewie/src/material/widgets/options_dialog.dart';
1011
import 'package:chewie/src/material/widgets/playback_speed_dialog.dart';
1112
import 'package:chewie/src/models/option_item.dart';
1213
import 'package:chewie/src/models/subtitle_model.dart';
1314
import 'package:chewie/src/notifiers/index.dart';
1415
import 'package:flutter/material.dart';
16+
import 'package:flutter/services.dart';
1517
import 'package:provider/provider.dart';
1618
import 'package:video_player/video_player.dart';
1719

@@ -49,16 +51,36 @@ class _MaterialDesktopControlsState extends State<MaterialDesktopControls>
4951

5052
late VideoPlayerController controller;
5153
ChewieController? _chewieController;
54+
late final FocusNode _focusNode;
5255

5356
// We know that _chewieController is set in didChangeDependencies
5457
ChewieController get chewieController => _chewieController!;
5558

5659
@override
5760
void initState() {
5861
super.initState();
62+
_focusNode = FocusNode();
63+
_focusNode.requestFocus();
5964
notifier = Provider.of<PlayerNotifier>(context, listen: false);
6065
}
6166

67+
void _handleKeyPress(event) {
68+
if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.space) {
69+
_playPause();
70+
} else if (event is KeyDownEvent &&
71+
event.logicalKey == LogicalKeyboardKey.arrowRight) {
72+
_seekForward();
73+
} else if (event is KeyDownEvent &&
74+
event.logicalKey == LogicalKeyboardKey.arrowLeft) {
75+
_seekBackward();
76+
} else if (event is KeyDownEvent &&
77+
event.logicalKey == LogicalKeyboardKey.escape) {
78+
if (chewieController.isFullScreen) {
79+
_onExpandCollapse();
80+
}
81+
}
82+
}
83+
6284
@override
6385
Widget build(BuildContext context) {
6486
if (_latestValue.hasError) {
@@ -75,39 +97,44 @@ class _MaterialDesktopControlsState extends State<MaterialDesktopControls>
7597
);
7698
}
7799

78-
return MouseRegion(
79-
onHover: (_) {
80-
_cancelAndRestartTimer();
81-
},
82-
child: GestureDetector(
83-
onTap: () => _cancelAndRestartTimer(),
84-
child: AbsorbPointer(
85-
absorbing: notifier.hideStuff,
86-
child: Stack(
87-
children: [
88-
if (_displayBufferingIndicator)
89-
_chewieController?.bufferingBuilder?.call(context) ??
90-
const Center(
91-
child: CircularProgressIndicator(),
92-
)
93-
else
94-
_buildHitArea(),
95-
Column(
96-
mainAxisAlignment: MainAxisAlignment.end,
97-
children: <Widget>[
98-
if (_subtitleOn)
99-
Transform.translate(
100-
offset: Offset(
101-
0.0,
102-
notifier.hideStuff ? barHeight * 0.8 : 0.0,
100+
return KeyboardListener(
101+
focusNode: _focusNode,
102+
onKeyEvent: _handleKeyPress,
103+
child: MouseRegion(
104+
onHover: (_) {
105+
_focusNode.requestFocus();
106+
_cancelAndRestartTimer();
107+
},
108+
child: GestureDetector(
109+
onTap: () => _cancelAndRestartTimer(),
110+
child: AbsorbPointer(
111+
absorbing: notifier.hideStuff,
112+
child: Stack(
113+
children: [
114+
if (_displayBufferingIndicator)
115+
_chewieController?.bufferingBuilder?.call(context) ??
116+
const Center(
117+
child: CircularProgressIndicator(),
118+
)
119+
else
120+
_buildHitArea(),
121+
Column(
122+
mainAxisAlignment: MainAxisAlignment.end,
123+
children: <Widget>[
124+
if (_subtitleOn)
125+
Transform.translate(
126+
offset: Offset(
127+
0.0,
128+
notifier.hideStuff ? barHeight * 0.8 : 0.0,
129+
),
130+
child: _buildSubtitles(
131+
context, chewieController.subtitle!),
103132
),
104-
child:
105-
_buildSubtitles(context, chewieController.subtitle!),
106-
),
107-
_buildBottomBar(context),
108-
],
109-
),
110-
],
133+
_buildBottomBar(context),
134+
],
135+
),
136+
],
137+
),
111138
),
112139
),
113140
),
@@ -117,6 +144,7 @@ class _MaterialDesktopControlsState extends State<MaterialDesktopControls>
117144
@override
118145
void dispose() {
119146
_dispose();
147+
_focusNode.dispose();
120148
super.dispose();
121149
}
122150

@@ -155,7 +183,7 @@ class _MaterialDesktopControlsState extends State<MaterialDesktopControls>
155183
}) {
156184
final options = <OptionItem>[
157185
OptionItem(
158-
onTap: () async {
186+
onTap: (context) async {
159187
Navigator.pop(context);
160188
_onSpeedButtonTap();
161189
},
@@ -471,7 +499,8 @@ class _MaterialDesktopControlsState extends State<MaterialDesktopControls>
471499
}
472500

473501
Future<void> _initialize() async {
474-
_subtitleOn = chewieController.subtitle?.isNotEmpty ?? false;
502+
_subtitleOn = chewieController.showSubtitles &&
503+
(chewieController.subtitle?.isNotEmpty ?? false);
475504
controller.addListener(_updateState);
476505

477506
_updateState();
@@ -570,6 +599,36 @@ class _MaterialDesktopControlsState extends State<MaterialDesktopControls>
570599
});
571600
}
572601

602+
void _seekBackward() {
603+
_seekRelative(
604+
const Duration(
605+
seconds: -10,
606+
),
607+
);
608+
}
609+
610+
void _seekForward() {
611+
_seekRelative(
612+
const Duration(
613+
seconds: 10,
614+
),
615+
);
616+
}
617+
618+
void _seekRelative(Duration relativeSeek) {
619+
_cancelAndRestartTimer();
620+
final position = _latestValue.position + relativeSeek;
621+
final duration = _latestValue.duration;
622+
623+
if (position < Duration.zero) {
624+
controller.seekTo(Duration.zero);
625+
} else if (position > duration) {
626+
controller.seekTo(duration);
627+
} else {
628+
controller.seekTo(position);
629+
}
630+
}
631+
573632
Widget _buildProgressBar() {
574633
return Expanded(
575634
child: MaterialVideoProgressBar(
@@ -596,8 +655,9 @@ class _MaterialDesktopControlsState extends State<MaterialDesktopControls>
596655
playedColor: Theme.of(context).colorScheme.secondary,
597656
handleColor: Theme.of(context).colorScheme.secondary,
598657
bufferedColor:
599-
Theme.of(context).colorScheme.surface.withOpacity(0.5),
600-
backgroundColor: Theme.of(context).disabledColor.withOpacity(.5),
658+
Theme.of(context).colorScheme.surface.withOpacityCompat(0.5),
659+
backgroundColor:
660+
Theme.of(context).disabledColor.withOpacityCompat(0.5),
601661
),
602662
draggableProgressBar: chewieController.draggableProgressBar,
603663
),

‎lib/src/material/widgets/options_dialog.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class _OptionsDialogState extends State<OptionsDialog> {
2828
itemCount: widget.options.length,
2929
itemBuilder: (context, i) {
3030
return ListTile(
31-
onTap: widget.options[i].onTap,
31+
onTap: () => widget.options[i].onTap(context),
3232
leading: Icon(widget.options[i].iconData),
3333
title: Text(widget.options[i].title),
3434
subtitle: widget.options[i].subtitle != null

‎lib/src/models/option_item.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ class OptionItem {
88
this.subtitle,
99
});
1010

11-
Function()? onTap;
12-
IconData iconData;
13-
String title;
14-
String? subtitle;
11+
final void Function(BuildContext context) onTap;
12+
final IconData iconData;
13+
final String title;
14+
final String? subtitle;
1515

1616
OptionItem copyWith({
17-
Function()? onTap,
17+
Function(BuildContext context)? onTap,
1818
IconData? iconData,
1919
String? title,
2020
String? subtitle,

‎pubspec.yaml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
name: chewie
22
description: A video player for Flutter with Cupertino and Material play controls
3-
version: 1.8.5
3+
version: 1.11.0
44
homepage: https://github.com/fluttercommunity/chewie
55

66
environment:
7-
sdk: '>=3.3.0 <4.0.0'
8-
flutter: ">=3.19.0"
7+
sdk: '>=3.6.0 <4.0.0'
8+
flutter: ">=3.27.0"
99

1010
dependencies:
11-
cupertino_icons: ^1.0.5
11+
cupertino_icons: ^1.0.8
1212
flutter:
1313
sdk: flutter
1414
provider: ^6.1.2
15-
video_player: ^2.9.1
16-
wakelock_plus: ^1.2.8
15+
video_player: ^2.9.3
16+
wakelock_plus: ^1.2.10
1717

1818
dev_dependencies:
1919
flutter_test:
2020
sdk: flutter
21-
flutter_lints: ^4.0.0
21+
flutter_lints: ^5.0.0
2222

2323
flutter:
2424
uses-material-design: true

0 commit comments

Comments
 (0)
Please sign in to comment.