How can I stop radio stream when navigating in Flutter?

Viewed 1077

I'm working on this flutter radio streaming app that currently works.I'm using AnimatedIcons.play_pause to start and stop the radio stream. Currently if I navigate to another page, the stream will keep playing. I'd like to stop the streaming (without using the pause button) when I navigate to another page. The reason is when you return back to the streaming page ... the page has reset back to the play buttonbool isPlaying = false; but the stream is still on and so pressing the play button will result in 2 streams playing.

Here's the full code below:

import 'dart:async';
import 'package:flutter_radio/flutter_radio.dart';

void main() => runApp(HarvestRadio());

class HarvestRadio extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<HarvestRadio>
    with SingleTickerProviderStateMixin {
  String url = "https://fm898.online/mystream.mp3";

  AnimationController _animationController;

  bool isPlaying = false;

  @override
  void initState() {
    super.initState();
    audioStart();
    _animationController =
        AnimationController(vsync: this, duration: Duration(milliseconds: 300));
  }

  @override
  void dispose() {
    super.dispose();
    _animationController.dispose();
  }

  Future<void> audioStart() async {
    await FlutterRadio.audioStart();
    print('Audio Start OK');
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Harvest Radio Online',
        debugShowCheckedModeBanner: false,
        home: Scaffold(
          appBar: AppBar(
            title: const Text('Harvest Radio Online '),
            backgroundColor: Colors.blue,
            centerTitle: true,
          ),
          body: Container(
            color: Colors.blueGrey.shade900,
            child: Column(
              children: <Widget>[
                Expanded(
                  flex: 7,
                  child: Icon(
                    Icons.radio, size: 250,
                    color: Colors.lightBlue,
                  ),
                ),
                Material(
                  color: Colors.blueGrey.shade900,
                  child: Center(
                    child: Ink(
                      decoration: const ShapeDecoration(
                        color: Colors.lightBlue,
                        shape: CircleBorder(),
                      ),
                      child: IconButton(
                        color: Colors.blueGrey.shade900,
                        iconSize: 67,
                        icon: AnimatedIcon(
                          icon: AnimatedIcons.play_pause,
                          progress: _animationController,
                        ),
                        onPressed: () => _handleOnPressed(),
                      ),
                    ),
                  ),
                ),
                SizedBox(height: 50,)

              ],
            ),
          ),
        ));
  }

  void _handleOnPressed() {
    setState(() {
      FlutterRadio.play(url: url);
      isPlaying = !isPlaying;
      isPlaying
          ? _animationController.forward()
          : _animationController.reverse();
    });
  }
}

Any help is much appreciated ... Thank you!

2 Answers

I had a similar situation, and ended up extending the routeObserver to know the users state all time:

the routeObserver.dart:

import 'package:flutter/material.dart';

class RouteObserverUtil extends RouteObserver<PageRoute<dynamic>> {
  RouteObserverUtil({this.onChange});
  final Function onChange;
  void _sendScreenView(PageRoute<dynamic> route) {
    String screenName = route.settings.name;
    onChange(screenName);
  }

  @override
  void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
    super.didPush(route, previousRoute);
    if (route is PageRoute) {
      _sendScreenView(route);
    }
  }

  @override
  void didReplace({Route<dynamic> newRoute, Route<dynamic> oldRoute}) {
    super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
    if (newRoute is PageRoute) {
      _sendScreenView(newRoute);
    }
  }

  @override
  void didPop(Route<dynamic> route, Route<dynamic> previousRoute) {
    super.didPop(route, previousRoute);
    if (previousRoute is PageRoute && route is PageRoute) {
      _sendScreenView(previousRoute);
    }
  }
}

and here is a usage example:

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

import 'package:myApp/utils/routeObserver.dart';

class _RouterScreenState extends State<RouterScreen> {
  RouteObserverUtil _routeObserver;

  @override
  void initState() {
    super.initState();
    _routeObserver = RouteObserverUtil(onChange: _onChangeScreen);
  }

  void _onChangeScreen(String screen) {
    SchedulerBinding.instance.addPostFrameCallback((_) {
      print("changed to screen: " + screen);
    });
  }

  ...

}

I found a simple solution by wrapping my MaterialApp with WillPopScope ...Here's the code:

  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: _onBackPressed,
      child: MaterialApp(
          title: 'Harvest Radio Online',
          home: Scaffold(
            appBar: AppBar(
              title: const Text('Harvest Radio Online '),
              backgroundColor: Colors.blue,
              centerTitle: true,
            ),

Then the function for _onBackPressed:

Future<bool>_onBackPressed(){
    FlutterRadio.stop();
    Navigator.pop(
        context, MaterialPageRoute(builder: (_) => Media()));
  }

I also had to add the Navigator route back to the previous page.

I know it's a bit of a hack but it works. Thank you all!

Related