skip to Main Content

I am trying to handle my nested navigators with Pop Scope.

Beacuse in the nested routes , when i tap the back button on the device the app closes.

The onInvoked method is deprecated so i couldn’t solve this problem. Also couldn’t figure out the implementation.

Here is my main_screen.dart file which handles the navigators:

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:calistimapp/widgetlar/bottom_navigation.dart';
import 'package:calistimapp/providers/main_screen_provider.dart';
import 'package:calistimapp/screens/job_search.dart';
import 'package:calistimapp/screens/cuzdan.dart';
import 'package:calistimapp/screens/islerim.dart';
import 'package:calistimapp/screens/profil.dart';
import 'package:calistimapp/screens/kategori_sayfalari/komi_garson_screen.dart';
import 'package:calistimapp/screens/kategori_sayfalari/etkinlik_gorevli_screen.dart';
import 'package:calistimapp/screens/kategori_sayfalari/otel_gorevlisi_screen.dart';
import 'package:calistimapp/screens/kategori_sayfalari/diger_isler_screen.dart';
import 'package:calistimapp/screens/job_details_screen.dart';
import 'package:calistimapp/screens/my_job_details_screen.dart';
import 'package:calistimapp/screens/pending_applications_screen.dart';
import 'package:calistimapp/screens/other_accepted_jobs_screen.dart';
import 'package:calistimapp/screens/update_worker_screen.dart';

class MainScreen extends ConsumerStatefulWidget {
  final String initialRoute;

  const MainScreen({Key? key, required this.initialRoute}) : super(key: key);

  @override
  ConsumerState<MainScreen> createState() => _MainScreenState();
}

class _MainScreenState extends ConsumerState<MainScreen> {
  late final PageController _pageController;

  @override
  void initState() {
    super.initState();
    _pageController = PageController();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      final initialPage = _getInitialPage(widget.initialRoute);
      ref.read(mainScreenProvider.notifier).setPage(initialPage);
      _pageController.jumpToPage(initialPage);
    });
  }

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

  @override
  Widget build(BuildContext context) {
    final currentIndex = ref.watch(mainScreenProvider);

    return Scaffold(
      body: PageView(
        controller: _pageController,
        physics: const NeverScrollableScrollPhysics(),
        onPageChanged: (index) {
          ref.read(mainScreenProvider.notifier).setPage(index);
        },
        children: [
          Navigator(
            onGenerateRoute: (settings) => _generateRoute(settings, const JobSearchScreen()),
          ),
          Navigator(
            onGenerateRoute: (settings) => _generateRoute(settings, const CuzdanScreen()),
          ),
          Navigator(
            onGenerateRoute: (settings) => _generateRoute(settings, const IslerimScreen()),
          ),
          Navigator(
            onGenerateRoute: (settings) => _generateRoute(settings, const ProfilScreen()),
          ),
        ],
      ),
      bottomNavigationBar: CustomBottomNavigationBar(
        currentIndex: currentIndex,
        onTap: (index) {
          _pageController.jumpToPage(index);
        },
      ),
    );
  }

  int _getInitialPage(String route) {
    switch (route) {
      case '/job_search':
        return 0;
      case '/cuzdan':
        return 1;
      case '/islerim':
        return 2;
      case '/profil':
        return 3;
      default:
        return 0;
    }
  }

  PageRouteBuilder _generateRoute(RouteSettings settings, Widget defaultPage) {
    Widget page;
    switch (settings.name) {
      case '/komi_garson':
        page = const KomiGarsonScreen();
        break;
      case '/etkinlik_gorevli':
        page = const EtkinlikGorevliScreen();
        break;
      case '/otel_gorevlisi':
        page = const OtelGorevlisiScreen();
        break;
      case '/diger_isler':
        page = const DigerIslerScreen();
        break;
      case '/job_details':
        final jobId = settings.arguments as String;
        page = JobDetailsScreen(jobId: jobId);
      
      case '/my_job_details':
        final jobId = settings.arguments as String;
        page = MyJobDetailsScreen(jobId: jobId);
      
      case '/pending_applications':
        page = const PendingApplicationsScreen();
        break;
      case '/other_accepted_jobs':
        page = const OtherAcceptedJobsScreen();
        break;
      case '/update-worker':
        page = const UpdateWorkerScreen();
        break;
      
    
       
      default:
        page = defaultPage;
    }

    return PageRouteBuilder(
      pageBuilder: (context, animation, secondaryAnimation) => page,
      transitionsBuilder: (context, animation, secondaryAnimation, child) {
        const begin = Offset(1.0, 0.0);
        const end = Offset.zero;
        const curve = Curves.easeInOut;
        var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
        var offsetAnimation = animation.drive(tween);
        return SlideTransition(
          position: offsetAnimation,
          child: child,
        );
      },
      transitionDuration: const Duration(milliseconds: 300),
    );
  }
}

I tried to go back to the previous pages with the back button.

In the nested navigators/routes , the app closes instead of going back to the previous screen.

2

Answers


  1. The WillPopScope widget can be used with the approach from above in order to properly handle the back button in Flutter application in the case nested navigators are provided. It controls what should happen when you press the back button. As the onInvoked method is deprecated nowadays, the WillPopScope is the correct approach to reach this. So, you need to wrap these nested navigators with the WillPopScope and handle the back navigation behavior manually.

    Login or Signup to reply.
  2. To handle back button presses for nested navigators in Flutter, you can wrap each Navigator with a WillPopScope widget. The WillPopScope widget allows you to intercept the back button press and determine whether to handle the pop action or pass it down to the nested navigator. You can also use the Navigator.of(context).canPop() method to determine whether there are any routes in the navigator stack that can be popped, and if so, allow the back button press to pop those routes.

    Here is your updated MainScreen file with WillPopScope added for each Navigator, handling the back button presses properly in nested navigators:

    import 'package:flutter/material.dart';
    import 'package:flutter_riverpod/flutter_riverpod.dart';
    import 'package:calistimapp/widgetlar/bottom_navigation.dart';
    import 'package:calistimapp/providers/main_screen_provider.dart';
    import 'package:calistimapp/screens/job_search.dart';
    import 'package:calistimapp/screens/cuzdan.dart';
    import 'package:calistimapp/screens/islerim.dart';
    import 'package:calistimapp/screens/profil.dart';
    import 'package:calistimapp/screens/kategori_sayfalari/komi_garson_screen.dart';
    import 'package:calistimapp/screens/kategori_sayfalari/etkinlik_gorevli_screen.dart';
    import 'package:calistimapp/screens/kategori_sayfalari/otel_gorevlisi_screen.dart';
    import 'package:calistimapp/screens/kategori_sayfalari/diger_isler_screen.dart';
    import 'package:calistimapp/screens/job_details_screen.dart';
    import 'package:calistimapp/screens/my_job_details_screen.dart';
    import 'package:calistimapp/screens/pending_applications_screen.dart';
    import 'package:calistimapp/screens/other_accepted_jobs_screen.dart';
    import 'package:calistimapp/screens/update_worker_screen.dart';
    
    class MainScreen extends ConsumerStatefulWidget {
      final String initialRoute;
    
      const MainScreen({Key? key, required this.initialRoute}) : super(key: key);
    
      @override
      ConsumerState<MainScreen> createState() => _MainScreenState();
    }
    
    class _MainScreenState extends ConsumerState<MainScreen> {
      late final PageController _pageController;
    
      @override
      void initState() {
        super.initState();
        _pageController = PageController();
        WidgetsBinding.instance.addPostFrameCallback((_) {
          final initialPage = _getInitialPage(widget.initialRoute);
          ref.read(mainScreenProvider.notifier).setPage(initialPage);
          _pageController.jumpToPage(initialPage);
        });
      }
    
      @override
      void dispose() {
        _pageController.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        final currentIndex = ref.watch(mainScreenProvider);
    
        return Scaffold(
          body: PageView(
            controller: _pageController,
            physics: const NeverScrollableScrollPhysics(),
            onPageChanged: (index) {
              ref.read(mainScreenProvider.notifier).setPage(index);
            },
            children: [
              _buildNavigator(context, '/job_search', const JobSearchScreen()),
              _buildNavigator(context, '/cuzdan', const CuzdanScreen()),
              _buildNavigator(context, '/islerim', const IslerimScreen()),
              _buildNavigator(context, '/profil', const ProfilScreen()),
            ],
          ),
          bottomNavigationBar: CustomBottomNavigationBar(
            currentIndex: currentIndex,
            onTap: (index) {
              _pageController.jumpToPage(index);
            },
          ),
        );
      }
    
      int _getInitialPage(String route) {
        switch (route) {
          case '/job_search':
            return 0;
          case '/cuzdan':
            return 1;
          case '/islerim':
            return 2;
          case '/profil':
            return 3;
          default:
            return 0;
        }
      }
    
      Widget _buildNavigator(BuildContext context, String route, Widget defaultPage) {
        return WillPopScope(
          onWillPop: () async {
            final canPop = Navigator.of(context).canPop();
            if (canPop) {
              Navigator.of(context).pop();
              return false; // Don't pop the current navigator
            }
            return true; // Allow the pop scope to pass to the parent
          },
          child: Navigator(
            onGenerateRoute: (settings) => _generateRoute(settings, defaultPage),
          ),
        );
      }
    
      PageRouteBuilder _generateRoute(RouteSettings settings, Widget defaultPage) {
        Widget page;
        switch (settings.name) {
          case '/komi_garson':
            page = const KomiGarsonScreen();
            break;
          case '/etkinlik_gorevli':
            page = const EtkinlikGorevliScreen();
            break;
          case '/otel_gorevlisi':
            page = const OtelGorevlisiScreen();
            break;
          case '/diger_isler':
            page = const DigerIslerScreen();
            break;
          case '/job_details':
            final jobId = settings.arguments as String;
            page = JobDetailsScreen(jobId: jobId);
            break;
          case '/my_job_details':
            final jobId = settings.arguments as String;
            page = MyJobDetailsScreen(jobId: jobId);
            break;
          case '/pending_applications':
            page = const PendingApplicationsScreen();
            break;
          case '/other_accepted_jobs':
            page = const OtherAcceptedJobsScreen();
            break;
          case '/update-worker':
            page = const UpdateWorkerScreen();
            break;
          default:
            page = defaultPage;
        }
    
        return PageRouteBuilder(
          pageBuilder: (context, animation, secondaryAnimation) => page,
          transitionsBuilder: (context, animation, secondaryAnimation, child) {
            const begin = Offset(1.0, 0.0);
            const end = Offset.zero;
            const curve = Curves.easeInOut;
            var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
            var offsetAnimation = animation.drive(tween);
            return SlideTransition(
              position: offsetAnimation,
              child: child,
            );
          },
          transitionDuration: const Duration(milliseconds: 300),
        );
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search