rodydavis / signals.dart

Reactive programming made simple for Dart and Flutter
http://dartsignals.dev
Apache License 2.0
378 stars 44 forks source link

Some Signals not working #255

Closed iSaqibShafique closed 2 months ago

iSaqibShafique commented 2 months ago

In This Controller some of the Signals not working especially the isVideoPlaying Signal.

class PlayVideosController {
  youtube.YoutubePlayerController? youtubePlayerController;
  Signal<int> menuSignal = Signal(0);
  YoutubeExplode yt = YoutubeExplode();
  late FutureSignal<MusicFile> audioStream;
  void onSignal(int _) => menuSignal.value = _;
  AudioPlayer audioPlayer = AudioPlayer();
  Signal<Duration> position = Signal(Duration.zero);
  late AudioStreamInfo streamInfoMain;
  FutureSignal<String>? audioPath;
  Signal<PlayerState> playerState =
      Signal(PlayerState(false, ProcessingState.loading));
  Signal<bool> isVideoPlaying = false.toSignal();

  void onAudioPlay() async {
    if (playerState.value.playing) {
      isVideoPlaying.value = false;
      audioPlayer.pause();
    } else {
      isVideoPlaying.value = true;
      await audioPlayer.play();
    }
  }

  void onVideoPlay() {
    if (youtubePlayerController!.value.isPlaying) {
      youtubePlayerController!.pause();
    } else {
      youtubePlayerController!.play();
    }
  }

  void listenToVideoPlayerStatus() {}

  void getAudio(VideoId url) async {
    audioStream = FutureSignal(
      () async {
        StreamManifest manifest =
            await yt.videos.streamsClient.getManifest(url);
        Video video = await yt.videos.get(url);
        youtubePlayerController = youtube.YoutubePlayerController(
          initialVideoId: video.id.value,
          flags: const youtube.YoutubePlayerFlags(
              autoPlay: false,
              showLiveFullscreenButton: false,
              controlsVisibleAtStart: false,
              hideControls: true),
        );
        AudioStreamInfo streamInfo = manifest.audio.withHighestBitrate();
        AudioSource source = ClippingAudioSource(
            child: AudioSource.uri(streamInfo.url),
            tag: MediaItem(
                id: video.id.value,
                title: video.title,
                duration: video.duration,
                artist: video.author,
                artUri: Uri.tryParse(video.thumbnails.standardResUrl)));
        audioPlayer.setAudioSource(source);
        return MusicFile(video: video);
      },
    );
  }

  void dispose() {
    audioPlayer.dispose();
    if (youtubePlayerController != null) {
      youtubePlayerController!.dispose();
    }
  }

  void listentoPlayerPosition() {
    audioPlayer.positionStream.listen(
      (event) {
        position.value = event;
      },
    );
  }

  void listenToPlayerState() {
    audioPlayer.playerStateStream.listen((event) {
      playerState.value = event;
    });
  }

  void onSeekAudio(Duration seconds) {
    audioPlayer.seek(seconds);
    position.value = seconds;
  }
}

UI Code

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:signals/signals_flutter.dart';
import 'package:youtube_clone/common/widgets/future_async.dart';
import 'package:youtube_clone/common/widgets/text.dart';
import 'package:youtube_clone/view/play_videos/components.dart';
import 'package:youtube_clone/view/play_videos/controller.dart';
import 'package:youtube_clone/view/play_videos/helper.dart';
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';

PlayVideosController controller = PlayVideosController();

class PlayVideosView extends StatefulWidget {
  final VideoId id;
  const PlayVideosView({super.key, required this.id});

  @override
  State<PlayVideosView> createState() => _PlayVideosViewState();
}

class _PlayVideosViewState extends State<PlayVideosView> {
  @override
  void initState() {
    controller.getAudio(widget.id);
    controller.listentoPlayerPosition();
    controller.listenToPlayerState();
    super.initState();
  }

  @override
  void dispose() {
    // controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.white,
        title: PlayVideosComponents.menuBar(controller.menuSignal,
            onTap: (index) => controller.onSignal(index)),
        centerTitle: true,
        leading: GestureDetector(
          onTap: () => GoRouter.of(context).pop(),
          child: const Icon(CupertinoIcons.chevron_back),
        ),
      ),
      body: Watch((context) =>
          controller.menuSignal.value == 0 ? whenAudio() : whenVideo()),
    );
  }

  Widget whenAudio() {
    return FutureAsyncWidget(
      signal: controller.audioStream,
      onData: (d) {
        MusicFile musicFile = d;
        return Padding(
          padding: const EdgeInsets.symmetric(horizontal: 20),
          child: Column(
            children: [
              const SizedBox(height: 60),
              Container(
                height: 200,
                width: MediaQuery.of(context).size.width,
                decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(10),
                    image: DecorationImage(
                        image:
                            NetworkImage(musicFile.video.thumbnails.highResUrl),
                        fit: BoxFit.cover)),
              ),
              const SizedBox(height: 10),
              TextWidget(
                  text: musicFile.video.title, fontWeight: FontWeight.bold),
              const SizedBox(height: 20),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Container(
                    padding:
                        const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
                    decoration: BoxDecoration(
                        color: Colors.black.withOpacity(.1),
                        borderRadius: BorderRadius.circular(5)),
                    child: TextWidget(
                      text:
                          "👍 : ${PlayVideosHelper.formatNumber(musicFile.video.engagement.likeCount ?? 0)}",
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  Container(
                    padding:
                        const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
                    decoration: BoxDecoration(
                        color: Colors.black.withOpacity(.1),
                        borderRadius: BorderRadius.circular(5)),
                    child: const TextWidget(
                      text: "Add to Picklist",
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  Container(
                    padding:
                        const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
                    decoration: BoxDecoration(
                        color: Colors.black.withOpacity(.1),
                        borderRadius: BorderRadius.circular(5)),
                    child: const TextWidget(
                      text: "Download",
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ],
              ),
              const SizedBox(height: 30),
              Watch(
                (_) => Slider(
                  min: 0.0,
                  value: controller.position.value.inSeconds.toDouble(),
                  onChanged: (value) {
                    controller.onSeekAudio(Duration(seconds: value.toInt()));
                  },
                  max: musicFile.video.duration!.inSeconds.toDouble(),
                ),
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Watch(
                    (_) => TextWidget(
                      text: PlayVideosHelper.formatDuration(
                          controller.position.value),
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  TextWidget(
                    text: PlayVideosHelper.formatDuration(
                        musicFile.video.duration ?? Duration.zero),
                    fontWeight: FontWeight.bold,
                  )
                ],
              ),
              const SizedBox(height: 20),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  const Icon(CupertinoIcons.speaker_1_fill, size: 25),
                  const Icon(CupertinoIcons.backward, size: 25),
                  GestureDetector(
                      onTap: () => controller.onAudioPlay(),
                      child: Container(
                        height: 50,
                        width: 50,
                        decoration: const BoxDecoration(
                            color: Colors.red, shape: BoxShape.circle),
                        child: Watch((_) => Icon(
                              controller.playerState.value.playing == true
                                  ? Icons.pause
                                  : Icons.play_arrow,
                              size: 30,
                              color: Colors.white,
                            )),
                      )),
                  const Icon(CupertinoIcons.forward, size: 25),
                  const Icon(CupertinoIcons.loop, size: 25),
                ],
              )
            ],
          ),
        );
      },
    );
  }

  Widget whenVideo() {
    return Column(
      children: [
        const SizedBox(height: 60),
        YoutubePlayer(
          bufferIndicator: const CupertinoActivityIndicator(),
          showVideoProgressIndicator: false,
          controller: controller.youtubePlayerController!,
        ),
        const SizedBox(height: 20),
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 20),
          child: TextWidget(
              text: controller.youtubePlayerController!.value.metaData.title,
              fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 20),
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 20),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Container(
                padding:
                    const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
                decoration: BoxDecoration(
                    color: Colors.black.withOpacity(.1),
                    borderRadius: BorderRadius.circular(5)),
                child: TextWidget(
                  text: "👍 : ${PlayVideosHelper.formatNumber(232)}",
                  fontWeight: FontWeight.bold,
                ),
              ),
              Container(
                padding:
                    const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
                decoration: BoxDecoration(
                    color: Colors.black.withOpacity(.1),
                    borderRadius: BorderRadius.circular(5)),
                child: const TextWidget(
                  text: "Add to Picklist",
                  fontWeight: FontWeight.bold,
                ),
              ),
              Container(
                padding:
                    const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
                decoration: BoxDecoration(
                    color: Colors.black.withOpacity(.1),
                    borderRadius: BorderRadius.circular(5)),
                child: const TextWidget(
                  text: "Download",
                  fontWeight: FontWeight.bold,
                ),
              ),
            ],
          ),
        ),
        const SizedBox(height: 20),
        Watch(
          (_) => Slider(
            min: 0.0,
            value: controller.youtubePlayerController!.value.position.inSeconds
                .toDouble(),
            onChanged: (value) {
              controller.onSeekAudio(Duration(seconds: value.toInt()));
            },
            max: controller
                .youtubePlayerController!.value.metaData.duration.inSeconds
                .toDouble(),
          ),
        ),
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 20),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Watch(
                (_) => TextWidget(
                  text: PlayVideosHelper.formatDuration(
                      controller.position.value),
                  fontWeight: FontWeight.bold,
                ),
              ),
              TextWidget(
                text: PlayVideosHelper.formatDuration(controller
                    .youtubePlayerController!.value.metaData.duration),
                fontWeight: FontWeight.bold,
              )
            ],
          ),
        ),
        const SizedBox(height: 20),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            const Icon(CupertinoIcons.speaker_1_fill, size: 25),
            const Icon(CupertinoIcons.backward, size: 25),
            GestureDetector(
                onTap: () => controller.onVideoPlay(),
                child: Container(
                  height: 50,
                  width: 50,
                  decoration: const BoxDecoration(
                      color: Colors.red, shape: BoxShape.circle),
                  child: Watch((_) => Icon(
                        controller.isVideoPlaying.value
                            ? Icons.pause
                            : Icons.play_arrow,
                        size: 30,
                        color: Colors.white,
                      )),
                )),
            const Icon(CupertinoIcons.forward, size: 25),
            const Icon(CupertinoIcons.loop, size: 25),
          ],
        )
      ],
    );
  }
}
rodydavis commented 2 months ago

Can you create a minimal example in the code that shows the error you are seeing?

ideally in test case form

iSaqibShafique commented 2 months ago

@rodydavis,

This issue I have already highlighted in an issue, I think it's better to stop this issue at here.

The issue was

I encountered a similar issue while setting up a signal with a null value initially and then updating it onButtonClick. Even after assigning values to these signals (Future and Async), I faced the problem of UI not updating. However, I employed a switch functionality to handle this, as shown below:

Signal<List?> futureList = FutureSignal(() async => null);

// UI code return switch (futureList.watch(context)) { // Cases handling UI updates // // }