Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

flutter music keeps playing when I stop playing song with function and change navigation bar index

I have a list containing music on each item. If I close the screen im changing the index of the currently located BottomNavigationPage and calling the stop function for stopping the audio, but exactly this doesnt work sometimes. If the song is in loading process or user is really fast, song will continue beeing played on different pages.

Im using https://pub.dev/packages/audioplayers plugin.

This is my minified code example full working demo: EDIT::

So i followed up the hint of @Ringil providing a full working example with the actual issue im facing in here. This is my code:

import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  int currentIndex =0;

  @override
  Widget build(BuildContext context) {

    // Page selector for tab list
  void _selectPage(int index) {
    print('page index: $index');
    setState(() {
      currentIndex = index;
    });
  }

  // Routes list for tab navigation Android
  final List<Widget> _pages = [
    ScreenA(),
    ScreenB(func: _selectPage),
  ];

    return Scaffold(
      appBar: AppBar(),
      body: _pages[currentIndex],
      bottomNavigationBar: SafeArea(
        child: BottomNavigationBar(
          onTap: _selectPage,
          iconSize: 22,
          currentIndex: currentIndex,
          type: BottomNavigationBarType.fixed,
          items: [
            BottomNavigationBarItem(
              backgroundColor: Theme.of(context).primaryColor,
              icon: Icon(Icons.description),
              label: 'ScreenA',
            ),
            BottomNavigationBarItem(
                backgroundColor: Theme.of(context).primaryColor,
                icon: Icon(Icons.ac_unit_outlined),
                label: 'ScreenB'),
          ],
        ),
      ),
    );
  }
}

class ScreenA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Text('HOME'),
    );
  }
}

class ScreenB extends StatefulWidget {
  Function func;
  ScreenB({Key key, @required this.func}) : super(key: key);
  @override
  _ScreenBState createState() => _ScreenBState();
}

class _ScreenBState extends State<ScreenB> {
  var audioPlayer = AudioPlayer(playerId: 'player');
  var audioIndex = 0;
  var audioFiles = [
    "https://docs.google.com/uc?export=open&id=1SaJWqfQuHnFtL7uqrzfYG31hzOnqDM3r",
    "https://docs.google.com/uc?export=open&id=1FZkFMjQyWguAl0RMAsYDEZ07c_Qf7gjz",
    "https://docs.google.com/uc?export=open&id=1GqrwQ3eRuiil0p-Na_R1tMAvggp9YrbH",
  ];
  var _controller = PageController();

  @override
  void initState() {
    super.initState();

// starting player
    startPlayer();

  }

  startPlayer() {
    play();
  }

// Player play class set globalindex
  Future<void> play() async {
    int result = await audioPlayer.play(audioFiles[audioIndex]);
    if (result == 1) {
      // success
    }
  }

  // Stop song instance of player
  Future<void> stop() async {
    int result = await audioPlayer.stop();
    if (result == 1) {
      // success
    }
  }

  // disposing listener if not needed or users navigates away from
  @override
  void dispose() {
    super.dispose();
    audioPlayer.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: [
          IconButton(
            icon: Icon(Icons.access_alarm_sharp),
            onPressed: () async {
              await stop();
              widget.func(0);
            },
          ),
        ],
      ),
      body: PageView.custom(
          dragStartBehavior: DragStartBehavior.start,
          controller: _controller,
          physics: NeverScrollableScrollPhysics(),
          scrollDirection: Axis.horizontal,
          childrenDelegate: SliverChildBuilderDelegate((ctx, pageIndex) =>
              GestureDetector(
                  onPanUpdate: (details) async {
                    if (details.delta.dx < 0) {
                      _controller.nextPage(
                          duration: Duration(milliseconds: 200),
                          curve: Curves.easeInOut);
                      await stop();

                      setState(() {
                        audioIndex = pageIndex;
                        play();
                      });
                    }
                  },
                  child: Center(
                      child: Container(
                          width: 200,
                          height: 200,
                          color: Colors.red,
                          child: Text(audioFiles[audioIndex])))))),
    );
  }
}

And a short video showing the issue. If we go to the carousel screen, navigate threw songs and closing the screen before they have been loaded, they will be played on a different screen which I dont want. I also dont want to somehow "block" the user sliding threw the carousel until the song is loaded. Video: https://streamable.com/e/ycxwob

Basically I shrinked my code up to the minimum possible. I took the code of Ringil because in his demo everything works perfectly fine. Then I added more and more code from me, until I noticed the error again. Now here we are. Basically there is a difference in the example audio files from hin and mine. My files arent big from filesize aspect but they seem to be longer, which gives us the delay Im expecting all the time. It seems like if the song isnt loaded before closing or changing the index, the error occurs.

like image 455
Marcel Dz Avatar asked Oct 24 '20 11:10

Marcel Dz


People also ask

How do I turn off audio cache on flutter?

To Stop: audioPlayer. stop(); You will have to get the instance of AudioPlayer to stop the file, simply use await on play() to get the instance and using this, you can call stop() .

What is playify flutter?

Playify is a Flutter plugin for play/pause/seek songs, fetching music metadata, and browsing music library. Playify was built using iOS's Media Player Framework to fetch and play music from iOS's Music Library. Currently supports only iOS, but Android support is being developed. PR's are welcomed. Checkout the documentation.

How to play music in flutter with audiomanager?

AudioManager provides us start () method to play the music. It takes a URL, title, description, cover, and auto. To play the music file using assets file you just need to change the song URL to assets file path. To fetch the music files form the external storage we will use a FutureBuilder as FlutterAudioQuery returns a future .

How to fetch music files from external storage in flutter?

Fetching music files from our external storage. We will use flutter_audio_query to fetch the music form our external storage (eg. mobile phone, memory card, etc). A Flutter plugin, Android only at this moment, that allows you query for audio metadata info about artists, albums…

Why is the audio garbled when I play a video?

Sign in to your account When playing a video on Android, 3rd party music players will continue to play their content along with the audio output from the video. This results in garbled audio for the end-user.


Video Answer


2 Answers

add: "with WidgetsBindingObserver"

class Screen extends StatefulWidget with WidgetsBindingObserver

and you'll get access to the App Lifecycle State:

void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.paused) {
      player.pause();
    }
  }
like image 198
Malbolged Avatar answered Sep 29 '22 11:09

Malbolged


It sounds like you're trying to build a carousel slider. I would suggest instead of trying to build it you use something like the carousel_slider package. You should be able to just call play in the callback for onPageChanged.

However, getting back to your specific issue, I think the issue is that you're likely getting the page index and the globalSongIndex out of sync.

It seems like you have something like this globally:

  var audioIndex = 0;
  var audioFiles = [
    "1.mp3",
    "2.mp3",
    "3.mp3"
  ];

with a play function like this:

  Future<void> play() async {
    int result = await audioPlayer.play(audioFiles[audioIndex]);
  }

Then to ensure your gesture and your pageView on your pageView controller are in sync you need to make sure when you call the nextPage function on the PageController you also ensure your state variable is also updated to the nextPage value. I'm not exactly sure on how the specifics of Provider.of<Songs> works, but you likely need to force it to a specific value.

SliverChildBuilderDelegate((ctx, pageIndex) => GestureDetector(
    onPanUpdate: (details) async {
      if (details.delta.dx < 0) {
        _controller.nextPage(
            duration: Duration(milliseconds: 200),
            curve: Curves.easeInOut);
        await stop();

        setState(() {
          audioIndex = pageIndex;
          play();
        });
      }
    },
    child: Center(child: Text(audioFiles[audioIndex])))

Full working example:

import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  var audioPlayer = AudioPlayer(playerId: 'player');
  var audioIndex = 0;
  var audioFiles = [
    "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3",
    "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3",
    "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-3.mp3"
  ];
  var _controller = PageController();

  @override
  void initState() {
    super.initState();

// starting player
    startPlayer();
  }

  startPlayer() {
    play();
  }

// Player play class set globalindex
  Future<void> play() async {
    int result = await audioPlayer.play(audioFiles[audioIndex]);
    if (result == 1) {
      // success
    }
  }

  // Stop song instance of player
  Future<void> stop() async {
    int result = await audioPlayer.stop();
    if (result == 1) {
      // success
    }
  }

  // disposing listener if not needed or users navigates away from
  @override
  void dispose() {
    super.dispose();
    audioPlayer.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: PageView.custom(
            dragStartBehavior: DragStartBehavior.start,
            controller: _controller,
            physics: NeverScrollableScrollPhysics(),
            scrollDirection: Axis.horizontal,
            childrenDelegate:
                SliverChildBuilderDelegate((ctx, pageIndex) => GestureDetector(
                    onPanUpdate: (details) async {
                      if (details.delta.dx < 0) {
                        _controller.nextPage(
                            duration: Duration(milliseconds: 200),
                            curve: Curves.easeInOut);
                        await stop();

                        setState(() {
                          audioIndex = pageIndex;
                          play();
                        });
                      }
                    },
                    child: Center(child: Text(audioFiles[audioIndex]))))));
  }
}
like image 21
Ringil Avatar answered Sep 29 '22 11:09

Ringil