如何在 Flutter 中正确处理嵌套 WillPopScope 小部件中的后退按钮

问题描述 投票:0回答:1

我正在开发一个带有 NavigationPage 的 Flutter 应用程序,该应用程序使用 IndexedStack 通过 BottomNavigationBar 管理不同的屏幕。每个屏幕都包含在具有自己的 GlobalKey 的 Navigator 中。我的问题是处理管理屏幕上的后退按钮按下。

以下是我目前在导航页面中管理后退按钮按下的方法:

class NavigationPage extends StatefulWidget {
  @override
  _NavigationPageState createState() => _NavigationPageState();
}

class _NavigationPageState extends State<NavigationPage> {
  int _selectedIndex = 0;
  bool isKeyboardVisible = false; // Update this based on keyboard visibility
  List<GlobalKey<NavigatorState>> _navigatorKeys = [
    GlobalKey<NavigatorState>(),
    GlobalKey<NavigatorState>(),
    // Add more keys if you have more tabs
  ];

  Future<bool> systemBackButtonPressed() async {
    if (_selectedIndex == 0) {
      return Future.value(true); // Exit the app or handle it as needed
    } else {
      var currentState = _navigatorKeys[_selectedIndex].currentState;
      if (currentState != null && currentState.canPop()) {
        currentState.pop();
        return Future.value(false);
      } else {
        setState(() {
          _selectedIndex = 0;
        });
        return Future.value(false);
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: systemBackButtonPressed,
      child: Scaffold(
        body: IndexedStack(
          index: _selectedIndex,
          children: _navigatorKeys.map((key) {
            return Navigator(
              key: key,
              onGenerateRoute: (routeSettings) {
                return MaterialPageRoute(
                  builder: (context) => getPage(_selectedIndex),
                );
              },
            );
          }).toList(),
        ),
        bottomNavigationBar: Visibility(
          visible: !isKeyboardVisible,
          child: BottomNavBar(
            selectedIndex: _selectedIndex,
            onItemTapped: (index) {
              setState(() {
                _selectedIndex = index;
              });
            },
          ),
        ),
      ),
    );
  }

  Widget getPage(int index) {
    switch (index) {
      case 0:
        return HomeScreen();
      case 1:
        return ManagementScreen();
      // Add more cases if you have more tabs
      default:
        return HomeScreen();
    }
  }
}

这是我的管理屏幕的代码:

class ManagementScreen extends StatelessWidget {
  Future<bool> _onWillPop(BuildContext context) async {
    debugPrint("ManagementScreen onWillPop triggered");
    // Add any specific logic you want here
    return true; // or false to prevent the screen from popping
  }

  @override
  Widget build(BuildContext context) {
    final height = MediaQuery.sizeOf(context).height;
    final width = MediaQuery.sizeOf(context).width;

    return WillPopScope(
      onWillPop: () => _onWillPop(context), //this not working
      child: Scaffold(
        body: SafeArea(
          child: Container(
            height: height * 1,
            decoration: const BoxDecoration(
              image: DecorationImage(
                image: AssetImage('assets/images/app_background1.png'),
                fit: BoxFit.cover,
                alignment: Alignment.center,
              ),
            ),
            child: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 20),
              child: Column(
                children: [
                  // Your content here
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

问题:

systemBackButtonPressed
为 0 时,NavigationPage 中的
 _selectedIndex
方法可以正常工作,但是当导航到 ManagementScreen 时,
 onWillPop
回调不会被触发。我希望当我按下 ManagementScreen 上的后退按钮时会调用
onWillPop 
方法,但它似乎没有按预期工作。

flutter dart navigation
1个回答
0
投票

问题在于您如何构建

WillPopScope
小部件。在您当前的设置中,ManagementScreen 中的
WillPopScope
会被绕过,因为
WillPopScope
中的
NavigationPage
会首先拦截后退按钮按下的操作。

解决方法如下:

选项 1:将后退按钮处理委托给 ManagementScreen:

从 NavigationPage 中删除 WillPopScope。 在 NavigationPage 中,在

systemBackButtonPressed
方法内部,不要直接弹出当前导航器,而是调用特定 ManagementScreen 的
_onWillPop
方法,如下所示:

Future<bool> systemBackButtonPressed() async {
  if (_selectedIndex == 0) {
    return Future.value(true); 
  } else {
    // Get the current ManagementScreen navigator
    var currentState = _navigatorKeys[_selectedIndex].currentState;
    if (currentState != null) {
      // Delegate back button handling to ManagementScreen
      return currentState.maybePop(); // Use maybePop to avoid errors if not at root
    } else {
      setState(() {
        _selectedIndex = 0;
      });
      return Future.value(false);
    }
  }
}

此方法允许 ManagementScreen 使用其

_onWillPop
方法处理自己的后退按钮逻辑。

选项 2:在 NavigationPage 中使用单个 WillPopScope:

将 WillPopScope 保留在 NavigationPage 中。 修改 NavigationPage 中的

onWillPop
回调,在弹出导航器之前首先检查当前屏幕的
_onWillPop
方法(使用
_navigatorKeys[_selectedIndex].currentState
访问)。

Future<bool> systemBackButtonPressed() async {
  if (_selectedIndex == 0) {
    return Future.value(true); // Exit the app or handle it as needed
  } else {
    var currentState = _navigatorKeys[_selectedIndex].currentState;
    if (currentState != null) {
      // Check ManagementScreen's onWillPop first
      bool canPop = await currentState.maybePop();
      if (canPop) {
        return canPop;
      } else {
        // ManagementScreen onWillPop returned false, handle fallback
        setState(() {
          _selectedIndex = 0;
        });
        return Future.value(false);
      }
    } else {
      setState(() {
        _selectedIndex = 0;
      });
      return Future.value(false);
    }
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.