Flutter在按下Android返回按钮时,子Beamer的行为很奇怪。

huangapple go评论81阅读模式
英文:

Flutter Strange behavior when pressing the Android back button on child Beamer

问题

在我的应用程序中,一开始我有3个具有以下路由的页面:

routerDelegate = BeamerDelegate(
  initialPath: initialPath,
  locationBuilder: RoutesLocationBuilder(
    routes: {
      '/signup': (_, __, ___) => const SignupScreenWidget(),
      '/verifyCode': (_, __, ___) => const VerifyCodeRootScreenWidget(),
      '/home': (_, __, ___) => const HomeScreen(),
    },
  ),
);

在主页内部,我定义了以下的 BeamLocations。由于在 ChefRootWidget 内部有另一个具有自己特定链接的 BeamLocation,因此当我进入以下路径时:

/home/chefMainWidget/chefStoreHomePage/chefStoreMenu

chefStoreMenuchefStoreHomePage 的返回按钮上点击返回到前一页是没有问题的。但是,在手机上点击返回按钮后,再次返回,即不是返回到 chefStoreHomePage,而是直接返回到主页,这是错误的,应该显示页面。因此,主页和 chefStoreHomePage 页面都有自己特定的 BeamLocations

主页的 BeamLocation 是:

class HomeScreenTab extends BeamLocation<BeamState> {
  HomeScreenTab(super.routeInformation);

  @override
  List<String> get pathPatterns => [
        '/home/chefMainWidget/:storeId',
      ];

  @override
  List<BeamPage> buildPages(BuildContext context, BeamState state) {
    List<BeamPage> pages = [];
    pages.add(
      const BeamPage(
        key: ValueKey('/home'),
        type: BeamPageType.noTransition,
        child: HomeRootScreenWidget(),
      ),
    );

    if (state.uri.pathSegments.length > 1) {
      String key = '';
      Widget? screen;
      switch (state.uri.pathSegments[1]) {
        case 'chefMainWidget':
          final storeId = state.pathParameters['storeId'];
          key = '/home/chefMainWidget-$storeId-${DateTime.now()}';
          screen = storeId == null ? null : ChefRootWidget(storeId: int.tryParse(storeId));
          break;
      }
      if (screen != null) {
        pages.add(BeamPage(
          key: ValueKey(key),
          type: BeamPageType.slideRightTransition,
          child: screen,
        ));
      }
    }

    return pages;
  }
}

ChefRootWidget 类:

class ChefRootWidget extends StatefulWidget {
  final int? storeId;

  const ChefRootWidget({super.key, required this.storeId});

  @override
  State<StatefulWidget> createState() => _ChefRootWidget();
}

class _ChefRootWidget extends State<ChefRootWidget> {
  int get storeId => widget.storeId!;
  late List<BeamerDelegate> _routerDelegates;

  @override
  void initState() {
    _routerDelegates = [
      BeamerDelegate(
        initialPath: '/home/chefMainWidget/chefStoreHomePage',
        locationBuilder: (routeInformation, _) {
          if (routeInformation.uri.toString().contains('/home/chefMainWidget/chefStoreHomePage')) {
            return ChefStoreBeamer(routeInformation, storeId);
          }
          return NotFound(path: routeInformation.uri.toString());
        },
      ),
    ];
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Beamer(
        routerDelegate: _routerDelegates[0],
      ),
    );
  }
}

chefStoreHomePage 页面的 BeamLocation 是:

class ChefStoreBeamer extends BeamLocation<BeamState> {
  final int storeId;

  ChefStoreBeamer(super.routeInformation, this.storeId);

  @override
  List<String> get pathPatterns => [
        '/home/chefMainWidget/chefStoreHomePage/chefStoreMenu/:menuId',
      ];

  @override
  List<BeamPage> buildPages(BuildContext context, BeamState state) {
    List<BeamPage> pages = [];
    pages.add(
      BeamPage(
        key: ValueKey('/home/chefMainWidget/chefStoreHomePage-$storeId-${DateTime.now()}'),
        type: BeamPageType.slideRightTransition,
        child: ChefStoreHomePageWidget(storeId: storeId),
      ),
    );
    if (state.uri.pathSegments.contains('chefStoreMenu')) {
      final menuId = state.pathParameters['menuId'];
      if (menuId != null) {
        pages.add(
          BeamPage(
            key: ValueKey('/home/chefMainWidget/chefStoreHomePage/chefStoreMenu-$menuId-${DateTime.now()}'),
            type: BeamPageType.slideRightTransition,
            child: ChefMenuWidget(menuId: int.tryParse(menuId)),
          ),
        );
      }
    }
    return pages;
  }
}

我使用 Beamer.of(context).beamToNamed 在页面之间进行导航,例如:

Beamer.of(context).beamToNamed('/home/chefMainWidget/chefStoreHomePage/chefStoreMenu/$menuId');

此外,这是一个示例代码。

英文:

In my application, at the beginning, I have 3 pages with the following routes:

routerDelegate = BeamerDelegate(
  initialPath: initialPath,
  locationBuilder: RoutesLocationBuilder(
    routes: {
      &#39;/signup&#39;: (_, __, ___) =&gt; const SignupScreenWidget(),
      &#39;/verifyCode&#39;: (_, __, ___) =&gt; const VerifyCodeRootScreenWidget(),
      &#39;/home&#39;: (_, __, ___) =&gt; const HomeScreen(),
    },
  ),
);

Inside the Home page, I defined the following BeamLocations, and since I have another BeamLocation inside the ChefRootWidget that has its own specific links, when I go to the following path:

/home/chefMainWidget/chefStoreHomePage/chefStoreMenu

There is no problem returning to the previous page by clicking on the back icon from chefStoreMenu to chefStoreHomePage. However, after touching the back button on the phone, the return occurs again, i.e., instead of returning to chefStoreHomePage, I am directly taken to the home page, which is wrong, and the page should be displayed. Therefore, both the home and chefStoreHomePage pages have their own specific BeamLocations.

The BeamLocation of the Home page is:

class HomeScreenTab extends BeamLocation&lt;BeamState&gt; {
  HomeScreenTab(super.routeInformation);

  @override
  List&lt;String&gt; get pathPatterns =&gt; [
        &#39;/home/chefMainWidget/:storeId&#39;,
      ];

  @override
  List&lt;BeamPage&gt; buildPages(BuildContext context, BeamState state) {
    List&lt;BeamPage&gt; pages = [];
    pages.add(
      const BeamPage(
        key: ValueKey(&#39;/home&#39;),
        type: BeamPageType.noTransition,
        child: HomeRootScreenWidget(),
      ),
    );

    if (state.uri.pathSegments.length &gt; 1) {
      String key = &#39;&#39;;
      Widget? screen;
      switch (state.uri.pathSegments[1]) {
        case &#39;chefMainWidget&#39;:
          final storeId = state.pathParameters[&#39;storeId&#39;];
          key = &#39;/home/chefMainWidget-$storeId-${DateTime.now()}&#39;;
          screen = storeId == null ? null : ChefRootWidget(storeId: int.tryParse(storeId));
          break;
      }
      if (screen != null) {
        pages.add(BeamPage(
          key: ValueKey(key),
          type: BeamPageType.slideRightTransition,
          child: screen,
        ));
      }
    }

    return pages;
  }
}

and ChefRootWidget class:

class ChefRootWidget extends StatefulWidget {
  final int? storeId;

  const ChefRootWidget({super.key, required this.storeId});

  @override
  State&lt;StatefulWidget&gt; createState() =&gt; _ChefRootWidget();
}

class _ChefRootWidget extends State&lt;ChefRootWidget&gt; {
  int get storeId =&gt; widget.storeId!;
  late List&lt;BeamerDelegate&gt; _routerDelegates;


  @override
  void initState() {
    _routerDelegates = [
      BeamerDelegate(
        initialPath: &#39;/home/chefMainWidget/chefStoreHomePage&#39;,
        locationBuilder: (routeInformation, _) {
          if (routeInformation.uri.toString().contains(&#39;/home/chefMainWidget/chefStoreHomePage&#39;)) {
            return ChefStoreBeamer(routeInformation, storeId);
          }
          return NotFound(path: routeInformation.uri.toString());
        },
      ),
    ];
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Beamer(
        routerDelegate: _routerDelegates[0],
      ),
    );
  }
}

The BeamLocation of the chefStoreHomePage page is:

class ChefStoreBeamer extends BeamLocation&lt;BeamState&gt; {
  final int storeId;

  ChefStoreBeamer(super.routeInformation, this.storeId);

  @override
  List&lt;String&gt; get pathPatterns =&gt; [
        &#39;/home/chefMainWidget/chefStoreHomePage/chefStoreMenu/:menuId&#39;,
      ];

  @override
  List&lt;BeamPage&gt; buildPages(BuildContext context, BeamState state) {
    List&lt;BeamPage&gt; pages = [];
    pages.add(
      BeamPage(
        key: ValueKey(&#39;/home/chefMainWidget/chefStoreHomePage-$storeId-${DateTime.now()}&#39;),
        type: BeamPageType.slideRightTransition,
        child: ChefStoreHomePageWidget(storeId: storeId),
      ),
    );
    if (state.uri.pathSegments.contains(&#39;chefStoreMenu&#39;)) {
      final menuId = state.pathParameters[&#39;menuId&#39;];
      if (menuId != null) {
        pages.add(
          BeamPage(
            key: ValueKey(&#39;/home/chefMainWidget/chefStoreHomePage/chefStoreMenu-$menuId-${DateTime.now()}&#39;),
            type: BeamPageType.slideRightTransition,
            child: ChefMenuWidget(menuId: int.tryParse(menuId)),
          ),
        );
      }
    }
    return pages;
  }
}

What I do is use Beamer.of(context).beamToNamed to move between pages, for example:

Beamer.of(context).beamToNamed(&#39;/home/chefMainWidget/chefStoreHomePage/chefStoreMenu/$menuId&#39;);

and then its full sample code:

import &#39;package:beamer/beamer.dart&#39;;
import &#39;package:flutter/material.dart&#39;;

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

class ALocation extends BeamLocation&lt;BeamState&gt; {
  ALocation(super.routeInformation);

  @override
  List&lt;String&gt; get pathPatterns =&gt; [&#39;/a/child-root&#39;];

  @override
  List&lt;BeamPage&gt; buildPages(BuildContext context, BeamState state) {
    List&lt;BeamPage&gt; pages = [];
    pages.add(
      const BeamPage(
        key: ValueKey(&#39;/a&#39;),
        type: BeamPageType.noTransition,
        child: RootScreen(),
      ),
    );

    if (state.pathPatternSegments.contains(&#39;child-root&#39;)) {
      pages.add(
        const BeamPage(
          key: ValueKey(&#39;/a/child-root&#39;),
          type: BeamPageType.noTransition,
          child: ChildScreen(),
        ),
      );
    }

    return pages;
  }
}

class RootBeamerScreen extends StatefulWidget {
  const RootBeamerScreen({super.key});

  @override
  State&lt;RootBeamerScreen&gt; createState() =&gt; _RootBeamerScreenState();
}

class _RootBeamerScreenState extends State&lt;RootBeamerScreen&gt; {
  final _routerDelegates = [
    BeamerDelegate(
      initialPath: &#39;/a&#39;,
      locationBuilder: (routeInformation, _) {
        if (routeInformation.uri.toString().contains(&#39;/a&#39;)) {
          return ALocation(routeInformation);
        }
        return NotFound(path: routeInformation.uri.toString());
      },
    ),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Beamer(
        routerDelegate: _routerDelegates[0],
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(&#39;root&#39;),
      ),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: &lt;Widget&gt;[
            const Padding(padding: EdgeInsets.all(4)),
            TextButton(
              onPressed: () =&gt; Beamer.of(context).beamToNamed(&#39;/a/child-root&#39;),
              child: const Text(&#39;View child-root&#39;),
            ),
          ],
        ),
      ),
    );
  }
}

class ChildLocation extends BeamLocation&lt;BeamState&gt; {
  ChildLocation(super.routeInformation);

  @override
  List&lt;String&gt; get pathPatterns =&gt; [&#39;/child-root/detail&#39;];

  @override
  List&lt;BeamPage&gt; buildPages(BuildContext context, BeamState state) {
    List&lt;BeamPage&gt; pages = [];
    pages.add(
      const BeamPage(
        key: ValueKey(&#39;/child-root&#39;),
        type: BeamPageType.noTransition,
        child: ChildDetailWidget(),
      ),
    );

    if (state.pathPatternSegments.contains(&#39;detail&#39;)) {
      pages.add(
        const BeamPage(
          key: ValueKey(&#39;/child-root/detail&#39;),
          type: BeamPageType.noTransition,
          child: DetailScreen(),
        ),
      );
    }

    return pages;
  }
}

class DetailScreen extends StatelessWidget {
  const DetailScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text(&#39;child screen&#39;)),
      body: const SizedBox(
        width: double.infinity,
        height: double.infinity,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Text(&#39;Yooohooooooooo&#39;),
          ],
        ),
      ),
    );
  }
}

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

  @override
  State&lt;StatefulWidget&gt; createState() =&gt; _ChildScreen();
}

class _ChildScreen extends State&lt;ChildScreen&gt; {
  final _routerDelegates = [
    BeamerDelegate(
      initialPath: &#39;/child-root&#39;,
      locationBuilder: (routeInformation, _) {
        if (routeInformation.uri.toString().contains(&#39;/child-root&#39;)) {
          return ChildLocation(routeInformation);
        }
        return NotFound(path: routeInformation.uri.toString());
      },
    ),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Beamer(
        routerDelegate: _routerDelegates[0],
      ),
    );
  }
}

class ChildDetailWidget extends StatelessWidget {
  const ChildDetailWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text(&#39;child screen&#39;)),
      body: SizedBox(
        width: double.infinity,
        height: double.infinity,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            TextButton(
              onPressed: () =&gt;
                  Beamer.of(context).beamToNamed(&#39;/child-root/detail&#39;),
              child: const Text(&#39;Click here&#39;),
            ),
          ],
        ),
      ),
    );
  }
}

class MyApp extends StatelessWidget {
  MyApp({super.key});

  final routerDelegate = BeamerDelegate(
    initialPath: &#39;/a&#39;,
    locationBuilder: RoutesLocationBuilder(
      routes: {
        &#39;*&#39;: (context, state, data) =&gt; const RootBeamerScreen(),
      },
    ),
  );

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(primarySwatch: Colors.indigo),
      routerDelegate: routerDelegate,
      routeInformationParser: BeamerParser(),
      backButtonDispatcher: BeamerBackButtonDispatcher(
        delegate: routerDelegate,
      ),
    );
  }
}

答案1

得分: 2

只需在您的ChildDetailWidget小部件内的此行代码中使用参数beamBackOnPop: trueBeamer.of(context).beamToNamed('/child-root/detail', beamBackOnPop: true)。发生的情况是,当您单击应用程序栏的返回按钮时,它会从Android的堆栈中弹出,但由于beamBackOnPop的默认值为false,beamer不会返回,而且仍然在其堆栈中有DetailScreen(但可见视图是ChildDetailWidget)。之后,当您单击Android的返回按钮时,仍然有DetailScreen的beamer会返回到ChildDetailWidget(之前已经可见)。

示例文档中提到了这一点。在那里查找Deep Location示例,他们在那里说明了以下内容:

Deep Location:您可以立即跳转到应用程序中的一个位置,其中有许多堆叠的页面(深度链接),然后逐个弹出它们,或者简单地使用beamBackOnPop参数覆盖AppBar的弹出以返回到您来自的位置。

英文:

You just need to use parameter beamBackOnPop: true in this line of code inside your ChildDetailWidget widget Beamer.of(context).beamToNamed(&#39;/child-root/detail&#39;,beamBackOnPop: true). <br>What's happening is that when you click app bar's back button then it's popped up from android's stack but because the default value of beamBackOnPop is false, beamer doesn't beam back and it still has DetailScreen in it's stack (but visible view is ChildDetailWidget) . After that when you click android's back button then beamer which still has DetailScreen, beams back to ChildDetailWidget(which was already previously visible).

They have mentioned in the examples docs. Look for Deep Location example there where they state the following<br>

> Deep Location: you can instantly beam to a location in your app that
> has many pages stacked (deep linking) and then pop them one by one or
> simply beamBack to where you came from. Note that beamBackOnPop
> parameter of beamToNamed might be useful here to override AppBar's pop
> with beamBack.

huangapple
  • 本文由 发表于 2023年7月23日 14:01:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/76746817.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定