You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter电台播放器切换页面时isPlaying布尔值重置问题求助

Flutter电台播放器页面切换后播放状态重置的解决方案

问题描述

我用Flutter开发一款电台播放器应用,通过GNav实现两个页面的切换。启动电台播放后,播放按钮会切换为暂停按钮,但切换页面再返回播放器页面时,音频明明还在播放,按钮却变回了播放按钮。排查后发现是每次切换页面都会重新执行bool isPlaying = false;,导致状态被重置。请问怎么让isPlaying在返回页面时不被重置?

解决方案

方法一:用AutomaticKeepAliveClientMixin保持页面状态

这个方法能让Home页面在切换时不被销毁,从而保留内部的状态变量。

修改home.dart的代码:

import 'package:flutter/material.dart';
import 'package:radio_player/radio_player.dart';

class Home extends StatefulWidget {
  Home({super.key});

  @override
  State<Home> createState() => _HomeState();
}

// 混入AutomaticKeepAliveClientMixin
class _HomeState extends State<Home> with AutomaticKeepAliveClientMixin {
  RadioPlayer _radioPlayer = RadioPlayer();
  List<String>? metadata;
  bool isPlaying = false;

  // 重写该方法,返回true表示需要保持状态
  @override
  bool get wantKeepAlive => true;

  void initRadioPlayer() {
    _radioPlayer.setChannel(
      title: 'My Radio',
      url: 'https://myradio.url',
      imagePath: 'assets/radio.jpg',
    );

    _radioPlayer.stateStream.listen((value) {
      setState(() {
        isPlaying = value;
      });
    });

    _radioPlayer.metadataStream.listen((value) {
      setState(() {
        metadata = value;
      });
    });
  }

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

  @override
  Widget build(BuildContext context) {
    // 必须调用super.build(context),否则状态保持不生效
    super.build(context);
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          FutureBuilder(
            future: _radioPlayer.getArtworkImage(),
            builder: (BuildContext context, AsyncSnapshot snapshot) {
              Image artwork;
              if (snapshot.hasData) {
                artwork = snapshot.data;
              } else {
                artwork = Image.asset(
                  'assets/radio.jpg',
                  fit: BoxFit.cover,
                );
              }
              return SizedBox(
                height: 180,
                width: 180,
                child: ClipRRect(
                  child: artwork,
                  borderRadius: BorderRadius.circular(15.0),
                ),
              );
            },
          ),
          const SizedBox(height: 20),
          const Text(
            'Radio Orient Live',
            softWrap: false,
            overflow: TextOverflow.fade,
            style: TextStyle(fontWeight: FontWeight.bold, fontSize: 24),
          ),
          Text(
            metadata?[1] ?? '',
            softWrap: false,
            overflow: TextOverflow.fade,
            style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
          ),
          SizedBox(height: 20),
          FloatingActionButton(
            onPressed: () {
              isPlaying ? _radioPlayer.pause() : _radioPlayer.play();
            },
            tooltip: 'Control button',
            child: Icon(
              isPlaying ? Icons.pause_rounded : Icons.play_arrow_rounded,
            ),
          ),
        ],
      ),
    );
  }
}

方法二:将播放状态提升到父组件(推荐)

把播放状态和RadioPlayer实例移到顶层的RadioApp组件中,这样页面切换时状态不会丢失,还能方便在多个页面共享播放状态。

1. 修改main.dart:

import 'package:flutter/material.dart';
import 'package:radio_orient/components/bottom_nav_bar.dart';
import 'package:radio_orient/pages/home.dart';
import 'package:radio_orient/pages/more_info.dart';
import 'package:radio_player/radio_player.dart';

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

class RadioApp extends StatefulWidget {
  @override
  _RadioAppState createState() => _RadioAppState();
}

class _RadioAppState extends State<RadioApp> {
  int _selectedIndex = 0;
  late RadioPlayer _radioPlayer;
  bool isPlaying = false;
  List<String>? metadata;

  void navigateBottomBar(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  void initRadioPlayer() {
    _radioPlayer = RadioPlayer();
    _radioPlayer.setChannel(
      title: 'My Radio',
      url: 'https://myradio.url',
      imagePath: 'assets/radio.jpg',
    );

    _radioPlayer.stateStream.listen((value) {
      setState(() {
        isPlaying = value;
      });
    });

    _radioPlayer.metadataStream.listen((value) {
      setState(() {
        metadata = value;
      });
    });
  }

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

  @override
  Widget build(BuildContext context) {
    // 动态传递状态和方法给Home页面
    final pages = [
      Home(
        isPlaying: isPlaying,
        metadata: metadata,
        onPlayPause: () {
          isPlaying ? _radioPlayer.pause() : _radioPlayer.play();
        },
        getArtworkImage: _radioPlayer.getArtworkImage,
      ),
      const MoreInfo()
    ];

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Radio Orient'),
          backgroundColor: Color.fromARGB(255, 36, 120, 85),
        ),
        body: pages[_selectedIndex],
        backgroundColor: const Color.fromRGBO(207, 185, 151, 1),
        bottomNavigationBar: MyBottomNavBar(
          onTabChange: (index) => navigateBottomBar(index),
        ),
      ),
    );
  }
}

2. 修改home.dart为无状态组件:

import 'package:flutter/material.dart';
import 'package:radio_player/radio_player.dart';

class Home extends StatelessWidget {
  final bool isPlaying;
  final List<String>? metadata;
  final VoidCallback onPlayPause;
  final Future<Image> Function() getArtworkImage;

  const Home({
    super.key,
    required this.isPlaying,
    required this.metadata,
    required this.onPlayPause,
    required this.getArtworkImage,
  });

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          FutureBuilder(
            future: getArtworkImage(),
            builder: (BuildContext context, AsyncSnapshot snapshot) {
              Image artwork;
              if (snapshot.hasData) {
                artwork = snapshot.data;
              } else {
                artwork = Image.asset(
                  'assets/radio.jpg',
                  fit: BoxFit.cover,
                );
              }
              return SizedBox(
                height: 180,
                width: 180,
                child: ClipRRect(
                  child: artwork,
                  borderRadius: BorderRadius.circular(15.0),
                ),
              );
            },
          ),
          const SizedBox(height: 20),
          const Text(
            'Radio Orient Live',
            softWrap: false,
            overflow: TextOverflow.fade,
            style: TextStyle(fontWeight: FontWeight.bold, fontSize: 24),
          ),
          Text(
            metadata?[1] ?? '',
            softWrap: false,
            overflow: TextOverflow.fade,
            style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
          ),
          SizedBox(height: 20),
          FloatingActionButton(
            onPressed: onPlayPause,
            tooltip: 'Control button',
            child: Icon(
              isPlaying ? Icons.pause_rounded : Icons.play_arrow_rounded,
            ),
          ),
        ],
      ),
    );
  }
}

方法对比

  • 方法一改动小,适合只需要保持单个页面状态的简单场景。
  • 方法二更符合Flutter状态管理的最佳实践,适合需要在多个页面共享播放状态的场景(比如在Info页面显示当前播放状态)。

内容的提问来源于stack exchange,提问作者Ah.Kh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 14:04:54