Flutter GoRouter context.push() 意外行为

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

在过去的几天里,我正在做一个项目,并被 gorouter 的行为所困扰。下面我就来解释一下它是什么-

class ABCScreen extends StatelessWidget {
  final String id;
  const ABCScreen({super.key, required this.id});

  @override
  Widget build(BuildContext context) {
    return Consumer<ABCState>(builder: (context, future, child) {
      return FutureBuilder(
        future: future.call(id: id),
        builder: ((context, snapshot) {
          if (snapshot.data != null &&
              snapshot.connectionState == ConnectionState.done) {
            return XYZScreen(model: snapshot.data!);
          } else {
            return const ShimmerScreen();
          }
        }),
      );
    });
  }

}

因此 ABCScreen 基本上负责使用提供程序从 REST API 端点获取数据,并将数据传递到 XYZ Screen 进行构建。它还使用 FutureBuilder。这就是国家的样子 -

class ABCState extends ChangeNotifierProvider {

  Future<Model?> call({required String id}) async {
    Model? data = await ABCServices().fetchData(id: id); // REST API call
    if (data != null) {
      return data;
    }
    return null;
  }

}

现在,我知道我没有将 API 结果存储到提供程序中的某个变量中,然后使用这些变量来构建屏幕,因为我正在通过未来的构建器获得所需的结果,但我不喜欢改变事情。

所以,GoRouter 的主要问题是从 XYZScreen ( ABCScreen 的子屏幕)产生的,该屏幕包含到另一个 QWEScreen 的导航路线(在 gorouter 路线配置中定义了路径,比如说 /path ),

  1. 当使用 context.push('/path') 导航到此屏幕时,它会再次构建整个父窗口小部件,即 ABCScreen(这会导致对 API 的一次额外调用)。
  2. 在使用 Navigator.push() 和 context.go() 时,一切都工作得很好,即没有对 API 的额外调用,即没有父小部件的重建/重新渲染。

另外,由于我使用 goRouter 作为我的应用程序的主要路由方法,所以我想坚持使用它并且不想使用 Navigator.push()。看来我在实现代码时遇到了一些错误。请看一下,任何帮助将不胜感激。提前谢谢你:)

class XYZScreen extends StatelessWidget {
  const XYZScreen({
    super.key,
    required this.model,
  });

  final Model model ;

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: () {
        if (context.mounted) {
          context.push('/path');
        }
      },
      child: SomeContainer(model: model),
    );
  }
}

期望只是在导航中使用 goRouter,同时确保不会发生额外的调用/重建小部件。

flutter flutter-navigation flutter-go-router
1个回答
0
投票

我已经询问了 Flutter 成员,根据 issue,如果视图小部件不是 const 类,GoRouter 会按照预期行为重建视图。

您有两种解决方案来解决这个问题:

  1. (建议)将您的小部件更改为 const 类,即使
    ABCScreen
    成为 const 类,如果您仍然需要将参数传递给小部件,请考虑使用:
    • Provider
      用于您案例中的全局状态管理。
    • 通过
      Provider
      传递参数(不太适合复杂数据)。
  2. 另一种方法是使用 GoRouter 构建条件小部件:
    • 好处:
      • 当您无法将
        ABCScreen
        设为 const 类时很有用(例如,需要动态初始化)。
    • 实施:
      • 此方法检查当前位置(屏幕)是否与 ABCScreen 路线匹配。
      • 如果它们不匹配(正在进行导航),则返回 SizedBox.shrink() 以避免重建 ABCScreen。
      • 如果它们匹配(返回 ABCScreen),则使用提供的参数构建小部件。
  GoRoute(
    path: ABCScreen.routePath,
    pageBuilder: (BuildContext context, GoRouterState state) {
      final String parameter = state.extra as String;
        return MaterialPage<void>(
          child: Builder(builder: (BuildContext context) {
            // FIXME:https://stackoverflow.com/a/56666801/5643911
            // FIXME:https://github.com/flutter/flutter/issues/152906
            if (state.matchedLocation != GoRouter.of(context).location) {
              return const SizedBox.shrink();
            }
          return ABCScreen(id: parameter);
        }),
      );
    },
  ),

希望答案为您的 Flutter 项目提供清晰的解决方案!

© www.soinside.com 2019 - 2024. All rights reserved.