Transitons 不适用于 go_router 包中的 StatefulShellRoute

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

这是演示如何使用 BottomNavigationBar 设置嵌套导航的官方示例,其中每个栏项都使用自己的持久导航器,即为每个项单独维护导航状态。此设置还可以深度链接到嵌套页面。

https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/stateful_shell_route.dart

现在我想在底部导航栏的页面之间添加CustomTransitionPage,所以我使用了pageBuilder而不是builder,但是没有成功。

这是添加 CustomTransitionPage 后的完整代码


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

final _rootNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'root');
final _sectionANavigatorKey =
    GlobalKey<NavigatorState>(debugLabel: 'sectionANav');

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

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

  final GoRouter _router = GoRouter(
    navigatorKey: _rootNavigatorKey,
    initialLocation: '/a',
    routes: <RouteBase>[
      StatefulShellRoute.indexedStack(
        pageBuilder: (context, state, navigationShell) {
          return CustomTransitionPage(
            key: state.pageKey,
            child: ScaffoldWithNavBar(navigationShell: navigationShell),
            transitionsBuilder:
                (context, animation, secondaryAnimation, child) {
              return FadeTransition(
                opacity: animation,
                child: child,
              );
            },
          );
        },
        branches: <StatefulShellBranch>[
          StatefulShellBranch(
            navigatorKey: _sectionANavigatorKey,
            routes: <RouteBase>[
              GoRoute(
                path: '/a',
                pageBuilder: (context, state) => CustomTransitionPage(
                  key: state.pageKey,
                  child: const RootScreen(label: 'A'),
                  transitionsBuilder:
                      (context, animation, secondaryAnimation, child) {
                    return FadeTransition(
                      opacity: animation,
                      child: child,
                    );
                  },
                ),
              ),
            ],
          ),
          StatefulShellBranch(
            routes: <RouteBase>[
              GoRoute(
                path: '/b',
                pageBuilder: (context, state) => CustomTransitionPage(
                  key: state.pageKey,
                  child: const RootScreen(label: 'B'),
                  transitionsBuilder:
                      (context, animation, secondaryAnimation, child) {
                    return FadeTransition(
                      opacity: animation,
                      child: child,
                    );
                  },
                ),
              ),
            ],
          ),
          StatefulShellBranch(
            routes: <RouteBase>[
              GoRoute(
                path: '/c',
                pageBuilder: (context, state) => CustomTransitionPage(
                  key: state.pageKey,
                  child: const RootScreen(label: 'C'),
                  transitionsBuilder:
                      (context, animation, secondaryAnimation, child) {
                    return FadeTransition(
                      opacity: animation,
                      child: child,
                    );
                  },
                ),
              ),
            ],
          ),
        ],
      ),
    ],
  );

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      routerConfig: _router,
    );
  }
}

/// Builds the "shell" for the app by building a Scaffold with a
/// BottomNavigationBar, where [child] is placed in the body of the Scaffold.
class ScaffoldWithNavBar extends StatelessWidget {
  const ScaffoldWithNavBar({
    required this.navigationShell,
    Key? key,
  }) : super(key: key ?? const ValueKey<String>('ScaffoldWithNavBar'));

  /// The navigation shell and container for the branch Navigators.
  final StatefulNavigationShell navigationShell;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: navigationShell,
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Section A'),
          BottomNavigationBarItem(icon: Icon(Icons.work), label: 'Section B'),
          BottomNavigationBarItem(icon: Icon(Icons.tab), label: 'Section C'),
        ],
        currentIndex: navigationShell.currentIndex,
        onTap: (int index) => _onTap(context, index),
      ),
    );
  }

  void _onTap(BuildContext context, int index) {
    navigationShell.goBranch(
      index,
      initialLocation: index == navigationShell.currentIndex,
    );
  }
}

/// Widget for the root/initial pages in the bottom navigation bar.
class RootScreen extends StatelessWidget {
  const RootScreen({
    required this.label,
    super.key,
  });

  final String label;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Root of section $label'),
      ),
      body: Center(
        child: Text('Screen $label'),
      ),
    );
  }
}

flutter flutter-animation flutter-go-router flutter-routes gorouter
1个回答
0
投票

可以对过渡进行动画处理,但您必须自己制作动画/过渡,并且不会使用 CustomTransitionsPage。

您要做的是将一个函数传递给

StatefulShellRoute.navigatorContainerBuilder
,其中有
context
StatefulNavigationShell
List<Children>
(它们是分支导航器)作为参数。

您想用它来构建

ScaffoldWithNavBar
,并传入
StatefulNavigationShell
List<Children.>
。然后你想在构建函数中做这样的事情:

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: BranchAnimator(
      currentIndex: navigationShell.currentIndex,
      children: children,
    ),
    ...

BranchAnimator 可以像您想要的那样简单或复杂。最基本的(无动画)是一个简单的堆栈:

class BranchAnimator extends StatelessWidget {
  const BranchAnimator({super.key, required this.currentIndex, required this.children});

  final int currentIndex;
  final List<Widget> children;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: children.mapIndexed(
        (int i, Widget child) {
          return Opacity(  /// <<---- here
            opacity: i == currentIndex ? 1 : 0,
            child: IgnorePointer(
              ignoring: i != currentIndex,
              child: TickerMode(
                enabled: i == currentIndex,
                child: child,
              ),
            ),
          );
        }
      ),
    );
  }
}

这基本上复制了默认行为 - 如果你想实际执行动画,那么你需要修改

<<---- here
在上面代码中的位置 - 或者通过将该不透明度设置为 AnimatedOpacity 或更复杂的东西(如果你想要更多)自定义动画。您可以采用多种不同的方法来实现此目的,具体取决于您想要的动画类型以及您希望它的复杂程度。您的第一步可能是将
BranchAnimator
制作为有状态小部件,保留最后一个索引,以便您可以知道要绘制什么类型的动画,并且可能在 TickerProviderStateMixin 中混合,然后执行动画应该与您的方式非常相似会实现 CustomTransitionPage。

在您链接到的同一存储库中有一个示例,它对不透明度和大小进行基本动画:https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/others /custom_stateful_shell_route.dart.

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