将Flutter MaterialPageRoute作为全屏对话框显示在BottomNavigationBar

时间:2020-08-03 14:29:19

标签: flutter flutter-navigation

这里还有一个类似的未解决问题:FullscreenDialog screen not covering bottomnavigationbar 但我想提供更多背景信息,以了解这是否有助于找到解决方案。 我们将从我的main.dart开始,在构建一个MaterialApp来构建自定义DynamicApp。这是重要的东西:

Widget build(BuildContext context) {
    var _rootScreenSwitcher = RootScreenSwitcher(key: switcherKey);

    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.green,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      builder: (context, child) {
        return DynamicApp(
          navigator: locator<NavigationService>().navigatorKey,
          child: child,
          switcher: _rootScreenSwitcher,
        );
      },
      navigatorKey: locator<NavigationService>().navigatorKey,
      onGenerateRoute: (routeSettings) {
        switch (routeSettings.name) {
          case SettingsNavigator.routeName:
            return MaterialPageRoute(
                builder: (context) => SettingsNavigator(),
                fullscreenDialog: true);
          default:
            return MaterialPageRoute(builder: (context) => SettingsNavigator());
        }
      },
      home: _rootScreenSwitcher,
    );
  }

我的DynamicApp像这样设置根Scaffold

Widget build(BuildContext context) {
    return Scaffold(
      drawer: NavigationDrawer(
        selectedIndex: _selectedIndex,
        drawerItems: widget.drawerItems,
        headerView: Container(
          child: Text('Drawer Header'),
          decoration: BoxDecoration(color: Colors.blue),
        ),
        onNavigationItemSelect: (index) {
          onTapped(index);
          return true; // true means that drawer must close and false is Vice versa
        },
      ),
      bottomNavigationBar: BottomNavigationBar(
        type: BottomNavigationBarType.fixed,
        onTap: (index) {
          onTapped(index);
        },
        currentIndex: _selectedIndex,
        items: bottomNavBarItems,
        showUnselectedLabels: false,
      ),
      body: widget.child,
    );
  }

DynamicApp的子级是名为RootScreenSwitcher的小部件,它是IndexedStack,可控制屏幕从我的BottomNavigationBar的切换,以及在Drawer。这是RootScreenSwitcher

class RootScreenSwitcherState extends State<RootScreenSwitcher> {
  int _currentIndex = 0;
  int get currentIndex => _currentIndex;
  set currentIndex(index) {
    setState(() {
      _currentIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        top: false,
        child: IndexedStack(
          index: _currentIndex,
          children: menuItems.map<Widget>((MenuItem item) {
            return item.widget;
          }).toList(),
        ),
      ),
    );
  }

  void switchScreen(int index) {
    setState(() {
      _currentIndex = index;
    });
  }
}

应用程序的每个主要部分都有自己的Navigator和根屏幕。一切正常,我对整体导航结构感到满意。每个根屏幕都有其自己的AppBar,但是全局DrawerBottomNavigationBar是在DynamicApp中处理的,因此我不必继续在另一个{{1 }}屏幕。

因此,接着开始介绍该应用程序的其他部分,这些部分不受底部标签栏的服务,可以通过Scaffold或其他操作按钮进行显示。这些新部分中的每一个都必须是模式Drawer屏幕,以便它们从底部向上滑动,但是具有自己的导航和支架。

我的问题是,当我导航到fullscreenDialog屏幕时,它是从SettingsNavigator的后面向上滑动的,而不是在所有内容的顶部。这是BottomNavigationBar的{​​{1}}方法:

onGenerateRoute

我是Flutter的新手,还不太了解路由如何与上下文一起使用,因此我想知道调用MaterialApp的{​​{1}}方法的屏幕上下文是否成为主要对象构建上下文,因此不在小部件树的顶部吗?

gif此处:https://www.dropbox.com/s/nwgzo0q28cqk61p/FlutteRModalProb.gif?dl=0

这是树状结构,显示设置屏幕的脚手架已放置在onGenerateRoute: (routeSettings) { switch (routeSettings.name) { case SettingsNavigator.routeName: return MaterialPageRoute( builder: (context) => SettingsNavigator(), fullscreenDialog: true); default: return MaterialPageRoute(builder: (context) => SettingsNavigator()); } } navigateTo内。模态需要坐在Navigator上方。 enter image description here

有人能对此有所启发吗?

更新:我尝试为标签栏屏幕创建和共享唯一的DynamicApp键,然后“设置”页面具有不同的键。没关系。我现在想知道是不是Scaffold拥有相同的父母。

UPDATE UPDATE: 昨晚我取得了突破,这使我意识到以目前的方式使用嵌入式脚手架是不可能的。问题是我有一个名为DynamicApp的根脚手架,它可以将我的ScaffoldStateBuildContext保留下来,但是将其他DynamicApp页加载到正文中意味着这些模式被插入其中正文和Drawer之后。要解决该问题,您必须将BottomNavigationBar子类化,并在每个Scaffold中引用它;这意味着封装所有业务逻辑,以便在与导航交互时使用BottomNavigationBar来更改状态。基本上,Flutter会在您的体系结构上强制关注点分离,我认为这是一件好事。完成所有额外工作后,我将为您提供一个更好的答案。

1 个答案:

答案 0 :(得分:0)

经过数小时的努力,我试图将ScaffoldState的密钥传递出去,而Navigators的答案是将BottomNavigationBar构建到每个Scaffold中。但是要做到这一点,我不得不改变我的架构的工作方式……变得更好!现在,我有一个BottomNavigationBarRootScreenSwitcher来监听AppState ChangeNotifier的更新,并在页面索引更改时自行重建。因此,我只需要在一个地方更改状态即可使应用程序自动适应。这是AppState类:

import 'package:flutter/material.dart';

class AppState extends ChangeNotifier {

  int _pageIndex = 0;
  int get pageIndex {
    return _pageIndex;
  }

  set setpageIndex(int index) {
    _pageIndex = index;
    notifyListeners();
  }
}

这是我的自定义BottomNavigationBar,名为AppBottomNavigationBar

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

class AppBottomNavigationBar extends StatefulWidget {
  AppBottomNavigationBar({Key key}) : super(key: key);
  @override
  _AppBottomNavigationBarState createState() => _AppBottomNavigationBarState();
}

class _AppBottomNavigationBarState extends State<AppBottomNavigationBar> {
  @override
  Widget build(BuildContext context) {
    var state = Provider.of<AppState>(
      context,
    );
    int currentIndex = state.pageIndex;
    return BottomNavigationBar(
      type: BottomNavigationBarType.fixed,
      currentIndex: currentIndex ?? 0,
      items: bottomNavBarItems,
      showUnselectedLabels: false,
      onTap: (int index) {
        setState(() {
          state.setpageIndex = index;
        });
      },
    );
  }
}

因此,现在在我的其他Scaffold页中,我只需要包括以下行以确保BottomNavigationBar is in the Scaffold`:

bottomNavigationBar: AppBottomNavigationBar(),

这意味着绝对最小的样板。 我将DynamicApp类的名称更改为AppRootScaffold,现在它包含一个ScaffoldDrawer,然后将Scaffold的主体设置为{ {1}}:

RootScreenSwitcher

要简化此架构,我还有很多工作要做,但这绝对是更好的选择。

更新: 您能发现新的class RootScreenSwitcher extends StatelessWidget { RootScreenSwitcher({Key key}) : super(key: key); @override Widget build(BuildContext context) { var state = Provider.of<AppState>( context, ); int currentIndex = state.pageIndex; return SafeArea( top: false, child: IndexedStack( index: currentIndex ?? 0, children: menuItems.map<Widget>((MenuItem item) { return item.widget; }).toList(), ), ); } } 体系结构的问题吗? enter image description here 这仍然不是很好。具有讽刺意味的是,我需要回到根Scaffold中的BottomNavigationBar才能正常工作。但是随后,模态将不会再次出现在栏的顶部。