Flutter在Web上的路由作为单页面应用程序

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

Flutter routing in web as single page application

问题

我有一个Web上的仪表板,它具有通用的布局,例如Web上的侧边菜单,当更改时它的视图会反映出来。

我已经设置了路由如下:

final GoRouter _router = GoRouter(routes: <RouteBase>[
  GoRoute(
    path: '/',
    builder: (BuildContext context, GoRouterState state) {
      return const LoginScreen();
    },
  ),
  GoRoute(
      path: '/dashboard',
      builder: (BuildContext context, GoRouterState state) {
        return const Dashboard();
      },
      routes: [
        GoRoute(
          path: 'home',
          builder: (BuildContext context, GoRouterState state) {
            return const HomeScreen();
          },
        ),
        GoRoute(
          path: 'home2',
          builder: (BuildContext context, GoRouterState state) {
            return const Home2Screen();
          },
        ),
      ]),
]);

我需要知道是否不可能像我所说的那样固定侧边菜单和路由在仪表板屏幕上,并且它会在视图中更改。

示例:

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        children: [
         SideMenu(),
         Route() //Home or Home1 
        ],
      ),
    );
  }
}

所以这里有侧边菜单和路由。如果URL路由是/dashboard/home/dashboard/home1,它会改变路由视图吗?因为如果我在每个home或home1文件中都添加一个侧边菜单,那么屏幕上将可见到屏幕的更改,这是我不想要的。

英文:

I have a dashboard on web, which have common layout like side menu on web and on change its view will reflect.

I have setup route like this:

final GoRouter _router = GoRouter(routes: &lt;RouteBase&gt;[
  GoRoute(
    path: &#39;/&#39;,
    builder: (BuildContext context, GoRouterState state) {
      return const LoginScreen();
    },
  ),
  GoRoute(
      path: &#39;/dashboard&#39;,
      builder: (BuildContext context, GoRouterState state) {
        return const Dashboard();
      },
      routes: [
        GoRoute(
          path: &#39;home&#39;,
          builder: (BuildContext context, GoRouterState state) {
            return const HomeScreen();
          },
        ),
        GoRoute(
          path: &#39;home2&#39;,
          builder: (BuildContext context, GoRouterState state) {
            return const Home2Screen();
          },
        ),
      ]),
]);

I need to know is it not possible like I fix side menu and route on dashboard screen and it will change in view.

Example:

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        children: [
         SideMenu(),
         Route() //Home or Home1 
        ],
      ),
    );
  }
}

So here is side menu and route. Some if url route is /dashboard/home or /dashboard/home1 it will change the Route view? Because if I added a side menu in each file of home or home1 then there will be screen change visible on it which I don't want.

答案1

得分: 2

在你的 GoRouter 内部,创建一个类似这样的 shell 路由:

final rootNavigatorKey = GlobalKey<NavigatorState>();

final _shellNavigatorKey = GlobalKey<NavigatorState>();

final GoRouter _router = GoRouter(
  navigatorKey: rootNavigatorKey,
  routes: <RouteBase>[
    GoRoute(
      path: '/',
      builder: (BuildContext context, GoRouterState state) {
        return const LoginScreen();
      },
    ),
    ShellRoute(
      navigatorKey: _shellNavigatorKey,
      builder: (BuildContext context, GoRouterState state, Widget child) {
        return Dashboard(child: child);
      },
      routes: [
        GoRoute(
          name: RouteConstants.homeScreenRoute,
          path: '/home',
          pageBuilder: (context, state) => const NoTransitionPage(
            child: HomeScreen(),
          ),
        ),
        GoRoute(
          name: RouteConstants.home2ScreenRoute,
          path: '/home2',
          pageBuilder: (context, state) => const NoTransitionPage(
            child: Home2Screen(),
          ),
        ),
      ],
    ),
  ],
]);

而你的仪表板页面可以是这样的:

class Dashboard extends StatelessWidget {
  final Widget child;

  const Dashboard({
    Key? key,
    required this.child,
  }) : super(key: key);

  Widget build(BuildContext context) {
    return Scaffold(
      body: child,
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _calculateSelectedIndex(context),
        onTap: (int idx) => onTap(idx, context),
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
          BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Home2'),
        ],
      ),
    );
  }

  int _calculateSelectedIndex(BuildContext context) {
    final GoRouter route = GoRouter.of(context);
    final String location = route.location;
    if (location == RouteConstants.homeScreenRoute) {
      return 0;
    } else if (location == RouteConstants.home2ScreenRoute) {
      return 1;
    }
    return 0;
  }

  void onTap(int index, BuildContext context) {
    switch (index) {
      case 0:
        context.go(RouteConstants.homeScreenRoute);
        break;
      case 1:
        context.go(RouteConstants.home2ScreenRoute);
        break;
      default:
        return context.go(RouteConstants.homeScreenRoute);
    }
  }
}

希望这有助于你的项目!

英文:

Inside your GoRouter, create a shell route like this:

final rootNavigatorKey = GlobalKey&lt;NavigatorState&gt;();

final _shellNavigatorKey = GlobalKey&lt;NavigatorState&gt;();

final GoRouter _router = GoRouter(
navigatorKey: rootNavigatorKey,
routes: &lt;RouteBase&gt;[
  GoRoute(
    path: &#39;/&#39;,
    builder: (BuildContext context, GoRouterState state) {
      return const LoginScreen();
    },
  ),
  ShellRoute(
        navigatorKey: _shellNavigatorKey,
        builder: (BuildContext context, GoRouterState state, Widget child) {
          return Dashboard(child: child);
        },
        routes: [
          GoRoute(
            name: RouteConstants.homeScreenRoute,
            path: &#39;/home&#39;,
            pageBuilder: (context, state) =&gt; const NoTransitionPage(
              child: HomeScreen(),
            ),
          ),
          GoRoute(
            name: RouteConstants.home2ScreenRoute,
            path: &#39;/home2&#39;,
            pageBuilder: (context, state) =&gt; const NoTransitionPage(
              child: Home2Screen(),
            ),
          ),
        ],
      ),
  
]);

And your dashboard page can be like:


class Dashboard extends StatelessWidget {
  final Widget child;

  const BottomNavigation({
    Key? key,
    required this.child,
  }) : super(key: key);

  Widget build(BuildContext context) {
    return Scaffold(
      body: child,
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _calculateSelectedIndex(context),
        onTap: (int idx) =&gt; onTap(idx, context),
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: &#39;Home&#39;),
          BottomNavigationBarItem(icon: Icon(Icons.person), label: &#39;Home2&#39;),

        ],
      ),
    );
  }

  int _calculateSelectedIndex(BuildContext context) {
    final GoRouter route = GoRouter.of(context);
    final String location = route.location;
    if (location == RouteConstants.homeScreenRoute) {
      return 0;
    } else if (location == RouteConstants.home2ScreenRoute) {
      return 1;
    }
    return 0;
  }

  void onTap(int index, BuildContext context) {
    switch (index) {
      case 0:
        context.go(RouteConstants.homeScreenRoute);
        break;

      case 1:
        context.go(RouteConstants.home2ScreenRoute);
        break;

      default:
        return context.go(RouteConstants.homeScreenRoute);
    }
  }
}

答案2

得分: 0

你可以使用"Stateful Nested Routes"来解决你的问题。实际上,你可以将侧边菜单视为底部导航栏。这样,你就不必将侧边菜单添加到所有屏幕上。

我为你提供了一个小示例。

Scaffold(
  body: /* TODO: 决定显示哪个页面,取决于所选的索引 */,
  bottomNavigationBar: NavigationBar(
    selectedIndex: /* TODO: 这是从哪里获取的? */,
    destinations: const [
      // 使用[NavigationDestination]小部件定义每个选项卡的外观
      NavigationDestination(label: 'Section A', icon: Icon(Icons.home)),
      NavigationDestination(label: 'Section B', icon: Icon(Icons.settings)),
    ],
    onDestinationSelected: (index) { /* TODO: 移动到索引处的选项卡 */ },
  ),
)

你可以从这里获取关于Go Router的嵌套路由的更多信息:https://codewithandrea.com/articles/flutter-bottom-navigation-bar-nested-routes-gorouter/

但也许这个使用方法不适合你的路径"/dashboard/home"。你必须检查你的URL结构是否重要。

英文:

You can use "Stateful Nested Routes" for your problem. You can actually think of the side menu as a bottom navigation bar. This way you won't have to add a side menu to all the screens.

I put a small example for you.

Scaffold(
  body: /* TODO: decide which page to show, depending on the selected index */,
  bottomNavigationBar: NavigationBar(
    selectedIndex: /* TODO: where does this come from? */,
    destinations: const [
      // the appearance of each tab is defined with a [NavigationDestination] widget
      NavigationDestination(label: &#39;Section A&#39;, icon: Icon(Icons.home)),
      NavigationDestination(label: &#39;Section B&#39;, icon: Icon(Icons.settings)),
    ],
    onDestinationSelected: (index) { /* TODO: move to the tab at index */ },
  ),
)

And you can access more information about Nested Routes of Go Router from here: https://codewithandrea.com/articles/flutter-bottom-navigation-bar-nested-routes-gorouter/

But maybe this using does not match your path "/dashboard/home". You must check if your URL structure is important.

答案3

得分: 0

你可以尝试使用ShellRoute

ShellRoute(
  builder: (BuildContext context, GoRouterState state, Widget child) {
    return Scaffold(
      appBar: AppBar(
        title: Text('App Shell')
      ),
      body: Center(
        child: child,
      ),
    );
  },
  routes: [
    GoRoute(
      path: 'a',
      builder: (BuildContext context, GoRouterState state) {
        return Text('Child Route "/a"');
      }
    ),
  ],
),

在这个示例中,应用栏中的文本 App Shell 将保持不变,但当您切换到 a 路由时,它将在 ShellRoute 的 body 属性内呈现 Text('Child Route "/a"') 小部件。

英文:

you can try ShellRoute

    ShellRoute(
  builder: (BuildContext context, GoRouterState state, Widget child) {
    return Scaffold(
      appBar: AppBar(
        title: Text(&#39;App Shell&#39;)
      ),
      body: Center(
        child: child,
      ),
    );
  },
  routes: [
    GoRoute(
      path: &#39;a&#39;
      builder: (BuildContext context, GoRouterState state) {
        return Text(&#39;Child Route &quot;/a&quot;&#39;);
      }
    ),
  ],
),

in this example, appbar with the text App Shell will be constant but when you change it to a route it will render Text(&#39;Child Route &quot;/a&quot;&#39;) widget inside the ShellRoute body property.

huangapple
  • 本文由 发表于 2023年7月10日 21:37:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/76654311.html
匿名

发表评论

匿名网友

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

确定