mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Gallery video demo (#13195)
This commit is contained in:
parent
1bd8ffbc46
commit
3a393249dc
@ -12,3 +12,4 @@ export 'material/material.dart';
|
|||||||
export 'pesto_demo.dart';
|
export 'pesto_demo.dart';
|
||||||
export 'shrine_demo.dart';
|
export 'shrine_demo.dart';
|
||||||
export 'typography_demo.dart';
|
export 'typography_demo.dart';
|
||||||
|
export 'video_demo.dart';
|
||||||
|
376
examples/flutter_gallery/lib/demo/video_demo.dart
Normal file
376
examples/flutter_gallery/lib/demo/video_demo.dart
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// 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:connectivity/connectivity.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:video_player/video_player.dart';
|
||||||
|
|
||||||
|
// TODO(sigurdm): These should not be stored here.
|
||||||
|
const String butterflyUri =
|
||||||
|
'https://flutter.github.io/assets-for-api-docs/videos/butterfly.mp4';
|
||||||
|
|
||||||
|
const String beeUri =
|
||||||
|
'https://flutter.github.io/assets-for-api-docs/videos/bee.mp4';
|
||||||
|
|
||||||
|
class VideoCard extends StatelessWidget {
|
||||||
|
final VideoPlayerController controller;
|
||||||
|
final String title;
|
||||||
|
final String subtitle;
|
||||||
|
|
||||||
|
const VideoCard({Key key, this.controller, this.title, this.subtitle})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
Widget _buildInlineVideo() {
|
||||||
|
return new Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 30.0),
|
||||||
|
child: new Center(
|
||||||
|
child: new AspectRatio(
|
||||||
|
aspectRatio: 3 / 2,
|
||||||
|
child: new Hero(
|
||||||
|
tag: controller,
|
||||||
|
child: new VideoPlayer(controller),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildFullScreenVideo() {
|
||||||
|
return new Scaffold(
|
||||||
|
appBar: new AppBar(
|
||||||
|
title: new Text(title),
|
||||||
|
),
|
||||||
|
body: new Center(
|
||||||
|
child: new AspectRatio(
|
||||||
|
aspectRatio: 3 / 2,
|
||||||
|
child: new Hero(
|
||||||
|
tag: controller,
|
||||||
|
child: new VideoPlayPause(controller),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Widget fullScreenRoutePageBuilder(BuildContext context,
|
||||||
|
Animation<double> animation, Animation<double> secondaryAnimation) {
|
||||||
|
return new AnimatedBuilder(
|
||||||
|
child: _buildFullScreenVideo(),
|
||||||
|
animation: animation,
|
||||||
|
builder: (BuildContext context, Widget child) {
|
||||||
|
// TODO(sigurdm): It seems we get a animation.value of 1.0
|
||||||
|
// at first when entering the route. Find out how to avoid
|
||||||
|
// this.
|
||||||
|
controller.setVolume(animation.value);
|
||||||
|
return child;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pushFullScreenWidget() {
|
||||||
|
final TransitionRoute<Null> route = new PageRouteBuilder<Null>(
|
||||||
|
settings: new RouteSettings(name: title, isInitialRoute: false),
|
||||||
|
pageBuilder: fullScreenRoutePageBuilder,
|
||||||
|
);
|
||||||
|
|
||||||
|
route.completed.then((Null _) {
|
||||||
|
controller.setVolume(0.0);
|
||||||
|
});
|
||||||
|
Navigator.of(context).push(route);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Card(
|
||||||
|
child: new Column(
|
||||||
|
children: <Widget>[
|
||||||
|
new ListTile(title: new Text(title), subtitle: new Text(subtitle)),
|
||||||
|
new GestureDetector(
|
||||||
|
onTap: pushFullScreenWidget,
|
||||||
|
child: _buildInlineVideo(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class VideoPlayPause extends StatefulWidget {
|
||||||
|
final VideoPlayerController controller;
|
||||||
|
|
||||||
|
const VideoPlayPause(this.controller);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State createState() => new _VideoPlayPauseState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _VideoPlayPauseState extends State<VideoPlayPause> {
|
||||||
|
FadeAnimation imageFadeAnimation;
|
||||||
|
VoidCallback listener;
|
||||||
|
|
||||||
|
_VideoPlayPauseState() {
|
||||||
|
listener = () {
|
||||||
|
setState(() {});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoPlayerController get controller => widget.controller;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
controller.addListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void deactivate() {
|
||||||
|
controller.removeListener(listener);
|
||||||
|
super.deactivate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final List<Widget> children = <Widget>[
|
||||||
|
new GestureDetector(
|
||||||
|
child: new VideoPlayer(controller),
|
||||||
|
onTap: () {
|
||||||
|
if (!controller.value.initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (controller.value.isPlaying) {
|
||||||
|
imageFadeAnimation = new FadeAnimation(
|
||||||
|
child: new Icon(Icons.pause, size: 100.0),
|
||||||
|
);
|
||||||
|
controller.pause();
|
||||||
|
} else {
|
||||||
|
imageFadeAnimation = new FadeAnimation(
|
||||||
|
child: new Icon(Icons.play_arrow, size: 100.0),
|
||||||
|
);
|
||||||
|
controller.play();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
new Center(child: imageFadeAnimation),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!controller.value.initialized) {
|
||||||
|
children.add(new Container());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Stack(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
fit: StackFit.passthrough,
|
||||||
|
children: children,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FadeAnimation extends StatefulWidget {
|
||||||
|
final Widget child;
|
||||||
|
final Duration duration;
|
||||||
|
|
||||||
|
const FadeAnimation({
|
||||||
|
this.child,
|
||||||
|
this.duration: const Duration(milliseconds: 500),
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_FadeAnimationState createState() => new _FadeAnimationState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FadeAnimationState extends State<FadeAnimation>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
AnimationController animationController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
animationController = new AnimationController(
|
||||||
|
duration: widget.duration,
|
||||||
|
vsync: this,
|
||||||
|
);
|
||||||
|
animationController.addListener(() {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
animationController.forward(from: 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void deactivate() {
|
||||||
|
animationController.stop();
|
||||||
|
super.deactivate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(FadeAnimation oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
if (oldWidget.child != widget.child) {
|
||||||
|
animationController.forward(from: 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
animationController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return animationController.isAnimating
|
||||||
|
? new Opacity(
|
||||||
|
opacity: 1.0 - animationController.value,
|
||||||
|
child: widget.child,
|
||||||
|
)
|
||||||
|
: new Container();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConnectivityOverlay extends StatefulWidget {
|
||||||
|
final Widget child;
|
||||||
|
final Completer<Null> connectedCompleter;
|
||||||
|
final GlobalKey<ScaffoldState> scaffoldKey;
|
||||||
|
|
||||||
|
const ConnectivityOverlay({
|
||||||
|
this.child,
|
||||||
|
this.connectedCompleter,
|
||||||
|
this.scaffoldKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ConnectivityOverlayState createState() => new _ConnectivityOverlayState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ConnectivityOverlayState extends State<ConnectivityOverlay> {
|
||||||
|
StreamSubscription<ConnectivityResult> connectivitySubscription;
|
||||||
|
bool connected = true;
|
||||||
|
|
||||||
|
static const Widget errorSnackBar = const SnackBar(
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
content: const ListTile(
|
||||||
|
title: const Text('No network'),
|
||||||
|
subtitle: const Text(
|
||||||
|
'To load the videos you must have an active network connection',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Stream<ConnectivityResult> connectivityStream() async* {
|
||||||
|
final Connectivity connectivity = new Connectivity();
|
||||||
|
ConnectivityResult previousResult = await connectivity.checkConnectivity();
|
||||||
|
yield previousResult;
|
||||||
|
await for (ConnectivityResult result
|
||||||
|
in connectivity.onConnectivityChanged) {
|
||||||
|
if (result != previousResult) {
|
||||||
|
yield result;
|
||||||
|
previousResult = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
connectivitySubscription = connectivityStream().listen(
|
||||||
|
(ConnectivityResult connectivityResult) {
|
||||||
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (connectivityResult == ConnectivityResult.none) {
|
||||||
|
widget.scaffoldKey.currentState.showSnackBar(errorSnackBar);
|
||||||
|
} else {
|
||||||
|
if (!widget.connectedCompleter.isCompleted) {
|
||||||
|
widget.connectedCompleter.complete(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
connectivitySubscription.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => widget.child;
|
||||||
|
}
|
||||||
|
|
||||||
|
class VideoDemo extends StatefulWidget {
|
||||||
|
const VideoDemo({Key key}) : super(key: key);
|
||||||
|
|
||||||
|
static const String routeName = '/video';
|
||||||
|
|
||||||
|
@override
|
||||||
|
_VideoDemoState createState() => new _VideoDemoState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _VideoDemoState extends State<VideoDemo>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
final VideoPlayerController butterflyController = new VideoPlayerController(
|
||||||
|
butterflyUri,
|
||||||
|
);
|
||||||
|
final VideoPlayerController beeController = new VideoPlayerController(
|
||||||
|
beeUri,
|
||||||
|
);
|
||||||
|
|
||||||
|
final GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();
|
||||||
|
final Completer<Null> connectedCompleter = new Completer<Null>();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
Future<Null> initController(VideoPlayerController controller) async {
|
||||||
|
controller.setLooping(true);
|
||||||
|
controller.setVolume(0.0);
|
||||||
|
controller.play();
|
||||||
|
await connectedCompleter.future;
|
||||||
|
await controller.initialize();
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
initController(butterflyController);
|
||||||
|
initController(beeController);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
butterflyController.dispose();
|
||||||
|
beeController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return new Scaffold(
|
||||||
|
key: scaffoldKey,
|
||||||
|
appBar: new AppBar(
|
||||||
|
title: const Text('Videos'),
|
||||||
|
),
|
||||||
|
body: new ConnectivityOverlay(
|
||||||
|
child: new ListView(
|
||||||
|
children: <Widget>[
|
||||||
|
new VideoCard(
|
||||||
|
title: 'Butterfly',
|
||||||
|
subtitle: '… flutters by',
|
||||||
|
controller: butterflyController,
|
||||||
|
),
|
||||||
|
new VideoCard(
|
||||||
|
title: 'Bee',
|
||||||
|
subtitle: '… gently buzzing',
|
||||||
|
controller: beeController,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
connectedCompleter: connectedCompleter,
|
||||||
|
scaffoldKey: scaffoldKey,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -54,7 +54,7 @@ List<GalleryItem> _buildGalleryItems() {
|
|||||||
// Demos
|
// Demos
|
||||||
new GalleryItem(
|
new GalleryItem(
|
||||||
title: 'Shrine',
|
title: 'Shrine',
|
||||||
subtitle:'Basic shopping app',
|
subtitle: 'Basic shopping app',
|
||||||
category: 'Demos',
|
category: 'Demos',
|
||||||
routeName: ShrineDemo.routeName,
|
routeName: ShrineDemo.routeName,
|
||||||
buildRoute: (BuildContext context) => new ShrineDemo(),
|
buildRoute: (BuildContext context) => new ShrineDemo(),
|
||||||
@ -73,6 +73,13 @@ List<GalleryItem> _buildGalleryItems() {
|
|||||||
routeName: AnimationDemo.routeName,
|
routeName: AnimationDemo.routeName,
|
||||||
buildRoute: (BuildContext context) => const AnimationDemo(),
|
buildRoute: (BuildContext context) => const AnimationDemo(),
|
||||||
),
|
),
|
||||||
|
new GalleryItem(
|
||||||
|
title: 'Video',
|
||||||
|
subtitle: 'Video playback',
|
||||||
|
category: 'Demos',
|
||||||
|
routeName: VideoDemo.routeName,
|
||||||
|
buildRoute: (BuildContext context) => const VideoDemo(),
|
||||||
|
),
|
||||||
// Material Components
|
// Material Components
|
||||||
new GalleryItem(
|
new GalleryItem(
|
||||||
title: 'Bottom navigation',
|
title: 'Bottom navigation',
|
||||||
|
@ -4,9 +4,11 @@ dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
collection: 1.14.3
|
collection: 1.14.3
|
||||||
intl: 0.15.2
|
intl: 0.15.2
|
||||||
|
connectivity: 0.1.0
|
||||||
string_scanner: 1.0.2
|
string_scanner: 1.0.2
|
||||||
url_launcher: 0.4.2+5
|
url_launcher: 0.4.2+5
|
||||||
cupertino_icons: 0.1.1
|
cupertino_icons: 0.1.1
|
||||||
|
video_player: 0.0.5
|
||||||
|
|
||||||
# Also update dev/benchmarks/complex_layout/pubspec.yaml
|
# Also update dev/benchmarks/complex_layout/pubspec.yaml
|
||||||
flutter_gallery_assets:
|
flutter_gallery_assets:
|
||||||
|
Loading…
Reference in New Issue
Block a user