Skip to content

Problem with Navigator.pushReplacement() #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
aeropagz opened this issue Oct 26, 2021 · 4 comments
Closed

Problem with Navigator.pushReplacement() #21

aeropagz opened this issue Oct 26, 2021 · 4 comments

Comments

@aeropagz
Copy link

aeropagz commented Oct 26, 2021

Hello,

I just noticed if I use the pushReplacement() method of the navigator in the widget which has the WithForegroundTask implemented, the foreground service get lost. Is there any solution to this?
I am using FirebaseAuth in my App and want to provide an logout function which routes to an login page. I dont want to register the service on login.

@Dev-hwang
Copy link
Owner

What does it mean to lose foreground service? Does it simply mean that WithForegroundTask doesn't work?

The WithForegroundTask is a widget that helps prevent the app from closing immediately when the user performs an action such as back button, go to home, or turn off the screen. For the WillStartForegroundTask widget to work properly, the child must contain the Scaffold widget.

So, if you replaced the page with pushReplacement() function, you should use WillStartForegroundTask again.

  @override
  Widget build(BuildContext context) {
    return WithForegroundTask(
      child: Scaffold(
        appBar: AppBar(
          title: const Text('MainPage'),
          centerTitle: true,
        ),
        body: const Center(
          child: Text('Hello'),
        ),
      ),
    );
  }

@aeropagz
Copy link
Author

aeropagz commented Oct 28, 2021

Yes I implemented it like that. The foreground service is still displayed but it does not send any data to my app. I see in the discription text that the service gets new data from a sensor but it is not passing it through. Maybe it is possible to throw an error if the foreground service lost his stream to the parent app.

So the solution would be to close the service before pushReplacement() and then start it again anywhere else?

@Dev-hwang
Copy link
Owner

Would you like to update the plugin version to 3.2.0 and try this way?

dependencies:
  flutter_foreground_task: ^3.2.0

The example below can receive messages of ReceivePort from multiple pages without stopping the service. When stopping the service, be sure to call the ReceivePortManager.instance.dispose() function.

import 'dart:async';
import 'dart:isolate';

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

/// ReceivePortManager
class ReceivePortManager {
  ReceivePortManager._internal();
  static final instance = ReceivePortManager._internal();

  ReceivePort? _receivePort;
  StreamController? _streamController;

  Stream? get messageStream => _streamController?.stream;

  bool get isSetReceivePort => _receivePort != null;

  Future setReceivePort(ReceivePort? receivePort) async {
    await dispose();

    _streamController = StreamController.broadcast();
    _receivePort = receivePort;
    _receivePort?.listen((message) => _streamController?.add(message));
  }

  Future dispose() async {
    await _streamController?.close();
    _streamController = null;
    _receivePort?.close();
    _receivePort = null;
  }
}


// The callback function should always be a top-level function.
void startCallback() =>
    FlutterForegroundTask.setTaskHandler(FirstTaskHandler());

class FirstTaskHandler implements TaskHandler {
  int updateCount = 0;

  @override
  Future<void> onStart(DateTime timestamp, SendPort? sendPort) async {

  }

  @override
  Future<void> onEvent(DateTime timestamp, SendPort? sendPort) async {
    sendPort?.send(updateCount);
    updateCount++;
  }

  @override
  Future<void> onDestroy(DateTime timestamp) async {

  }
}

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: LoginPage());
  }
}

class LoginPage extends StatefulWidget {
  const LoginPage({Key? key}) : super(key: key);

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

class _LoginPageState extends State<LoginPage> {
  StreamSubscription? _streamSubscription;

  Future<void> _initForegroundTask() async {
    await FlutterForegroundTask.init(
      androidNotificationOptions: const AndroidNotificationOptions(
        channelId: 'notification_channel_id',
        channelName: 'Foreground Notification',
        channelDescription: 'This notification appears when the foreground service is running.',
        channelImportance: NotificationChannelImportance.LOW,
        priority: NotificationPriority.LOW,
      ),
      iosNotificationOptions: const IOSNotificationOptions(),
      foregroundTaskOptions: const ForegroundTaskOptions(),
      printDevLog: true,
    );
  }

  Future<void> _startForegroundTask() async {
    ReceivePort? receivePort;
    if (await FlutterForegroundTask.isRunningService) {
      if (!ReceivePortManager.instance.isSetReceivePort) {
        receivePort = await FlutterForegroundTask.restartService();
      }
    } else {
      receivePort = await FlutterForegroundTask.startService(
        notificationTitle: 'Foreground Service is running',
        notificationText: 'Tap to return to the app',
        callback: startCallback,
      );
    }

    if (receivePort != null) {
      await ReceivePortManager.instance.setReceivePort(receivePort);
    }

    _streamSubscription = ReceivePortManager.instance.messageStream?.listen((event) {
      if (event is int)
        print('receive1 updateCount: $event');
    });
  }

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance?.addPostFrameCallback((_) async {
      await _initForegroundTask();
      await _startForegroundTask();
    });
  }

  @override
  void dispose() {
    _streamSubscription?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return WithForegroundTask(
      child: Scaffold(
        appBar: AppBar(
          title: const Text('Login Page'),
          centerTitle: true,
        ),
        body: Center(
          child: ElevatedButton(
            child: const Text('goto MainPage'),
            onPressed: () {
              final route = MaterialPageRoute(builder: (_) => const MainPage());
              Navigator.of(context).pushReplacement(route);
            },
          ),
        ),
      ),
    );
  }
}

class MainPage extends StatefulWidget {
  const MainPage({Key? key}) : super(key: key);

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

class _MainPageState extends State<MainPage> {
  StreamSubscription? _streamSubscription;

  @override
  void initState() {
    super.initState();
    _streamSubscription = ReceivePortManager.instance.messageStream?.listen((message) {
      if (message is int)
        print('receive2 updateCount: $message');
    });
  }

  @override
  void dispose() {
    _streamSubscription?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return WithForegroundTask(
      child: Scaffold(
        appBar: AppBar(
          title: const Text('Main Page'),
          centerTitle: true,
        ),
        body: Center(
          child: ElevatedButton(
            child: const Text('goto LoginPage'),
            onPressed: () {
              final route = MaterialPageRoute(builder: (_) => const LoginPage());
              Navigator.of(context).pushReplacement(route);
            },
          ),
        ),
      ),
    );
  }
}

@aeropagz
Copy link
Author

aeropagz commented Nov 3, 2021

Wow thnx for the effort. It's working now. Big thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants