5

我有一个包含每个项目的音乐的列表。如果我关闭屏幕,我会更改index当前位置的屏幕BottomNavigationPage并调用停止功能来停止音频,但这有时确实不起作用。如果歌曲正在加载过程中或用户速度非常快,歌曲将继续在不同的页面上播放。

我正在使用https://pub.dev/packages/audioplayers插件。

这是我的缩小代码示例完整工作演示:编辑::

所以我跟进了@Ringil 的提示,提供了一个完整的工作示例以及我在这里面临的实际问题。这是我的代码:

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])))))),
    );
  }
}

还有一段短视频显示了这个问题。如果我们转到轮播屏幕,在加载之前浏览已播放的歌曲并关闭屏幕,它们将在我不想要的不同屏幕上播放。我也不想以某种方式“阻止”用户滑动扔旋转木马,直到加载歌曲。视频:https ://streamable.com/e/ycxwob

基本上,我将我的代码缩减到了最低限度。我拿了 Ringil 的代码,因为在他的演示中一切都很好。然后我添加了越来越多的代码,直到我再次注意到错误。现在我们到了。基本上,来自 hin 和 mine 的示例音频文件有所不同。从文件大小方面来看,我的文件并不大,但它们似乎更长,这给了我们一直期待的延迟。似乎如果在关闭或更改索引之前未加载歌曲,则会发生错误。

4

2 回答 2

4

添加:“使用 WidgetsBindingObserver”

class Screen extends StatefulWidget with WidgetsBindingObserver

您将可以访问应用程序生命周期状态:

void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.paused) {
      player.pause();
    }
  }
于 2020-10-27T16:20:54.093 回答
1

听起来您正在尝试构建一个轮播滑块。我建议不要尝试构建它,而是使用carousel_slider包之类的东西。您应该能够只play调用onPageChanged.

但是,回到您的具体问题,我认为问题在于您可能会获得页面索引并且globalSongIndex不同步。

似乎您在全球范围内有这样的事情:

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

具有这样的播放功能:

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

然后,为了确保你的手势和你的 pageView 控制器上的 pageView 是同步的,你需要确保当你调用nextPage函数时,PageController你还要确保你的 state 变量也更新为该nextPage值。我不确定具体的Provider.of<Songs>工作原理,但您可能需要将其强制为特定值。

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])))

完整的工作示例:

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]))))));
  }
}
于 2020-10-28T13:18:21.427 回答