【问题标题】:Flutter navigation push, while keeping the same AppbarFlutter 导航推送,同时保持 Appbar 不变
【发布时间】:2021-06-19 15:31:02
【问题描述】:

我目前正在构建一个 Flutter 应用程序,我正在努力找出实现导航的最佳方式。

我有 2 页,分别是:

  • 主页:我想从那里使用 IndexedStack 来管理提要。
  • ProfilePage:个人资料页面,(以图形方式)与主页共享相同的 AppBar 和相同的 Drawer。

在我的应用程序中,用户在登录后立即到达主页。不涉及导航。

从那里,我现在有一个 TextButton,它调用 Navigator.of(context).pushNamed(AppRoutes.profile)

正如我所说,两个页面共享相同的 Appbar 和 Drawer,因此我创建了一个自定义 myScaffold。 两个页面都使用这个脚手架。

所以行为是正确的,因为在单击按钮后,ProfilePage 移动到了 HomePage 上。

我的问题是应用栏在图形上应该保持不变,但是当个人资料页面被推送时,动画清楚地表明它不是同一个应用栏。

  • 是否可以为个人资料页面的条目设置动画,无需 动画应用栏的重建?

  • 或者是否可以将路由直接推送到脚手架内容中?

  • 作为替代方案,我只是想写一个函数 返回要在脚手架内容中显示的页面小部件。 但这种方法对我来说似乎不合适,因为有 路线。

从官方文档中,您可以从交互式示例中看到我的意思: Docs

当第二条路线建立在第一条路线之上时,新的 Appbar 会建立在前一条路线之上。 但是如果我需要 appbar 保持不变呢?

【问题讨论】:

    标签: flutter user-interface navigation


    【解决方案1】:

    您可以使用Navigator 类创建子导航器。

    我在当前项目中创建了一个路由库 (routes.dart),用于在仍显示 bottomNavigationBar 时导航到其他屏幕。使用相同的想法,您可以在使用相同的AppBar 的同时执行导航。

    这是您的场景的示例代码。

    ma​​in.dart

    import 'package:flutter/material.dart';
    import 'package:flutter2sample/routes.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          navigatorKey: Routes.rootNavigatorKey,
          initialRoute: Routes.PAGE_INITIAL,
          onGenerateRoute: Routes.onGenerateRoute,
        );
      }
    }
    

    routes.dart

    import 'package:flutter/cupertino.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    import 'package:flutter2sample/pages/home_page.dart';
    import 'package:flutter2sample/pages/initial_page.dart';
    import 'package:flutter2sample/pages/main_page.dart';
    import 'package:flutter2sample/pages/profile_page.dart';
    
    class Routes {
      Routes._();
    
      static const String PAGE_INITIAL = '/';
      static const String PAGE_MAIN = '/main';
      static const String PAGE_HOME = '/home';
      static const String PAGE_PROFILE = '/profile';
    
      static final GlobalKey<NavigatorState> rootNavigatorKey =
          GlobalKey<NavigatorState>();
      static final GlobalKey<NavigatorState> mainNavigatorKey =
          GlobalKey<NavigatorState>();
    
      static String currentSubNavigatorInitialRoute;
    
      static CupertinoPageRoute<Widget> onGenerateRoute(RouteSettings settings) {
        Widget page;
    
        switch (settings.name) {
          case PAGE_INITIAL:
            page = InitialPage();
            break;
          case PAGE_MAIN:
            page = MainPage();
            break;
          case PAGE_HOME:
            page = HomePage();
            break;
          case PAGE_PROFILE:
            page = ProfilePage();
            break;
        }
    
        if (settings.name == PAGE_INITIAL &&
            currentSubNavigatorInitialRoute != null) {
          // When current sub-navigator initial route is set,
          // do not display initial route because it is already displayed.
          return null;
        }
    
        return CupertinoPageRoute<Widget>(
          builder: (_) {
            if (currentSubNavigatorInitialRoute == settings.name) {
              return WillPopScope(
                onWillPop: () async => false,
                child: page,
              );
            }
    
            return page;
          },
          settings: settings,
        );
      }
    
      /// [MaterialApp] navigator key.
      ///
      ///
      static NavigatorState get rootNavigator => rootNavigatorKey.currentState;
    
      /// [PAGE_MAIN] navigator key.
      ///
      ///
      static NavigatorState get mainNavigator => mainNavigatorKey.currentState;
    
      /// Navigate to screen via [CupertinoPageRoute].
      ///
      /// If [navigator] is not set, it will use the [rootNavigator].
      static void push(Widget screen, {NavigatorState navigator}) {
        final CupertinoPageRoute<Widget> route = CupertinoPageRoute<Widget>(
          builder: (_) => screen,
        );
    
        if (navigator != null) {
          navigator.push(route);
          return;
        }
    
        rootNavigator.push(route);
      }
    
      /// Navigate to route name via [CupertinoPageRoute].
      ///
      /// If [navigator] is not set, it will use the [rootNavigator].
      static void pushNamed(
        String routeName, {
        NavigatorState navigator,
        Object arguments,
      }) {
        if (navigator != null) {
          navigator.pushNamed(routeName, arguments: arguments);
          return;
        }
    
        rootNavigator.pushNamed(routeName, arguments: arguments);
      }
    
      /// Pop current route of [navigator].
      ///
      /// If [navigator] is not set, it will use the [rootNavigator].
      static void pop<T extends Object>({
        NavigatorState navigator,
        T result,
      }) {
        if (navigator != null) {
          navigator.pop(result);
          return;
        }
    
        rootNavigator.pop(result);
      }
    }
    
    //--------------------------------------------------------------------------------
    /// A navigator widget who is a child of [MaterialApp] navigator.
    ///
    ///
    class SubNavigator extends StatelessWidget {
      const SubNavigator({
        @required this.navigatorKey,
        @required this.initialRoute,
        Key key,
      }) : super(key: key);
    
      final GlobalKey<NavigatorState> navigatorKey;
      final String initialRoute;
    
      @override
      Widget build(BuildContext context) {
        final _SubNavigatorObserver _navigatorObserver = _SubNavigatorObserver(
          initialRoute,
          navigatorKey,
        );
        Routes.currentSubNavigatorInitialRoute = initialRoute;
    
        return WillPopScope(
          onWillPop: () async {
            if (_navigatorObserver.isInitialPage) {
              Routes.currentSubNavigatorInitialRoute = null;
              await SystemNavigator.pop();
              return true;
            }
    
            final bool canPop = navigatorKey.currentState.canPop();
    
            if (canPop) {
              navigatorKey.currentState.pop();
            }
    
            return !canPop;
          },
          child: Navigator(
            key: navigatorKey,
            observers: <NavigatorObserver>[_navigatorObserver],
            initialRoute: initialRoute,
            onGenerateRoute: Routes.onGenerateRoute,
          ),
        );
      }
    }
    
    //--------------------------------------------------------------------------------
    /// [NavigatorObserver] of [SubNavigator] widget.
    ///
    ///
    class _SubNavigatorObserver extends NavigatorObserver {
      _SubNavigatorObserver(this._initialRoute, this._navigatorKey);
    
      final String _initialRoute;
      final GlobalKey<NavigatorState> _navigatorKey;
      final List<String> _routeNameStack = <String>[];
    
      bool _isInitialPage = false;
    
      /// Flag if current route is the initial page.
      ///
      ///
      bool get isInitialPage => _isInitialPage;
    
      @override
      void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
        _routeNameStack.add(route.settings.name);
        _isInitialPage = _routeNameStack.last == _initialRoute;
      }
    
      @override
      void didPop(Route<dynamic> route, Route<dynamic> previousRoute) {
        _routeNameStack.remove(route.settings.name);
        _isInitialPage = _routeNameStack.last == _initialRoute;
      }
    
      @override
      void didRemove(Route<dynamic> route, Route<dynamic> previousRoute) {
        _routeNameStack.remove(route.settings.name);
        _isInitialPage = _routeNameStack.last == _initialRoute;
      }
    
      @override
      void didReplace({Route<dynamic> newRoute, Route<dynamic> oldRoute}) {
        _routeNameStack.remove(oldRoute.settings.name);
        _routeNameStack.add(newRoute.settings.name);
        _isInitialPage = _routeNameStack.last == _initialRoute;
      }
    }
    

    initial_page.dart

    import 'package:flutter/material.dart';
    import 'package:flutter2sample/routes.dart';
    
    class InitialPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Initial Page'),
          ),
          body: Center(
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                const Text('This is INITIAL page'),
                TextButton(
                  onPressed: () => Routes.pushNamed(Routes.PAGE_MAIN),
                  child: const Text('To Main page'),
                ),
              ],
            ),
          ),
        );
      }
    }
    

    ma​​in_page.dart

    import 'package:flutter/material.dart';
    import 'package:flutter2sample/routes.dart';
    
    class MainPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Main Page'),
          ),
          body: SubNavigator(
            navigatorKey: Routes.mainNavigatorKey,
            initialRoute: Routes.PAGE_HOME,
          ),
        );
      }
    }
    

    home_page.dart

    import 'package:flutter/material.dart';
    import 'package:flutter2sample/routes.dart';
    
    class HomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: Colors.yellow,
          body: SafeArea(
            child: Center(
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: <Widget>[
                  const Text('This is HOME page'),
                  TextButton(
                    onPressed: () => Routes.pushNamed(
                      Routes.PAGE_PROFILE,
                      navigator: Routes.mainNavigator,
                    ),
                    child: const Text('To Profile page'),
                  ),
                ],
              ),
            ),
          ),
        );
      }
    }
    

    profile_page.dart

    import 'package:flutter/material.dart';
    import 'package:flutter2sample/routes.dart';
    
    class ProfilePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: Colors.grey,
          body: SafeArea(
            child: Center(
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: <Widget>[
                  const Text('This is PROFILE page'),
                  TextButton(
                    onPressed: () => Routes.pop(navigator: Routes.mainNavigator),
                    child: const Text('Back to Home page'),
                  ),
                ],
              ),
            ),
          ),
        );
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-12
      • 1970-01-01
      • 2021-02-12
      • 2013-07-19
      • 2019-07-20
      • 2023-03-27
      • 1970-01-01
      • 2020-03-04
      相关资源
      最近更新 更多