Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
sparksworld opened this issue Aug 16, 2024 · 14 comments
Open

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

sparksworld opened this issue Aug 16, 2024 · 14 comments

Comments

@sparksworld
Copy link

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
Copy link

I got the same problem . How to resolve it

@Chensp171254
Copy link

I got the same problem . How to resolve it

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

@sparksworld
Copy link
Author

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
Copy link

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
Copy link

same problem

@zoozobib
Copy link

zoozobib commented Sep 1, 2024

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
Copy link

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
Copy link

zoozobib commented Sep 2, 2024

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
Copy link

I got the same problem

@AhmedAlaaGenina
Copy link

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
Copy link

zoozobib commented Oct 6, 2024

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
Copy link

junixapp commented Nov 8, 2024

same issue

@azhezzzz
Copy link

azhezzzz commented Nov 8, 2024

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
Copy link

loicbrigardis commented Dec 2, 2024

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();
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants