skip to Main Content

I am currently working on a Flutter project and utilizing the circular_countdown_timer package to display a countdown timer. However, I am facing a challenge in implementing a feature where I want to play a "Tic" audio during the last 5 seconds of the countdown. Specifically, I would like to play a "Tic" audio for each last second of the countdown, summing up to a total of 5 "Tic" sounds.

I am seeking guidance on how to achieve this functionality within my Flutter application. How can I trigger the audio playback during the last 5 seconds of the countdown, ensuring that the "Tic" audio is played for each last second that counts down?

Any insights, suggestions, or code samples related to integrating audio playback during the countdown using the circular_countdown_timer package would be highly appreciated.

Thank you in advance for your support and expertise in resolving this challenge.

Widget:

 /// Returns Body as per mode or mode conditions
  Widget returnSmQuizBodyWidget({required BuildContext context}) {
    if (_soloModeLogic.currentMode == 'Exclusive'{
      return const SMWinScreen();
    } else {
      return Column(
        children: [
          //-----------------------   Question Container
          Stack(
            clipBehavior: Clip.none,
            children: [
              //-----------------------   Question Frame
              returnQuestionFrame(context: context),

              //-----------------------   Timer
              Positioned(
                      right: 0,
                      left: 0,
                      bottom: -25,
                      child: CircularCountDownTimer(
                        duration: _soloModeLogic.quizTime,
                        initialDuration: 0,
                        controller: _soloModeLogic.countDownController,
                        width: 5.w,
                        height: 7.h,
                        ringColor: Colors.grey.shade400,
                        ringGradient: null,
                        fillColor: _soloModeLogic.isFreezeColor
                            ? Colors.lightBlueAccent
                            : Colors.red,
                        fillGradient: null,
                        backgroundColor: const Color(0xfff5f5f5),
                        backgroundGradient: null,
                        strokeWidth: 4.w,
                        strokeCap: StrokeCap.butt,
                        textStyle: TextStyle(
                          fontSize: 20.sp,
                          color: _soloModeLogic.isFreezeColor
                              ? Colors.lightBlueAccent
                              : Colors.red,
                          fontWeight: FontWeight.bold,
                        ),
                        textFormat: CountdownTextFormat.S,
                        isReverse: true,
                        isReverseAnimation: false,
                        isTimerTextShown: true,
                        autoStart: true,
                        onStart: () {},
                        onChange: (v) {},
                        onComplete: () =>
                            _soloModeLogic.onTimeOut(context: context),
                      ),
                    ),
            ],
          ),
        ],
      );
    }
  }

2

Answers


  1. I have created a demo app.
    based on your code.
    It does what you need.
    One important thing I came to realize is that during the last five seconds, instead of playing/doing pause each second, which causes sync loss between counter and audio player, start playing audio from the last 5th second and stop at or before zero and select suitable audio file that takes appx 1 second to play a sound.
    You can change the audio speed to adjust and you can also look out for alternate audio player library.
    You can do anything with the code.(Fork/copy-pase,etc)
    Thanks 🙂

    Edit :
    I realized that in future, I may delete the app from the repo, hence pasting important parts of the code here.
    I have assets/tick.mp3 file in the root directory.
    This is my pubspec.yaml file.

    name: solution_76450197
    description: A new Flutter project.
    publish_to: 'none' # Remove this line if you wish to publish to pub.dev
    version: 1.0.0+1
    
    environment:
      sdk: '>=3.0.3 <4.0.0'
    dependencies:
      flutter:
        sdk: flutter
      cupertino_icons: ^1.0.2
      circular_countdown_timer: ^0.2.3
      audioplayers: ^4.1.0
    
    dev_dependencies:
      flutter_test:
        sdk: flutter
      flutter_lints: ^2.0.0
    flutter:
      uses-material-design: true
    
      assets:
        - assets/
    

    This is my main.yaml file.

    import 'dart:async';
    
    import 'package:audioplayers/audioplayers.dart';
    import 'package:circular_countdown_timer/circular_countdown_timer.dart';
    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
            useMaterial3: true,
          ),
          home: const MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      const MyHomePage({super.key, required this.title});
      final String title;
    
      @override
      State<MyHomePage> createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      int quizTime = 10;
      int thresholdTime = 5;
      final CountDownController _controller = CountDownController();
      final player = AudioPlayer();
    
      bool isAudioPlaying = false;
    
    
      // Please not that it's not called at every second change but instead
      // called on more smaller unit of second second.
      // Hence it could be called multiple times per second
      void onCountdownChange(v) {
        int vInt = int.parse(v);
        if (!isAudioPlaying && vInt > 0 && vInt < thresholdTime) {
          // Do not call setUpdate here, we don't need to rerender UI here
          isAudioPlaying = true;
           player.seek(const Duration(milliseconds: 0));
           player.resume();
        }
      }
    
      @override
      void initState() {
        super.initState();
        player.setSource(AssetSource('tick.mp3'));
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            backgroundColor: Theme.of(context).colorScheme.inversePrimary,
            title: Text(widget.title),
          ),
          body: Center(
            child: CircularCountDownTimer(
                duration: quizTime,
                initialDuration: 0,
                controller: _controller,
                width: 50,
                height: 50,
                ringColor: Colors.grey.shade400,
                ringGradient: null,
                fillColor: _controller.isPaused
                    ? Colors.lightBlueAccent
                    : Colors.redAccent,
                fillGradient: null,
                backgroundColor: const Color(0xfff5f5f5),
                backgroundGradient: null,
                strokeWidth: 4,
                strokeCap: StrokeCap.butt,
                textStyle: TextStyle(
                  fontSize: 20,
                  color: _controller.isPaused
                      ? Colors.blueAccent
                      : Colors.red,
                  fontWeight: FontWeight.bold,
                ),
                textFormat: CountdownTextFormat.S,
                isReverse: true,
                isReverseAnimation: false,
                isTimerTextShown: true,
                autoStart: false,
                onStart: () {},
                onChange: onCountdownChange,
                onComplete: () {
                  player.pause();
                  setState(() {
                    isAudioPlaying = false;
                  });
                  print("on Complete");
                }
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed:  (){
              player.pause();
              isAudioPlaying = false;
              _controller.restart(duration: quizTime);
            },
            tooltip: 'Start',
            child: Text("Start"),
          ), // This trailing comma makes auto-formatting nicer for build methods.
        );
      }
    }
    
    Login or Signup to reply.
  2. When it comes to audio handling in Flutter, I always go for the just_audio package. It does what’s needed with ease.

    Your CircularCountDownTimer has an onChange callback that should be sufficient for your use-case of playing a sound every second, in the last 5 seconds.

    Step 1
    Install the just_audio package from here: https://pub.dev/packages/just_audio

    Step 2
    In your onChange callback, you should be able to get the current duration (e.g. 23) and compare it to the final value (e.g. 0 if counting down), and if the difference is less than or equal to 4, play an audio. Since I’m not a 100% sure about the working of the countdown timer package, I will use pseudo-code.

    late AudioPlayer player;
    
    void initAudio() {
      player = AudioPlayer();
      await player.setAsset('assets/audio/tic.mp3'); // add whatever audio you have
    }
    
    CircularCountDownTimer(
      duration: _soloModeLogic.quizTime,
      initialDuration: 0,
      controller: _soloModeLogic.countDownController,
      width: 5.w,
      height: 7.h,
      ringColor: Colors.grey.shade400,
      ringGradient: null,
      fillColor: _soloModeLogic.countDownController.isPaused
          ? Colors.lightBlueAccent
          : Colors.redAccent,
      fillGradient: null,
      backgroundColor: const Color(0xfff5f5f5),
      backgroundGradient: null,
      strokeWidth: 4.w,
      strokeCap: StrokeCap.butt,
      textStyle: TextStyle(
        fontSize: 20.sp,
        color: _soloModeLogic.countDownController.isPaused
            ? Colors.blueAccent
            : Colors.red,
        fontWeight: FontWeight.bold,
      ),
      textFormat: CountdownTextFormat.S,
      isReverse: true,
      isReverseAnimation: false,
      isTimerTextShown: true,
      autoStart: true,
      onStart: () {},
      onChange: () {
        // considering you're counting from 0 to _soloModeLogic.quizTime
        player.seek(Duration(second: 0));     // set track position to 0
        if (_soloModeLogic.quizTime - currentDuration <= 4) {   // if duration is in last 5 seconds
          player.play();
        }
        
      },
      onComplete: () =>
          player.stop();
          _soloModeLogic.onTimeOut(context: context),
    ),
    

    I hope this made sense. Feel free to ask anything if you’re not sure.

    Once again, I’m not sure about how the currentDuration will be accessed as I have never used the circular_countdown_timer package.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search