簡體   English   中英

如何在 go_router 中使用 NavigationBar? | Flutter

[英]How to work with NavigationBar in go_router? | Flutter

我目前正在努力用go_router重構我的路由代碼。

我已經有了一些簡單的路由,例如/signin & /signup ,但是當我嘗試使路由與具有多個屏幕的 BottomNavigationBar 一起工作時,問題就來了。 我想為他們每個人提供單獨的路線,如/home/events/profile

我發現我必須以某種方式返回具有不同參數的相同小部件,以防止在按下 BottomNavigationBarItem 時整個屏幕發生變化,而是只更新 BottomNavigationBar 上方的部分,這將是屏幕本身。

我想出了一個非常棘手的解決方案:

GoRoute(
  path: '/:path',
  builder: (BuildContext context, GoRouterState state) {
    final String path = state.params['path']!;

    if (path == 'signin') {
      return const SignInScreen();
    }

    if (path == 'signup') {
      return const SignUpScreen();
    }

    if (path == 'forgot-password') {
      return const ForgotPasswordScreen();
    }

    // Otherwise it has to be the ScreenWithBottomBar

    final int index = getIndexFromPath(path);

    if (index != -1) {
      return MainScreen(selectedIndex: index);
    }

    return const ErrorScreen();
  }
)

這看起來不太好,並且無法添加像/profile/settings/appearance/events/:id這樣的子路徑。

我想要一些簡單易懂的東西:

GoRoute(
  path: '/signin',
  builder: (BuildContext context, GoRouterState state) {
    return const SignInScreen();
  }
),
GoRoute(
  path: '/signup',
  builder: (BuildContext context, GoRouterState state) {
    return const SignUpScreen();
  }
),
GoRoute(
  path: '/home',
  builder: (BuildContext context, GoRouterState state) {
    return const ScreenWithNavBar(selectedScreen: 1);
  }
),
GoRoute(
  path: '/events',
  builder: (BuildContext context, GoRouterState state) {
    return const ScreenWithNavBar(selectedScreen: 2);
  },
  routes: <GoRoute>[
    GoRoute(
      path: ':id',
      builder: (BuildContext context, GoRouterState state) {
        return const EventScreen();
      }
    )
  ]
)

有什么辦法可以實現這種行為嗎?

這是對 go_router的一項出色功能請求,我希望在未來幾周內解決。 敬請關注。

最終對我有用的解決方案如下:

我能夠創建一個指定允許哪些值的路由:

GoRoute(
  path: '/:screen(home|discover|notifications|profile)',
  builder: (BuildContext context, GoRouterState state) {
    final String screen = state.params['screen']!;

    return TabScreen(screen: screen);
  }
)

完成后,我將路由包含的任何值(例如“/home”或“/discover”)傳遞給 TabScreen,然后顯示完全相同的小部件,但無需一遍又一遍地重新加載 TabBar:

class TabScreen extends StatelessWidget {

  const TabScreen({
    super.key,
    required this.screen
  });

  final String screen;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Expanded(child: screen == 'home' ? const HomeScreen() : screen == 'discover' ? const DiscoverScreen() : screen == 'notifications' ? const NotificationsScreen() : ProfileScreen(),
        CustomBottomNavigationBar()
     
      ]
    );
  }
}

現在您可以將ShellRouterGoRouter一起使用來創建Navigation Bar


說明:

ShellRouteGoRoute使用context.go()時要記住的事情

  1. 在每個GoRoute中指定parentNavigatorKey屬性
  2. 使用context.go()替換頁面,使用 context.push( context.push()將頁面推入堆棧

代碼結構遵循:


final _parentKey = GlobalKey<NavigatorState>();
final _shellKey = GlobalKey<NavigatorState>();

|_ GoRoute
  |_ parentNavigatorKey = _parentKey   👈 Specify key here
|_ ShellRoute
  |_ GoRoute                            // Needs Bottom Navigation                  
    |_ parentNavigatorKey = _shellKey   
  |_ GoRoute                            // Needs Bottom Navigation
    |_ parentNavigatorKey = _shellKey   
|_ GoRoute                              // Full Screen which doesn't need Bottom Navigation
  |_parentNavigatorKey = _parentKey

代碼:

路由器


final _rootNavigatorKey = GlobalKey<NavigatorState>();
final _shellNavigatorKey = GlobalKey<NavigatorState>();

final router = GoRouter(
  initialLocation: '/',
  navigatorKey: _rootNavigatorKey,
  routes: [
    ShellRoute(
      navigatorKey: _shellNavigatorKey,
      pageBuilder: (context, state, child) {
        print(state.location);
        return NoTransitionPage(
            child: ScaffoldWithNavBar(
          location: state.location,
          child: child,
        ));
      },
      routes: [
        GoRoute(
          path: '/',
          parentNavigatorKey: _shellNavigatorKey,
          pageBuilder: (context, state) {
            return const NoTransitionPage(
              child: Scaffold(
                body: Center(child: Text("Home")),
              ),
            );
          },
        ),
        GoRoute(
          path: '/discover',
          parentNavigatorKey: _shellNavigatorKey,
          pageBuilder: (context, state) {
            return const NoTransitionPage(
              child: Scaffold(
                body: Center(child: Text("Discover")),
              ),
            );
          },
        ),
        GoRoute(
            parentNavigatorKey: _shellNavigatorKey,
            path: '/shop',
            pageBuilder: (context, state) {
              return const NoTransitionPage(
                child: Scaffold(
                  body: Center(child: Text("Shop")),
                ),
              );
            }),
      ],
    ),
    GoRoute(
      parentNavigatorKey: _rootNavigatorKey,
      path: '/login',
      pageBuilder: (context, state) {
        return NoTransitionPage(
          key: UniqueKey(),
          child: Scaffold(
            appBar: AppBar(),
            body: const Center(
              child: Text("Login"),
            ),
          ),
        );
      },
    ),
  ],
);
底部導航欄
class ScaffoldWithNavBar extends StatefulWidget {
  String location;
  ScaffoldWithNavBar({super.key, required this.child, required this.location});

  final Widget child;

  @override
  State<ScaffoldWithNavBar> createState() => _ScaffoldWithNavBarState();
}

class _ScaffoldWithNavBarState extends State<ScaffoldWithNavBar> {
  int _currentIndex = 0;

  static const List<MyCustomBottomNavBarItem> tabs = [
    MyCustomBottomNavBarItem(
      icon: Icon(Icons.home),
      activeIcon: Icon(Icons.home),
      label: 'HOME',
      initialLocation: '/',
    ),
    MyCustomBottomNavBarItem(
      icon: Icon(Icons.explore_outlined),
      activeIcon: Icon(Icons.explore),
      label: 'DISCOVER',
      initialLocation: '/discover',
    ),
    MyCustomBottomNavBarItem(
      icon: Icon(Icons.storefront_outlined),
      activeIcon: Icon(Icons.storefront),
      label: 'SHOP',
      initialLocation: '/shop',
    ),
    MyCustomBottomNavBarItem(
      icon: Icon(Icons.account_circle_outlined),
      activeIcon: Icon(Icons.account_circle),
      label: 'MY',
      initialLocation: '/login',
    ),
  ];

  @override
  Widget build(BuildContext context) {
    const labelStyle = TextStyle(fontFamily: 'Roboto');
    return Scaffold(
      body: SafeArea(child: widget.child),
      bottomNavigationBar: BottomNavigationBar(
        selectedLabelStyle: labelStyle,
        unselectedLabelStyle: labelStyle,
        selectedItemColor: const Color(0xFF434343),
        selectedFontSize: 12,
        unselectedItemColor: const Color(0xFF838383),
        showUnselectedLabels: true,
        type: BottomNavigationBarType.fixed,
        onTap: (int index) {
          _goOtherTab(context, index);
        },
        currentIndex: widget.location == '/'
            ? 0
            : widget.location == '/discover'
                ? 1
                : widget.location == '/shop'
                    ? 2
                    : 3,
        items: tabs,
      ),
    );
  }

  void _goOtherTab(BuildContext context, int index) {
    if (index == _currentIndex) return;
    GoRouter router = GoRouter.of(context);
    String location = tabs[index].initialLocation;

    setState(() {
      _currentIndex = index;
    });
    if (index == 3) {
      context.push('/login');
    } else {
      router.go(location);
    }
  }
}

class MyCustomBottomNavBarItem extends BottomNavigationBarItem {
  final String initialLocation;

  const MyCustomBottomNavBarItem(
      {required this.initialLocation,
      required Widget icon,
      String? label,
      Widget? activeIcon})
      : super(icon: icon, label: label, activeIcon: activeIcon ?? icon);
}

Output:

在此處輸入圖像描述

在此處輸入圖像描述

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM