【Flutter】youtube_player_flutterというパッケージのサンプルをriverpod, state_notifier, freezedで書き直す

サンプルlib内にはmain.dartとvideo_list.dartの二つがあり今回はmain.dartの方を書き直す。

状態管理の部分を外部に出しただけです。

ウィジットの部分は↓(HookWidget内にfinal以外の定義をするのはよくない?)

import 'dart:developer';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:mytube/provider/youtube_player_provider.dart';
import 'package:mytube/view/video_list.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';

class MyYoutubePlayerPage extends HookWidget {
 final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey();
 final ypsp = useProvider(youtubePlayerStateProvider);
 final yps = useProvider(youtubePlayerStateProvider.state);
 TextEditingController _idController = TextEditingController();
 
 @override
 Widget build(BuildContext context) {
   useEffect(() {
     Future.microtask(() => ypsp.initState());
     return () {
       ypsp.dispose();
       _idController.dispose();
     };
   }, []);
   
   return YoutubePlayerBuilder(
     onExitFullScreen: () {
       // The player forces portraitUp after exiting fullscreen. This overrides the behaviour.
       SystemChrome.setPreferredOrientations(DeviceOrientation.values);
     },
     player: YoutubePlayer(
       controller: yps.youtubePlayerController,
       showVideoProgressIndicator: true,
       progressIndicatorColor: Colors.blueAccent,
       topActions: <Widget>[
         const SizedBox(width: 8.0),
         Expanded(
           child: Text(
             yps.youtubePlayerController.metadata.title,
             style: const TextStyle(
               color: Colors.white,
               fontSize: 18.0,
             ),
             overflow: TextOverflow.ellipsis,
             maxLines: 1,
           ),
         ),
         IconButton(
           icon: const Icon(
             Icons.settings,
             color: Colors.white,
             size: 25.0,
           ),
           onPressed: () {
             log('Settings Tapped!');
           },
         ),
       ],
       onReady: () {
         ypsp.onReady();
       },
       onEnded: (data) {
         yps.youtubePlayerController.load(
             yps.ids[(yps.ids.indexOf(data.videoId) + 1) % yps.ids.length]);
         _showSnackBar('Next Video Started!');
       },
     ),
     builder: (context, player) => Scaffold(
       key: _scaffoldKey,
       appBar: AppBar(
         leading: Padding(
           padding: const EdgeInsets.only(left: 12.0),
           child: Image.asset(
             'images/ypf.png',
             fit: BoxFit.fitWidth,
           ),
         ),
         title: const Text(
           'Youtube Player Flutter',
           style: TextStyle(color: Colors.white),
         ),
         actions: [
           IconButton(
             icon: const Icon(Icons.video_library),
             onPressed: () => Navigator.push(
               context,
               CupertinoPageRoute(
                 builder: (context) => VideoList(),
               ),
             ),
           ),
         ],
       ),
       body: ListView(
         children: [
           player,
           Padding(
             padding: const EdgeInsets.all(8.0),
             child: Column(
               crossAxisAlignment: CrossAxisAlignment.stretch,
               children: [
                 _space,
                 _text('Title', yps.videoMetaData.title),
                 _space,
                 _text('Channel', yps.videoMetaData.author),
                 _space,
                 _text('Video Id', yps.videoMetaData.videoId),
                 _space,
                 Row(
                   children: [
                     _text(
                       'Playback Quality',
                       yps.youtubePlayerController.value.playbackQuality,
                     ),
                     const Spacer(),
                     _text(
                       'Playback Rate',
                       '${yps.youtubePlayerController.value.playbackRate}x  ',
                     ),
                   ],
                 ),
                 _space,
                 TextField(
                   enabled: yps.isPlayerReady,
                   controller: _idController,
                   decoration: InputDecoration(
                     border: InputBorder.none,
                     hintText: 'Enter youtube \<video id\> or \<link\>',
                     fillColor: Colors.blueAccent.withAlpha(20),
                     filled: true,
                     hintStyle: const TextStyle(
                       fontWeight: FontWeight.w300,
                       color: Colors.blueAccent,
                     ),
                     suffixIcon: IconButton(
                       icon: const Icon(Icons.clear),
                       onPressed: () => _idController.clear(),
                     ),
                   ),
                 ),
                 _space,
                 Row(
                   children: [
                     _loadCueButton('LOAD', context),
                     const SizedBox(width: 10.0),
                     _loadCueButton('CUE', context),
                   ],
                 ),
                 _space,
                 Row(
                   mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                   children: [
                     IconButton(
                       icon: const Icon(Icons.skip_previous),
                       onPressed: yps.isPlayerReady
                           ? () => yps.youtubePlayerController.load(yps.ids[
                               (yps.ids.indexOf(yps.youtubePlayerController
                                           .metadata.videoId) -
                                       1) %
                                   yps.ids.length])
                           : null,
                     ),
                     IconButton(
                       icon: Icon(
                         yps.youtubePlayerController.value.isPlaying
                             ? Icons.pause
                             : Icons.play_arrow,
                       ),
                       onPressed: yps.isPlayerReady
                           ? () {
                               yps.youtubePlayerController.value.isPlaying
                                   ? yps.youtubePlayerController.pause()
                                   : yps.youtubePlayerController.play();
                               // setState(() {});
                             }
                           : null,
                     ),
                     IconButton(
                       icon: Icon(
                           yps.muted ? Icons.volume_off : Icons.volume_up),
                       onPressed: yps.isPlayerReady
                           ? () {
                               yps.muted
                                   ? yps.youtubePlayerController.unMute()
                                   : yps.youtubePlayerController.mute();
                               ypsp.changeMuted();
                             }
                           : null,
                     ),
                     FullScreenButton(
                       controller: yps.youtubePlayerController,
                       color: Colors.blueAccent,
                     ),
                     IconButton(
                       icon: const Icon(Icons.skip_next),
                       onPressed: yps.isPlayerReady
                           ? () => yps.youtubePlayerController.load(yps.ids[
                               (yps.ids.indexOf(yps.youtubePlayerController
                                           .metadata.videoId) +
                                       1) %
                                   yps.ids.length])
                           : null,
                     ),
                   ],
                 ),
                 _space,
                 Row(
                   children: <Widget>[
                     const Text(
                       "Volume",
                       style: TextStyle(fontWeight: FontWeight.w300),
                     ),
                     Expanded(
                       child: Slider(
                         inactiveColor: Colors.transparent,
                         value: yps.volume,
                         min: 0.0,
                         max: 100.0,
                         divisions: 10,
                         label: '${(yps.volume).round()}',
                         onChanged: yps.isPlayerReady
                             ? (value) {
                                 ypsp.setVolume(value);
                                 yps.youtubePlayerController
                                     .setVolume(yps.volume.round());
                               }
                             : null,
                       ),
                     ),
                   ],
                 ),
                 _space,
                 AnimatedContainer(
                   duration: const Duration(milliseconds: 800),
                   decoration: BoxDecoration(
                     borderRadius: BorderRadius.circular(20.0),
                     color: _getStateColor(yps.playerState),
                   ),
                   padding: const EdgeInsets.all(8.0),
                   child: Text(
                     yps.playerState.toString(),
                     style: const TextStyle(
                       fontWeight: FontWeight.w300,
                       color: Colors.white,
                     ),
                     textAlign: TextAlign.center,
                   ),
                 ),
               ],
             ),
           ),
         ],
       ),
     ),
   );
 }
 Widget _text(String title, String value) {
   return RichText(
     text: TextSpan(
       text: '$title : ',
       style: const TextStyle(
         color: Colors.blueAccent,
         fontWeight: FontWeight.bold,
       ),
       children: [
         TextSpan(
           text: value ?? '',
           style: const TextStyle(
             color: Colors.blueAccent,
             fontWeight: FontWeight.w300,
           ),
         ),
       ],
     ),
   );
 }
 Color _getStateColor(PlayerState state) {
   switch (state) {
     case PlayerState.unknown:
       return Colors.grey[700];
     case PlayerState.unStarted:
       return Colors.pink;
     case PlayerState.ended:
       return Colors.red;
     case PlayerState.playing:
       return Colors.blueAccent;
     case PlayerState.paused:
       return Colors.orange;
     case PlayerState.buffering:
       return Colors.yellow;
     case PlayerState.cued:
       return Colors.blue[900];
     default:
       return Colors.blue;
   }
 }
 Widget get _space => const SizedBox(height: 10);
 Widget _loadCueButton(String action, BuildContext context) {
   return Expanded(
     child: MaterialButton(
       color: Colors.blueAccent,
       onPressed: yps.isPlayerReady
           ? () {
               if (_idController.text.isNotEmpty) {
                 var id = YoutubePlayer.convertUrlToId(
                   _idController.text,
                 );
                 if (action == 'LOAD') yps.youtubePlayerController.load(id);
                 if (action == 'CUE') yps.youtubePlayerController.cue(id);
                 FocusScope.of(context).requestFocus(FocusNode());
               } else {
                 _showSnackBar('Source can\'t be empty!');
               }
             }
           : null,
       disabledColor: Colors.grey,
       disabledTextColor: Colors.black,
       child: Padding(
         padding: const EdgeInsets.symmetric(vertical: 14.0),
         child: Text(
           action,
           style: const TextStyle(
             fontSize: 18.0,
             color: Colors.white,
             fontWeight: FontWeight.w300,
           ),
           textAlign: TextAlign.center,
         ),
       ),
     ),
   );
 }
 void _showSnackBar(String message) {
   _scaffoldKey.currentState.showSnackBar(
     SnackBar(
       content: Text(
         message,
         textAlign: TextAlign.center,
         style: const TextStyle(
           fontWeight: FontWeight.w300,
           fontSize: 16.0,
         ),
       ),
       backgroundColor: Colors.blueAccent,
       behavior: SnackBarBehavior.floating,
       elevation: 1.0,
       shape: RoundedRectangleBorder(
         borderRadius: BorderRadius.circular(50.0),
       ),
     ),
   );
 }
}

stateは

import 'package:flutter/foundation.dart'; // *.freezed.dartで必要なのでimportしておく
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';

part 'youtube_player.freezed.dart';

@freezed
abstract class YoutubePlayerState with _$YoutubePlayerState {
 const factory YoutubePlayerState({
   PlayerState playerState,
   YoutubeMetaData videoMetaData,
   YoutubePlayerController youtubePlayerController,
   @Default([]) List<String> ids,
   @Default(100) double volume,
   @Default(false) bool muted,
   @Default(false) bool isPlayerReady,
 }) = _YoutubePlayerState;
}

providerは

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/all.dart';
import 'package:kikutube/entity/youtube_player.dart';
import 'package:state_notifier/state_notifier.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';

final youtubePlayerStateProvider =
   StateNotifierProvider((_) => YoutubePlayerStateProvider());

class YoutubePlayerStateProvider extends StateNotifier<YoutubePlayerState> {
 YoutubePlayerStateProvider() : super(const YoutubePlayerState());
 
 void initState() {
   state = state.copyWith(ids: [
     'nPt8bK2gbaU',
     'gQDByCdjUXw',
     'iLnmTe5Q2Qw',
     '_WoCV4c6XOE',
     'KmzdUe0RSJo',
     '6jZDSSZZxjQ',
     'p2lYr3vM_1w',
     '7QUtEmBT_-w',
     '34_PXCzGw1M',
   ]);
   state = state.copyWith(
       youtubePlayerController: YoutubePlayerController(
     initialVideoId: state.ids.first,
     flags: const YoutubePlayerFlags(
       mute: false,
       autoPlay: true,
       disableDragSeek: false,
       loop: false,
       isLive: false,
       forceHD: false,
       enableCaption: true,
     ),
   )..addListener(listener));
   state = state.copyWith(videoMetaData: const YoutubeMetaData());
   state = state.copyWith(playerState: PlayerState.unknown);
 }
 
 void listener() {
   if (state.isPlayerReady &&
       mounted &&
       !state.youtubePlayerController.value.isFullScreen) {
     state = state.copyWith(
         playerState: state.youtubePlayerController.value.playerState);
     state =
         state.copyWith(videoMetaData: state.youtubePlayerController.metadata);
   }
 }

 void deactivate() {
   // Pauses video while navigating to next page.
   state.youtubePlayerController.pause();
 }

 void dispose() {
   state.youtubePlayerController.dispose(); 
 }
 
 void onReady() {
   state = state.copyWith(isPlayerReady: true);
 }
 
 void changeMuted() {
   state = state.copyWith(muted: !state.muted);
 }
 
 void setVolume(value) {
   state = state.copyWith(volume: value);
 }
}

いいなと思ったら応援しよう!