skip to Main Content

When i swipe back, if nothing is changed it should just go back. if i have changed some personal data (for example, first name or last name) and I swiped back, it should stay in PersonalDataScreen and show ShowDialog.showDialogOnScreen()

  @override
  Widget build(BuildContext context) {
    return PopScope(
      canPop: false, // I tried use here canPopMethod() below, but this isn't working
      child: Scaffold(
        appBar: CustomAppBar2(
          title: 'Personal data',
          onTapBack: () {
            isProfileLoadedSuccesfully && hasChanges(profileModel)
                ? ShowDialog.showDialogOnScreen(
                    context: context,
                    title: 'Has changes, do you want to save?',
                    onTapNo: () {
                      Navigator.pop(context);
                      Navigator.pop(context);
                    },
                    onTapYes: () {
                      Navigator.pop(context); //close showDialog
                      Navigator.pop(context); //go back
                      changeProfile();
                    })
                : Navigator.pop(context);
          },
        ),
        body: BlocBuilder<ProfileBloc, ProfileState>(
          builder: (context, state) {
            isProfileLoadedSuccesfully = false;
            return switch (state) {
              ProfileInitial() => Center(child: CircularProgressIndicator()),
              ProfileLoaded() => _buildBody(state, context),
            };
          },
        ),
      ),
    );
  }

  Widget _buildBody(ProfileLoaded state, BuildContext context) {
    isProfileLoadedSuccesfully = true;
    return someWidget();
}
  bool canPopMethod() {
    if (isProfileLoadedSuccesfully == false) {
      return true;
    }
    if (hasChanges(profileModel)) {
      ShowDialog.showDialogOnScreen(
        context: context,
        title: 'Has changes, do you want to save?',
        onTapNo: () {
          Navigator.pop(context); //close showDialog
          Navigator.pop(context); // go back
        },
        onTapYes: () {
          Navigator.pop(context); //close showDialog
          Navigator.pop(context); //go back
          changeProfile();
        },
      );
      return false;
    }
    return true;
  }

  bool hasChanges(ProfileModel? profileModel) {
    return firstNameController.text != profileModel?.firstName ||
        lastNameController.text != profileModel?.lastName ||
        phoneController.text != profileModel?.phone ||
        emailController.text != profileModel?.email ||
        innPassportController.text != profileModel?.innPassport;
  }

I tried use method in canPop: canPopMethod() but it doesn’t working. I think canPopMethod() works only one time when i opened PersonalDataScreen, and not working every time when i swipe back.

2

Answers


  1. Chosen as BEST ANSWER

    I did some research. I don't know if this is right way, but it worked for me. If you have another variants. I will appreciate it.

    @override
      Widget build(BuildContext context) {
        return PopScope(
          canPop: false,
          onPopInvoked: (didPop) async {
            if (didPop) return;
            // This is for Android(back button)
            isProfileLoadedSuccesfully && hasChanges(profileModel)
                ? _showDialog()
                : Navigator.pop(context);
          },
          child: Platform.isIOS
              ? GestureDetector(
                  // This is for IOS(swipe back)
                  onHorizontalDragUpdate: (details) {
                    // Note: Sensitivity is integer used when you don't want to mess up vertical drag
                    int sensitivity = 8;
                    if (details.delta.dx > sensitivity) {
                      isProfileLoadedSuccesfully && hasChanges(profileModel)
                          ? _showDialog()
                          : Navigator.pop(context);
                    }
                  },
                  child: _buildScaffold(context),
                )
              : _buildScaffold(context),
        );
      }
    

  2. There has been some inconsistent behavior from PopScope and navigation. Table here explains it better. Note that ‘Expected’ column states if behavior is expected or not rather than expected value.

    For best practice it’s good to use onPopInvoked for most consistent behavior of your app while this problem is not fixed. Couple things to keep in mind is downgrading your navigation, for example GoRouter 12.0.0 with code provided below works fine and stops all popping from the page. Easy work around would be using GestureDetector to see which way you are swiping and adding in the onPopInvoked your logic how you want to handle that. This code here is only to showcase how it works

    Edit: Flutter version 3.22.1 is used here

    Table with CanPop bahavior

    import 'package:flutter/material.dart';
    import 'package:go_router/go_router.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    final GoRouter _router = GoRouter(
      initialLocation: '/',
      routes: [
        GoRoute(
          name: 'home', // Optional, add name to your routes. Allows you navigate by name instead of path
          path: '/',
          builder: (context, state) => const HomeScreen(),
        ),
        GoRoute(
          name: 'second', // Optional, add name to your routes. Allows you navigate by name instead of path
          path: '/second',
          builder: (context, state) => const SecondScreen(),
        ),
      ],
    );
    
    class MyApp extends StatelessWidget {
      /// Constructs a [MyApp]
      const MyApp({super.key});
      @override
      Widget build(BuildContext context) {
        return MaterialApp.router(
          routerConfig: _router,
        );
      }
    }
    
    class HomeScreen extends StatelessWidget {
      const HomeScreen({super.key});
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: const Text('Home Screen')),
          body: Center(
            child: ElevatedButton(
              onPressed: () => context.go('/second'),
              child: const Text('Go to the second screen'),
            ),
          ),
        );
      }
    }
    
    class SecondScreen extends StatelessWidget {
      const SecondScreen({super.key});
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: const Text('Second Screen')),
          body: PopScope(
            canPop: false,
            onPopInvoked: (didPop) {
              if (didPop) {
                print('Did pop');
              } else {
                print('Did not get to pop');
              }
            },
            child: Center(
              child: ElevatedButton(
                onPressed: () => context.go('/'),
                child: const Text('Back to the Home screen'),
              ),
            ),
          ),
        );
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search