fluttercommunity / chewie

The video player for Flutter with a heart of gold
MIT License
1.95k stars 1.01k forks source link

Video src cannot be switched in full-screen mode #849

Open sparksworld opened 3 months ago

sparksworld commented 3 months ago
Future<void> _initController() async {
    var list = widget.list;
    var originIndex = widget.originIndex;
    var teleplayIndex = widget.teleplayIndex;

    final originPlayList = list?[originIndex]?.linkList;
    final url = originPlayList?[teleplayIndex].link ?? '';

    setState(() {
      _loading = true;
      _error = false;
    });
    _videoPlayerController = VideoPlayerController.networkUrl(
      Uri.parse(url),
    )
      ..addListener(_listener)
      ..initialize().then(
        (value) {
          double? videoAspectRatio = _videoPlayerController?.value.aspectRatio;
          setState(() {
            _loading = false;
            _error = false;
          });
          _chewieController = ChewieController(
            videoPlayerController: _videoPlayerController!,
            allowFullScreen: true,
            autoPlay: true,
            looping: false,
            autoInitialize: true,
            startAt: Duration(seconds: widget.startAt ?? 0),
            showControlsOnInitialize: true,
            aspectRatio: videoAspectRatio ?? _aspectRatio,
            additionalOptions: (context) {
              return <OptionItem>[
                OptionItem(
                  onTap: () {
                    var originIndex = widget.originIndex;
                    var teleplayIndex = widget.teleplayIndex;
                    if (teleplayIndex < originPlayList!.length - 1) {
                      widget.callback(originIndex, teleplayIndex + 1);
                    }
                  },
                  title: 'Next video',
                ),
              ];
            },
           };
          setState(() { });
        },
      ).catchError((error) {
        // print(error);
        setState(() {
          _loading = false;
          _error = true;
          _errorMessage = error.message;
        });
      });
  }

  @override
  void didUpdateWidget(covariant Player oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.teleplayIndex != oldWidget.teleplayIndex) {
      _videoPlayerController?.dispose();
      _initController();
    }
  }

Result

Abnormal black screen is stuck

Error info

Another exception was thrown: A PlayerNotifier was used after being disposed.
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: A PlayerNotifier was used after being disposed.
Once you have called dispose() on a PlayerNotifier, it can no longer be used.
#0      ChangeNotifier.debugAssertNotDisposed.<anonymous closure> (package:flutter/src/foundation/change_notifier.dart:179:9)
#1      ChangeNotifier.debugAssertNotDisposed (package:flutter/src/foundation/change_notifier.dart:186:6)
#2      ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:412:27)
#3      PlayerNotifier.hideStuff= (package:chewie/src/notifiers/player_notifier.dart:19:5)
#4      _CupertinoControlsState._startHideTimer.<anonymous closure>.<anonymous closure> (package:chewie/src/cupertino/cupertino_controls.dart:787:18)
#5      State.setState (package:flutter/src/widgets/framework.dart:1203:30)
#6      _CupertinoControlsState._startHideTimer.<anonymous closure> (package:chewie/src/cupertino/cupertino_controls.dart:786:7)
#7      Timer._createTimer.<anonymous c<…>
Chensp171254 commented 3 months ago

I got the same problem . How to resolve it

Chensp171254 commented 3 months ago

I got the same problem . How to resolve it

I can only move the [Chewie] Widget to a stable parent widget.

sparksworld commented 3 months ago

I got the same problem . How to resolve it

I can only move the [Chewie] Widget to a stable parent widget.

Did you resolve the issue after the move? I replaced it with the better_player module.

Chensp171254 commented 3 months ago

I got the same problem . How to resolve it

I can only move the [Chewie] Widget to a stable parent widget.

Did you resolve the issue after the move? I replaced it with the better_player module. I tried various ways to solve it, but it didn't work I have decided to handle the interactive interface myself

zoozobib commented 3 months ago

same problem

zoozobib commented 3 months ago

i had some test by offical example (ChewieDemo) , in the official example, ChewieDemo is loaded as the entree node: void main() { runApp( const ChewieDemo(), ); }

  1. If the "home" property of MaterialAPP is set to ChewieDemo() in a new page , when playing a full-screen video, the video switch fails and the screen stays still ( This is a more general scene, because ChewieDemo cannot always be used as the entry widget )

  2. but , use "builder" property with MaterialAPP , playing a full-screen video and switch another one is normal

MaterialApp( // home: Scaffold( // body: Stack( // children: [ // ChewieDemo(), // ], // ), // ), builder: (_,child){ return ChewieDemo(); }, )

So far, I don't know what the problem is and need to debug it further

Chensp171254 commented 3 months ago

I got the same problem . How to resolve it

I can only move the [Chewie] Widget to a stable parent widget.

Did you resolve the issue after the move? I replaced it with the better_player module. I tried various ways to solve it, but it didn't work I have decided to handle the interactive interface myself

当进入全屏后,假如外部包裹【Chiewe】的父组件【Dispose】,问题就会出现。我接到的产品需求是在【List】列表页中的每个【item】播放视频,在这种情况下全屏模式会随着【item】的复用出现问题。所以我禁用了全屏模式,添加了按钮跳转到一个独立的大屏播放界面过渡。在单一的新界面使用全屏模式是稳定的。

zoozobib commented 3 months ago

finally , i rebuild this module to resolve it that full screen change source problem . it means that i must be implement to fullscreen widget and state management and controller bar by myself.

duongtricn2c commented 3 months ago

I got the same problem

AhmedAlaaGenina commented 2 months ago

finally , i rebuild this module to resolve it that full screen change source problem . it means that i must be implement to fullscreen widget and state management and controller bar by myself.

how to you solve it please ?

zoozobib commented 1 month ago

finally , i rebuild this module to resolve it that full screen change source problem . it means that i must be implement to fullscreen widget and state management and controller bar by myself.

how to you solve it please ?

This is brief implementation that about solve it :

  1. create 2 view page from State Widget and refer to Hero widget on the page
  2. on the Ontap event of the fullscreen icon to make sure keep playing video source in the second view page landscapeLeft Mode , that is why include the identical Hero Widget Tag
  3. it must be implement to SeekBar code related to scene by your self even the PlayPause and PlaySpeed and Duration or Other state
  4. i has using Stream Widget by sync time tick and player positions
  5. at last that animate the status bar to invisible mode
junixapp commented 3 weeks ago

same issue

azhezzzz commented 3 weeks ago

I guess that since full screen is entering a new page, the changes in page1 parameters cannot be responded to page2 in time (I tried to pass count from page1 to page2 and modify it, and it turned out that only page1 responded to the change).

Therefore, just encapsulate the parameters through ChangeNotifier and use them.

Here is a simple demo

class TestScreen extends StatefulWidget {
  const TestScreen({super.key});

  @override
  State<TestScreen> createState() => _TestScreenState();
}

class _TestScreenState extends State<TestScreen> {
  MyChewieControllerNotifier myChewieControllerNotifier =
      MyChewieControllerNotifier();

  toggleLink() {
    myChewieControllerNotifier.updateChewieController(
      onTap: _handleTap,
      myChangeNotifier: myChewieControllerNotifier,
    );
  }

  void _handleTap() {
    toggleLink();
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        myChewieControllerNotifier.chewieController == null
            ? const Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  CircularProgressIndicator(),
                  SizedBox(height: 20),
                  Text('Loading'),
                ],
              )
            : Expanded(
                child: Chewie(
                    controller: myChewieControllerNotifier.chewieController!)),
        TextButton(onPressed: _handleTap, child: Text('切换链接')),
      ],
    );
  }
}

class TestFullScreen extends StatefulWidget {
  final MyChewieControllerNotifier myChangeNotifier;
  final void Function() onTest1;
  const TestFullScreen({
    super.key,
    required this.onTest1,
    required this.myChangeNotifier,
  });

  @override
  State<TestFullScreen> createState() => _TestFullScreenState();
}

class _TestFullScreenState extends State<TestFullScreen> {
  @override
  void initState() {
    widget.myChangeNotifier.addListener(_update);
    super.initState();
  }

  _update() {
    setState(() {});
  }

  @override
  void setState(VoidCallback fn) {
    if (!mounted) return;
    super.setState(fn);
  }

  @override
  void dispose() {
    // TODO: implement dispose
    widget.myChangeNotifier.removeListener(_update);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      // appBar: AppBar(title: Text("测试全屏页面")),
      floatingActionButton: FloatingActionButton(onPressed: widget.onTest1),
      body: Container(
        alignment: Alignment.center,
        color: Colors.red,
        child: Chewie(controller: widget.myChangeNotifier.chewieController!),
      ),
    );
  }
}

class MyChewieControllerNotifier extends ChangeNotifier {
  ChewieController? chewieController;

  int currPlayIndex = 0;

  List<String> srcs = [
    "https://assets.mixkit.co/videos/preview/mixkit-spinning-around-the-earth-29351-large.mp4",
    "https://assets.mixkit.co/videos/preview/mixkit-daytime-city-traffic-aerial-view-56-large.mp4",
    "https://assets.mixkit.co/videos/preview/mixkit-a-girl-blowing-a-bubble-gum-at-an-amusement-park-1226-large.mp4"
  ];

  updateChewieController({
    required void Function() onTap,
    required MyChewieControllerNotifier myChangeNotifier,
  }) {
    currPlayIndex += 1;
    if (currPlayIndex >= srcs.length) {
      currPlayIndex = 0;
    }
    String link = srcs[currPlayIndex];
    print("切换了链接  $link");
    chewieController?.videoPlayerController.dispose();
    chewieController?.dispose();
    chewieController = ChewieController(
      videoPlayerController: VideoPlayerController.networkUrl(
        Uri.parse(srcs[currPlayIndex]),
      ),
      autoPlay: true,
      autoInitialize: true,
      aspectRatio: 16 / 9,
      routePageBuilder:
          (context, animation, secondaryAnimation, controllerProvider) =>
              AnimatedBuilder(
        animation: animation,
        builder: (BuildContext context, Widget? child) {
          return TestFullScreen(
            onTest1: onTap,
            myChangeNotifier: myChangeNotifier,
          );
        },
      ),
    );
    notifyListeners();
  }
}
loicbrigardis commented 2 days ago

Based on your answer @azhezzzz , an alternative with ChangeNotifier.


List<String> srcs = [
  "https://samplelib.com/lib/preview/mp4/sample-5s.mp4",
  "https://samplelib.com/lib/preview/mp4/sample-10s.mp4",
  "https://samplelib.com/lib/preview/mp4/sample-5s.mp4",
];

class NotFullScreen extends StatefulWidget {
  const NotFullScreen({super.key});

  @override
  State<NotFullScreen> createState() => _NotFullScreenState();
}

class _NotFullScreenState extends State<NotFullScreen> {
  final VideoNotifier _videoNotifier = VideoNotifier();
  int indexVideoPlayed = 0;
  bool videoIsPlaying = false;

  @override
  void initState() {
    super.initState();
    loadVideo(indexVideoPlayed);
  }

  void loadVideo(indexVideoPlayed) async {
    final String videoUrl = srcs[indexVideoPlayed];
    final videoPlayerController = VideoPlayerController.networkUrl(Uri.parse(videoUrl));
    await videoPlayerController.initialize();
    setState(() {});
    _videoNotifier.initChewieController(videoPlayerController);

    videoPlayerController.addListener(() {
      if (videoPlayerController.value.isCompleted && videoIsPlaying) {
        // videoIsPlaying prevent listener to call multiple time at end.
        videoIsPlaying = false;
        indexVideoPlayed++;
        loadVideo(indexVideoPlayed);
      }
      videoIsPlaying = true;
    });
  }

  @override
  void dispose() {
    _videoNotifier.chewieController?.dispose();
    _videoNotifier.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: _videoNotifier.chewieController != null
          ? SizedBox(
              height: 400,
              child: ChewieVideo(
                notifier: _videoNotifier,
              ),
            )
          : const Center(
              child: CircularProgressIndicator(),
            ),
    );
  }
}

class ChewieVideo extends StatelessWidget {
  final VideoNotifier _videoNotifier;

  const ChewieVideo({super.key, notifier}) : _videoNotifier = notifier;

  @override
  Widget build(BuildContext context) {
    return ListenableBuilder(
      listenable: _videoNotifier,
      builder: (context, child) => Chewie(
        controller: _videoNotifier.chewieController!,
      ),
    );
  }
}

class VideoNotifier extends ChangeNotifier {
  ChewieController? _chewieController;

  ChewieController? get chewieController => _chewieController;

  void initChewieController(videoPlayerController) {
    _chewieController = ChewieController(
      autoPlay: true,
      videoPlayerController: videoPlayerController,
      routePageBuilder: (context, animation, secondaryAnimation, controllerProvider) {
        return ChewieVideo(notifier: this);
      },
    );
    notifyListeners();
  }
}