video_viewer 1.2.1 copy "video_viewer: ^1.2.1" to clipboard
video_viewer: ^1.2.1 copied to clipboard

outdated

Multiplatform minimalist video viewer with spectacular user experience.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:video_viewer/video_viewer.dart';

void main() => runApp(App());

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    SystemChrome.setSystemUIOverlayStyle(
        SystemUiOverlayStyle(statusBarIconBrightness: Brightness.light));
    return MaterialApp(
      home: HomePage(),
      title: 'Video Viewer Example',
      debugShowCheckedModeBanner: false,
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey[100],
      body: Center(child: HLSVideoExample()),
    );
  }
}

class SerieExample extends StatefulWidget {
  SerieExample({Key? key}) : super(key: key);

  @override
  _SerieExampleState createState() => _SerieExampleState();
}

class _SerieExampleState extends State<SerieExample> {
  final VideoViewerController controller = VideoViewerController();
  final Map<String, Map<String, String>> database = {
    "第1集": {
      "超清":
          "https://ks-xpc4.xpccdn.com/ff6c8b38-79a6-4040-b220-899e579a5c28.mp4",
    },
    "第2集": {
      "超清":
          "https://hls.syrme.top/hls/5ac73019-c1bc-4f5b-b295-52ff6a29a9e7/playlist.m3u8"
    },
    "第3集": {
      "超清":
          "https://sfux-ext.sfux.info/hls/chapter/105/1588724110/1588724110.m3u8"
    },
    "第4集": {
      "超清":
          "https://ks-xpc4.xpccdn.com/ff6c8b38-79a6-4040-b220-899e579a5c28.mp4"
    },
    "第5集": {
      "超清":
          "https://ks-xpc4.xpccdn.com/3368e540-b1f9-4832-8167-a2334da19b5c.mp4"
    },
  };

  final Map<String, String> thumbnails = {
    "第1集":
        "https://cloudfront-us-east-1.images.arcpublishing.com/semana/FUM2RCCVW5EL5LQYCDVC6VRO2U.jpg",
    "第2集":
        "https://www.elcomercio.com/files/article_main/uploads/2019/03/29/5c9e3ddfc85ca.jpeg",
    "第3集":
        "https://cloudfront-us-east-1.images.arcpublishing.com/semana/FUM2RCCVW5EL5LQYCDVC6VRO2U.jpg",
    "第4集":
        "https://www.elcomercio.com/files/article_main/uploads/2019/03/29/5c9e3ddfc85ca.jpeg",
    "第5集":
        "https://cloudfront-us-east-1.images.arcpublishing.com/semana/FUM2RCCVW5EL5LQYCDVC6VRO2U.jpg",
  };

  String episode = "";
  late MapEntry<String, Map<String, String>> initial;

  @override
  void initState() {
    initial = database.entries.first;
    episode = initial.key;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey[100],
      body: Center(
        child: VideoViewer(
          source: VideoSource.fromNetworkVideoSources(initial.value),
          controller: controller,
          language: VideoViewerLanguage.es,
          style: VideoViewerStyle(
            header: Builder(
              builder: (innerContext) {
                return Container(
                  width: double.infinity,
                  padding: EdgeInsets.symmetric(horizontal: 10, vertical: 20),
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        "Game of Thrones: $episode",
                        style: TextStyle(
                          fontSize: 18,
                          fontWeight: FontWeight.bold,
                          color: Colors.white,
                        ),
                      ),
                    ],
                  ),
                );
              },
            ),
            settingsStyle: SettingsMenuStyle(
              paddingBetween: 10,
              items: [
                SettingsMenuItem(
                  themed: SettingsMenuItemThemed(
                    title: "Episodes",
                    subtitle: episode,
                    icon: Icon(
                      Icons.view_module_outlined,
                      color: Colors.white,
                    ),
                  ),
                  secondaryMenuWidth: 300,
                  secondaryMenu: Padding(
                    padding: EdgeInsets.only(top: 5),
                    child: Center(
                      child: Container(
                        child: Wrap(
                          spacing: 20,
                          runSpacing: 10,
                          children: [
                            for (var entry in database.entries)
                              episodeImage(entry)
                          ],
                        ),
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Widget episodeImage(MapEntry<String, Map<String, String>> entry) {
    return ClipRRect(
      borderRadius: BorderRadius.all(Radius.circular(5)),
      child: Material(
        child: InkWell(
          onTap: () async {
            final episodeName = entry.key;
            final qualities = entry.value;

            Map<String, VideoSource> sources;
            String url = qualities.entries.first.value;

            if (url.contains("m3u8")) {
              sources = await VideoSource.fromM3u8PlaylistUrl(
                url,
                formatter: (quality) =>
                    quality == "Auto" ? "Auto" : "${quality.split("x").last}p",
              );
            } else {
              sources = VideoSource.fromNetworkVideoSources(qualities);
            }

            final video = sources.entries.first;

            await controller.changeSource(
              inheritValues: false, //RESET SPEED TO NORMAL AND POSITION TO ZERO
              source: video.value,
              name: video.key,
            );

            controller.closeAllSecondarySettingsMenus();
            controller.source = sources;
            episode = episodeName;
            setState(() {});
          },
          child: Stack(
            alignment: AlignmentDirectional.bottomCenter,
            children: [
              Container(
                width: 80,
                height: 80,
                color: Colors.white,
                child: Image.network(thumbnails[entry.key]!, fit: BoxFit.cover),
              ),
              Text(
                entry.key,
                style: TextStyle(
                  color: Colors.white,
                  fontWeight: FontWeight.bold,
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

// class PortraitVideoExample extends StatelessWidget {
//   const PortraitVideoExample({Key key}) : super(key: key);

//   @override
//   Widget build(BuildContext context) {
//     final Map<String, String> src = {
//       "1":
//           "https://assets.mixkit.co/videos/preview/mixkit-mysterious-pale-looking-fashion-woman-at-winter-39878-large.mp4",
//       "2":
//           "https://assets.mixkit.co/videos/preview/mixkit-winter-fashion-cold-looking-woman-concept-video-39874-large.mp4",
//     };

//     return VideoViewer(
//       language: VideoViewerLanguage.es,
//       source: VideoSource.fromNetworkVideoSources(src),
//       style: VideoViewerStyle(
//         settingsStyle: SettingsMenuStyle(paddingBetween: 10),
//       ),
//     );
//   }
// }

class HLSVideoExample extends StatelessWidget {
  const HLSVideoExample({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<Map<String, VideoSource>>(
      future: VideoSource.fromM3u8PlaylistUrl(
        "https://sfux-ext.sfux.info/hls/chapter/105/1588724110/1588724110.m3u8",
        formatter: (quality) =>
            quality == "Auto" ? "Auto" : "${quality.split("x").last}p",
      ),
      builder: (_, data) {
        return data.hasData
            ? VideoViewer(
                language: VideoViewerLanguage.ar,
                source: data.data!,
                onFullscreenFixLandscape: true,
                style: VideoViewerStyle(
                  thumbnail: Image.network(
                    "https://play-lh.googleusercontent.com/aA2iky4PH0REWCcPs9Qym2X7e9koaa1RtY-nKkXQsDVU6Ph25_9GkvVuyhS72bwKhN1P",
                  ),
                ),
              )
            : CircularProgressIndicator();
      },
    );
  }
}

class WebVTTSubtitleVideoExample extends StatelessWidget {
  const WebVTTSubtitleVideoExample({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return VideoViewer(
      source: {
        "WebVTT Caption": VideoSource(
          intialSubtitle: "Spanish",
          video: VideoPlayerController.network(
            "https://www.speechpad.com/proxy/get/marketing/samples/standard-captions-example.mp4",
          ),
          subtitle: {
            "English": VideoViewerSubtitle.network(
              "https://felipemurguia.com/assets/txt/WEBVTT_English.txt",
            ),
            "Spanish": VideoViewerSubtitle.network(
              "https://felipemurguia.com/assets/txt/WEBVTT_Spanish.txt",
            ),
          },
        )
      },
    );
  }
}

class SubRipSubtitleVideoExample extends StatelessWidget {
  const SubRipSubtitleVideoExample({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final String content = '''
      1
      00:00:03,400 --> 00:00:06,177
      In this lesson, we're going to
      be talking about finance. And

      2
      00:00:06,177 --> 00:00:10,009
      one of the most important aspects
      of finance is interest.

      3
      00:00:10,009 --> 00:00:13,655
      When I go to a bank or some
      other lending institution

      4
      00:00:13,655 --> 00:00:17,720
      to borrow money, the bank is happy
      to give me that money. But then I'm

      5
      00:00:17,900 --> 00:00:21,480
      going to be paying the bank for the
      privilege of using their money. And that

      6
      00:00:21,660 --> 00:00:26,440
      amount of money that I pay the bank is
      called interest. Likewise, if I put money

      7
      00:00:26,620 --> 00:00:31,220
      in a savings account or I purchase a
      certificate of deposit, the bank just

      8
      00:00:31,300 --> 00:00:35,800
      doesn't put my money in a little box
      and leave it there until later. They take

      9
      00:00:35,800 --> 00:00:40,822
      my money and lend it to someone
      else. So they are using my money.

      10
      00:00:40,822 --> 00:00:44,400
      The bank has to pay me for the privilege
      of using my money.

      11
      00:00:44,400 --> 00:00:48,700
      Now what makes banks
      profitable is the rate

      12
      00:00:48,700 --> 00:00:53,330
      that they charge people to use the bank's
      money is higher than the rate that they

      13
      00:00:53,510 --> 00:01:00,720
      pay people like me to use my money. The
      amount of interest that a person pays or

      14
      00:01:00,800 --> 00:01:06,640
      earns is dependent on three things. It's
      dependent on how much money is involved.

      15
      00:01:06,820 --> 00:01:11,300
      It's dependent upon the rate of interest
      being paid or the rate of interest being

      16
      00:01:11,480 --> 00:01:17,898
      charged. And it's also dependent upon
      how much time is involved. If I have

      17
      00:01:17,898 --> 00:01:22,730
      a loan and I want to decrease the amount
      of interest that I'm going to pay, then

      18
      00:01:22,800 --> 00:01:28,040
      I'm either going to have to decrease how
      much money I borrow, I'm going to have

      19
      00:01:28,220 --> 00:01:32,420
      to borrow the money over a shorter period
      of time, or I'm going to have to find a

      20
      00:01:32,600 --> 00:01:37,279
      lending institution that charges a lower
      interest rate. On the other hand, if I

      21
      00:01:37,279 --> 00:01:41,480
      want to earn more interest on my
      investment, I'm going to have to invest

      22
      00:01:41,480 --> 00:01:46,860
      more money, leave the money in the
      account for a longer period of time, or

      23
      00:01:46,860 --> 00:01:49,970
      find an institution that will pay
      me a higher interest rate.
      ''';

    return VideoViewer(
      source: {
        "SubRip Caption": VideoSource(
          video: VideoPlayerController.network(
              "https://www.speechpad.com/proxy/get/marketing/samples/standard-captions-example.mp4"),
          subtitle: {
            "English": VideoViewerSubtitle.content(
              content,
              type: SubtitleType.srt,
            ),
          },
        )
      },
    );
  }
}
216
likes
0
pub points
85%
popularity

Publisher

verified publisherfelipemurguia.com

Multiplatform minimalist video viewer with spectacular user experience.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, gesture_x_detector, helpers, http, provider, universal_html, video_player

More

Packages that depend on video_viewer