Flutter:如何根据上一页更改页面过渡

6qfn3psc  于 2023-05-23  发布在  Flutter
关注(0)|答案(2)|浏览(102)

我有一个Flutter Web应用程序,有一个自定义的侧导航栏,显示在每一页上。因此,我可以始终显示相同的侧边导航栏,同时仍然保持URL导航,我使用了go_router包,所有页面都在ShellRoute中。现在我想滑动过渡到当前一个和签证诗句下面的导航项。我如何才能做到这一点?
下面是我代码的一个简化版本

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: GoRouter(
        initialLocation: "/dash",
        routes: [
          ShellRoute(
            builder: (context, state, child) => SideNavBarPage(subPage: child),
            routes: [
              GoRoute(
                path: '/dash',
                builder: (context, state) => DashPage(),
              ),
              GoRoute(
                path: '/other',
                builder: (context, state) => OtherPage(),
              ),
              GoRoute(
                path: '/another',
                builder: (context, state) => AnotherPage(),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

class SideNavBarPage extends StatelessWidget {
  final Widget subPage;

  const SideNavBarPage({
    required this.subPage,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        children: [
          CustomSideNavBar(
            items: [
              CustomSideNavBarItem(onTap: () => context.go("/dash")),
              CustomSideNavBarItem(onTap: () => context.go("/other")),
              CustomSideNavBarItem(onTap: () => context.go("/another")),
            ],
          ),
          Expanded(child: subPage),
        ],
      ),
    );
  }
}

例如:如果我想从/another切换到/dash,我想从上到下转换,而从/dash切换到/other,我想从下到上向上滑动。

yv5phkfx

yv5phkfx1#

更新(推荐):

我现在已经制定了一套解决这个问题的方案。使用这个go_router_tabs包,解决方案简单得多,只需要几行额外的代码:

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: GoRouter(
        initialLocation: "/dash",
        routes: [
          TabShellRoute(
            builder: (context, state, index, child) => SideNavBarPage(
              selectedIndex: index,
              subPage: child,
            ),
            childPageBuilder: (context, state, direction, child) {
              return TabTransitionPage(
                key: state.pageKey,
                direction: direction,
                transitionsBuilder: TabTransitionPage.verticalPushTransition,
                child: child,
              );
            },
            routes: [
              GoRoute(
                path: "/dash",
                builder: (context, state) => DashPage(),
              ),
              GoRoute(
                path: "/other",
                builder: (context, state) => OtherPage(),
              ),
              GoRoute(
                path: "/another",
                builder: (context, state) => AnotherPage(),
              ),
            ],
          ).toShellRoute,
        ],
      ),
    );
  }
}

class SideNavBarPage extends StatelessWidget {
  final int selectedIndex;
  final Widget subPage;

  const SideNavBarPage({
    required this.selectedIndex,
    required this.subPage,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        children: [
          CustomSideNavBar(
            selectedIndex: selectedIndex,
            items: [
              CustomSideNavBarItem(onTap: () => context.go("/dash")),
              CustomSideNavBarItem(onTap: () => context.go("/other")),
              CustomSideNavBarItem(onTap: () => context.go("/another")),
            ],
          ),
          Expanded(child: subPage),
        ],
      ),
    );
  }
}

此解决方案消除了对全局控制器的需要。该包还可以处理嵌套的导航栏设置,并且无论当前路径嵌套有多深以及用户如何到达那里,都将始终知道选择了哪个导航项。

原始答案(不再推荐):

首先,让我们创建一个控制器,用于更新导航栏中的选定选项卡。因此,它将能够计算滑动过渡的方向:

class NavRailController {
  /// The paths of the routes represented by the navigation rail.
  final routePaths = <String>["/1", "/2", "/3"];

  /// The index of the tab currently displayed.
  var currentTabIndex = 0;

  /// The index of the tab last displayed.
  var _previousTabIndex = 0;

  /// The direction of a slide transition.
  TextDirection slideDirection() {
    return currentTabIndex >= _previousTabIndex
        ? TextDirection.rtl
        : TextDirection.ltr;
  }

  /// Used in the [GoRouter] redirect to update the selected tab in the
  /// navigation rail.
  String? redirect(BuildContext context, GoRouterState state) {
    _previousTabIndex = currentTabIndex;
    currentTabIndex = routePaths.indexWhere(
      (path) => state.location.contains(path),
    );

    return null;
  }
}

通过将控制器的redirect方法添加到GoRouter中,我们可以使导航栏在每次导航到新页面时更新:

final navRailController = NavRailController();

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: GoRouter(
        initialLocation: navRailController.routePaths[0],
        redirect: navRailController.redirect,
        routes: [
          // ...
        ],
      ),
    );
  }
}

这是一个带有导航轨道的示例页面:

class NavRailPage extends StatelessWidget {
  final Widget child;

  const NavRailPage({super.key, required this.child});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        children: [
          NavigationRail(
            selectedIndex: navRailController.currentTabIndex,
            onDestinationSelected: (value) => context.go(
              navRailController.routePaths[value],
            ),
            destinations: const [
              NavigationRailDestination(
                icon: Icon(Icons.favorite_border),
                label: Text("1"),
              ),
              NavigationRailDestination(
                icon: Icon(Icons.bookmark_border),
                label: Text("2"),
              ),
              NavigationRailDestination(
                icon: Icon(Icons.star_border),
                label: Text("3"),
              ),
            ],
          ),
          Expanded(child: child),
        ],
      ),
    );
  }
}

这是导航栏旁边显示的示例页面。

class NavRailItemPage extends StatelessWidget {
  final String title;

  const NavRailItemPage(this.title, {super.key});

  @override
  Widget build(BuildContext context) {
    return Center(child: Text(title));
  }
}

现在让我们创建一个GoRouterCustomTransitionPage,让我们控制幻灯片过渡的方向:

class SlideTransitionPage extends CustomTransitionPage {
  SlideTransitionPage({
    super.key,
    required TextDirection Function() direction,
    required super.child,
  }) : super(
          transitionsBuilder: (context, animation, secondaryAnimation, child) {
            final dir = direction();

            final slideTween = Tween<Offset>(
              begin: dir == TextDirection.ltr
                  ? const Offset(0, -1)
                  : const Offset(0, 1),
              end: const Offset(0, 0),
            );
            final secondarySlideTween = Tween<Offset>(
              begin: const Offset(0, 0),
              end: dir == TextDirection.ltr
                  ? const Offset(0, 1)
                  : const Offset(0, -1),
            );
            return SlideTransition(
              position: slideTween.animate(animation),
              child: SlideTransition(
                position: secondarySlideTween.animate(secondaryAnimation),
                child: child,
              ),
            );
          },
        );
}

现在,我们可以在MyApp中添加路由到GoRouter

routes: [
          ShellRoute(
            builder: (context, state, child) => NavRailPage(child: child),
            routes: [
              GoRoute(
                path: navRailController.routePaths[0],
                pageBuilder: (context, state) => SlideTransitionPage(
                  key: state.pageKey,
                  direction: navRailController.slideDirection,
                  child: const NavRailItemPage("1"),
                ),
              ),
              GoRoute(
                path: navRailController.routePaths[1],
                pageBuilder: (context, state) => SlideTransitionPage(
                  key: state.pageKey,
                  direction: navRailController.slideDirection,
                  child: const NavRailItemPage("2"),
                ),
              ),
              GoRoute(
                path: navRailController.routePaths[2],
                pageBuilder: (context, state) => SlideTransitionPage(
                  key: state.pageKey,
                  direction: navRailController.slideDirection,
                  child: const NavRailItemPage("3"),
                ),
              )
            ],
          ),
        ],
q9yhzks0

q9yhzks02#

使用auto_route: ^7.1.0在flutter中导航,它有transitionBuilder,这样你就可以简单地在页面之间启用过渡动画。
例如:

class DashboardPage extends StatelessWidget {                
@override                
Widget build(BuildContext context) {                
return AutoTabsRouter(                
// list of your tab routes                
// routes used here must be declared as children                
// routes of /dashboard                 
  routes: const [                
    UsersRoute(),                
    PostsRoute(),                
    SettingsRoute(),                
  ],          
  transitionBuilder: (context,child,animation)=> FadeTransition(                
          opacity: animation,                
          // the passed child is technically our animated selected-tab page                
          child: child,                
        ),       
  builder: (context, child) {                
    // obtain the scoped TabsRouter controller using context                
    final tabsRouter = AutoTabsRouter.of(context);                
    // Here we're building our Scaffold inside of AutoTabsRouter                
    // to access the tabsRouter controller provided in this context                
    //                 
    //alterntivly you could use a global key                
    return Scaffold(                
        body: child,               
        bottomNavigationBar: BottomNavigationBar(                
          currentIndex: tabsRouter.activeIndex,                
          onTap: (index) {                
            // here we switch between tabs                
            tabsRouter.setActiveIndex(index);                
          },                
          items: [                
            BottomNavigationBarItem(label: 'Users',...),                
            BottomNavigationBarItem(label: 'Posts',...),                
            BottomNavigationBarItem(label: 'Settings',...),                
          ],                
        ));                
  },                
);}}

相关问题