jonataslaw / getx

Open screens/snackbars/dialogs/bottomSheets without context, manage states and inject dependencies easily with Get.
MIT License
10.44k stars 1.63k forks source link

BUG: [GETX] GOING TO ROUTE interacts with a dialog and making invalid route. And the Get.previousRoute cann't back to empty value. #2502

Open SittiphanSittisak opened 2 years ago

SittiphanSittisak commented 2 years ago

It happened when using the Get.toNamed(). It works fine with Navigator.push() but I need Get.toNamed for the web app.

The first page has a button that will show the first dialog. The first dialog will show the order type button list. When pressing an order type button, the program will find a new order of this type and open the second page with a new order data. The second page has some work to do and this work will open the second dialog. After finishing this work, the user will click on the back button back to the first page and find a new order again.

The problem is when the second dialog works on the second page. The first dialog on the first page will not work.

see video example: https://user-images.githubusercontent.com/59549741/184571169-01a030db-59a1-4618-a8c8-003a14e88b10.mp4

web example: web exam github_2502

code example:

import 'package:flutter/material.dart';
import 'package:flutter_test_exam_bug/config/path/page_path.dart';
import 'package:get/get.dart';

Future<void> _showMyDialog({required BuildContext context, required Widget child}) async {
  return showDialog<void>(
    context: context,
    builder: (BuildContext context) => child,
  );
}

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

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

class _PageTestState extends State<PageTest> {
  @override
  Widget build(BuildContext context) {
    Widget dialog_ = Center(
            child: ElevatedButton(onPressed: () => Get.toNamed(PagePath.test2), child: const Text("Open second page"))),
        openDialogButton_ = ElevatedButton(
            onPressed: () => _showMyDialog(context: context, child: dialog_), child: const Text("Open first dialog"));

    return Scaffold(body: SafeArea(child: Center(child: openDialogButton_)));
  }
}

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

  @override
  State<PageTest2> createState() => _PageTest2State();
}

class _PageTest2State extends State<PageTest2> {
  ButtonStyle buttonStyle = ElevatedButton.styleFrom(primary: Colors.green);

  @override
  Widget build(BuildContext context) {
    Widget dialog_ = Center(
            child: ElevatedButton(
                onPressed: () => Navigator.pop(context), child: const Text("I am second dialog"), style: buttonStyle)),
        openDialogButton_ = ElevatedButton(
            onPressed: () => _showMyDialog(context: context, child: dialog_),
            child: const Text("Open second dialog"),
            style: buttonStyle);

    return Scaffold(appBar: AppBar(), body: SafeArea(child: Center(child: openDialogButton_)));
  }
}

flutter doctor:

Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 3.0.5, on Microsoft Windows [Version 10.0.19044.1889], locale th-TH)
[√] Android toolchain - develop for Android devices (Android SDK version 32.0.0)
[√] Chrome - develop for the web
[√] Visual Studio - develop for Windows (Visual Studio Community 2022 17.1.5)
[√] Android Studio (version 2021.2)
[√] VS Code (version 1.70.1)
[√] Connected device (3 available)
[√] HTTP Host Availability

• No issues found!
SittiphanSittisak commented 2 years ago

I found the case of this issue. The issue is when opening a dialog, the rouse name will think that is a new route. Assume the previous route is A and the current route is B and the dialog route is C. After opening the dialog, the current route will change from B to C and the previous route will change from A to B. This is a basic step but the c route is a dialog and not defined in GetMaterialApp. So, the C route is equivalent to the B route. At the moment, the previous route is B => Get.previousRoute = B the current route is C => Get.currentRoute= C but C = B then Get.currentRoute= B Did you see that? Even though we used Ger.back the previous route and the current route still B. Then we can go to route B again because the current route is already B! For using ModalRoute.of(context)!.settings.name, this is working successfully but this can't use with Get.context. This is a bug! Please, fix it.

You can see this issue in a new video: https://user-images.githubusercontent.com/59549741/188280286-aa3f0cfe-d63a-4bce-bf9d-433a56e73275.mp4

web example: web exam github_2502

code: main.dart

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_test_exam_bug/page/page_github2502.dart';
import 'package:get/get.dart';

void main() {
  runApp(const MyApp());
}

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      scrollBehavior: MyCustomScrollBehavior(),
      initialRoute: "/github_2502",
      getPages: [
        GetPage(name: "/github_2502", page: () => const PageGithub2502()),
        GetPage(name: "/github_2502_Page_2", page: () => const PageGithub2502Page2()),
      ],
    );
  }
}

class MyCustomScrollBehavior extends MaterialScrollBehavior {
  @override
  Set<PointerDeviceKind> get dragDevices => {
        PointerDeviceKind.touch,
        PointerDeviceKind.mouse,
      };
}

page_github2502.dart

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

Future<void> _showMyDialog({required BuildContext context, required Widget child}) async {
  return showDialog<void>(
    context: context,
    builder: (BuildContext context) => child,
  );
}

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

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

class _PageGithub2502State extends State<PageGithub2502> {
  @override
  Widget build(BuildContext context) {
    Widget dialog_ = Center(
            child: ElevatedButton(
                onPressed: () async {
                  print("\n--- Before open the second page ---");
                  print("previous route :${Get.previousRoute}");
                  print("current route :${Get.currentRoute}");
                  print("ModalRoute.of(context)!.settings.name: ${ModalRoute.of(context)!.settings.name}");
                  await Get.toNamed("/github_2502_Page_2");
                  print("\n--- After open the second page ---");
                  print("previous route :${Get.previousRoute}");
                  print("current route :${Get.currentRoute}");
                  print("ModalRoute.of(context)!.settings.name: ${ModalRoute.of(context)!.settings.name}");
                },
                child: const Text("Open second page"))),
        openDialogButton_ = ElevatedButton(
            onPressed: () async {
              print("\n+++ Before open a first dialog +++");
              print("previous route :${Get.previousRoute}");
              print("current route :${Get.currentRoute}");
              print("ModalRoute.of(context)!.settings.name: ${ModalRoute.of(context)!.settings.name}");
              await _showMyDialog(context: context, child: dialog_);
              print("\n+++ Before open a first dialog +++");
              print("previous route :${Get.previousRoute}");
              print("current route :${Get.currentRoute}");
              print("ModalRoute.of(context)!.settings.name: ${ModalRoute.of(context)!.settings.name}");
            },
            child: const Text("Open first dialog"));

    return Scaffold(backgroundColor: Colors.brown, body: SafeArea(child: Center(child: openDialogButton_)));
  }
}

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

  @override
  State<PageGithub2502Page2> createState() => _PageGithub2502Page2State();
}

class _PageGithub2502Page2State extends State<PageGithub2502Page2> {
  ButtonStyle buttonStyle = ElevatedButton.styleFrom(primary: Colors.brown);

  @override
  Widget build(BuildContext context) {
    Widget dialog_ = Center(
            child: ElevatedButton(
                onPressed: () => Navigator.pop(context), child: const Text("I am second dialog"), style: buttonStyle)),
        openDialogButton_ = ElevatedButton(
            onPressed: () async {
              print("\n*** Before open a second dialog ***");
              print("previous route :${Get.previousRoute}");
              print("current route :${Get.currentRoute}");
              print("ModalRoute.of(context)!.settings.name: ${ModalRoute.of(context)!.settings.name}");
              await _showMyDialog(context: context, child: dialog_);
              print("\n*** After open a second dialog ***");
              print("previous route :${Get.previousRoute}");
              print("current route :${Get.currentRoute}");
              print("ModalRoute.of(context)!.settings.name: ${ModalRoute.of(context)!.settings.name}");
            },
            child: const Text("Open second dialog"),
            style: buttonStyle);

    return Scaffold(
      appBar: AppBar(backgroundColor: Colors.blueAccent),
      backgroundColor: Colors.blueAccent,
      body: SafeArea(child: Center(child: openDialogButton_)),
    );
  }
}
SittiphanSittisak commented 2 years ago

I found more problems. In the situation that Get.previousRoute is empty.

In A page Get.previousRoute = ''; Get.currentRoute= 'A';

Open B page Get.previousRoute = 'A'; Get.currentRoute= 'B';

Using Get.back() back to A page Get.previousRoute = 'A'; Get.currentRoute= 'A';

The problem is the Get.previousRoute doesn't back to empty. You can see this problem in this video: https://user-images.githubusercontent.com/59549741/188297758-ac9c425f-540c-45d3-baa2-966a700808b8.mp4

So, this issue contains 2 bugs with

  1. The Get.previousRoute and Get.currentRoute have interaction with a dialog.
  2. The Get.previousRoute can't go back to empty.
wilhantian commented 2 years ago

I'm also using version 4.6.5 and have the same problem. It seems that this problem has always existed and has not been solved yet

heshesh2010 commented 2 years ago

+1

nullhandler commented 1 year ago

+1

jonataslaw commented 1 year ago

The problem occurs when you use Get.showDialog instead of showDialog?

heshesh2010 commented 1 year ago

The problem occurs when you use Get.showDialog instead of showDialog?

yes

nullhandler commented 1 year ago

For me, it doesnt matter whether I use Get.showDialog or showDialog.

Root -> Bottomsheet -> Page1 -> Dialog

Pop 2 times.

Root -> Bottomsheet

Now, cannot go to Page1 since currentRoute is set to Page1

@jonataslaw what is the tag for version 4.6.5, I checked out to both the branch 4.6.5 and tag 4.6.5, it contains some other changes which are not there in pub variant.

ngthailam commented 1 year ago

I have the same problem, had to use preventDuplicates: false to overcome this

AsifAnsari96 commented 2 weeks ago

when i go back from second routes i use Get.back(closeOverlays: true); solve my issue

as mentioned in method comment /// Navigation.popUntil() shortcut.

/// /// Pop the current page, snackbar, dialog or bottomsheet in the stack /// /// if your set [closeOverlays] to true, Get.back() will close the /// currently open snackbar/dialog/bottomsheet AND the current page /// /// [id] is for when you are using nested navigation, /// as explained in documentation /// /// It has the advantage of not needing context, so you can call /// from your business logic.