expo / ex-navigation

Route-centric navigation for React Native
997 stars 201 forks source link

How to do a drawer + tabs with shared routes? (Same route can be accessed via drawer or tab) #383

Closed booboothefool closed 7 years ago

booboothefool commented 7 years ago

I tried doing something like this:

Drawer

      <DrawerNavigation
        id="main"
        drawerPosition="left"
        renderHeader={this._renderHeader}
        drawerWidth={300}
        initialItem="tabs">
        <DrawerNavigationItem
          id="tabs"
          selectedStyle={styles.selectedItemStyle}
          renderTitle={isSelected => this._renderTitle('Browse', isSelected)}
          renderIcon={isSelected => this._renderIcon('md-apps', isSelected)}>
          <Tabs initialTab={'browse'} />
        </DrawerNavigationItem>
        <DrawerNavigationItem
          id="tabs"
          selectedStyle={styles.selectedItemStyle}
          renderTitle={isSelected => this._renderTitle('Visitors', isSelected)}
          renderIcon={isSelected => this._renderIcon('md-apps', isSelected)}>
          <Tabs initialTab={'visitors'} />
        </DrawerNavigationItem>
      </DrawerNavigation>

Tabs

      <View style={styles.container}>
        <TabNavigation
          id="tab-navigation"
          navigatorUID="tab-navigation"
          initialTab={initialTab}>
          <TabNavigationItem
            id="browse"
            title="Browse"
            selectedStyle={styles.selectedTab}
            renderTitle={this._renderTitle}
            renderIcon={(isSelected) => <Icon name="ios-boat-outline" size={24} color={getColor(isSelected)} />}>
            <StackNavigation
              id="browse"
              defaultRouteConfig={{
                navigationBar: {
                  backgroundColor: '#0084FF',
                  tintColor: '#fff',
                },
              }}
              initialRoute={Router.getRoute('browse')}
            />
          </TabNavigationItem>
          <TabNavigationItem
            id="visitors"
            title="Visitors"
            selectedStyle={styles.selectedTab}
            renderTitle={this._renderTitle}
            renderIcon={(isSelected) => <Icon name="ios-boat-outline" size={24} color={getColor(isSelected)} />}
            renderBadge={() => this._renderBadge(_.get(userVisitors, 'unseenCount'))}
          >
            <StackNavigation
              id="visitors"
              defaultRouteConfig={{
                navigationBar: {
                  backgroundColor: '#0084FF',
                  tintColor: '#fff',
                },
              }}
              initialRoute={Router.getRoute('visitors')}
            />
          </TabNavigationItem>
        </TabNavigation>
      </View>

But I don't really get it. Basically I want to be able to get to Browse and Visitors from either the drawer or the tabs. <Tabs> work, but only the first <DrawerNavigationItem> works. Also, clicking on a <DrawerNavigationItem> should highlight that corresponding <TabNavigationItem>.

booboothefool commented 7 years ago

I was able to achieve the first step with:

        <DrawerNavigationItem
          id="visitors"
          selectedStyle={styles.selectedItemStyle}
          renderTitle={isSelected => this._renderTitle('Visitors', isSelected)}
          renderIcon={isSelected => this._renderIcon('md-apps', isSelected)}
          onPress={() => this.props.navigation.getNavigator('tab-navigation').jumpToTab('visitors')}
        />

This way, pressing 'visitors' in my drawer actually brings me that tab.

However the stuff in <Tabs> are missing the hamburger to open the <DrawerNavigation>? (swipe gesture to open drawer works, so I know it's wrapping properly)

booboothefool commented 7 years ago

So I noticed:

        <DrawerNavigationItem
          id="visitors"
          selectedStyle={styles.selectedItemStyle}
          renderTitle={isSelected => this._renderTitle('Visitors', isSelected)}
          renderIcon={isSelected => this._renderIcon('md-apps', isSelected)}
        >
        <StackNavigation ...>

^ puts the hamburger icon for you.

Whereas:

        <DrawerNavigationItem
          id="visitors"
          selectedStyle={styles.selectedItemStyle}
          renderTitle={isSelected => this._renderTitle('Visitors', isSelected)}
          renderIcon={isSelected => this._renderIcon('md-apps', isSelected)}
          onPress={() => this.props.navigation.getNavigator('tab-navigation').jumpToTab('visitors')}
        />

^ does not. But I don't think I need a <DrawerNavigationItem><StackNavigation> for a route like visitors because it's already a <TabNavigationItem><StackNavigation>. Essentially all my <DrawerNavigation> does is setup the initial, single set of tabs with the first item, then just jumps tabs for the remaining items.

So since I'm just using <DrawerNavigationItem> onPress for things like visitors, I just added the hamburger with a programmatic toggle using custom nav bar?

@withNavigation
class MenuButton extends Component {
  toggleDrawer() {
    const { navigation } = this.props;
    navigation.getNavigator('main').toggleDrawer();
  }

  render() {
    return (
      <Button transparent onPress={() => this.toggleDrawer()}>
        <Icon name="md-menu" style={{ alignSelf: 'flex-start', color: 'white', fontSize: 30, lineHeight: 30 }} />
      </Button>
    );
  }
}

class Visitors extends Component {
  static route = {
    navigationBar: {
      title: 'VISITORS',
      renderLeft: () => <MenuButton />,
    },
  }

Yeah I don't really know what I'm doing but it seems to work. 😝

booboothefool commented 7 years ago

Doesn't work with <DrawerNavigationItem> isSelected, hmm. Similar to the missing hamburger icon issue, it doesn't properly do isSelected if there is no child view. But again, I don't think I need a child view because I have tabs.