grahammendick / navigation

Scene-Based Navigation for React and React Native
https://grahammendick.github.io/navigation/
Apache License 2.0
592 stars 41 forks source link

Confusing navigation tree #636

Closed anhtuank7c closed 2 years ago

anhtuank7c commented 2 years ago

Hi,

I'm confusing the way you set up navigation in the twitter sample. For example: scene home and tweet repeated from root stack, then appear one more time in the stack under the Tab.

Could you explain it?

Root Stack Root Stack

Stack under Tabs Home Tabs

anhtuank7c commented 2 years ago

Another question: How do I navigate to a screen from the root stack?

Navigation Tree:

<Stack>
  <Scene>
    <Tabs>
      <Screen_Stack_1 />
      <Screen_Stack_2 /> // How do I navigate to Screen_3 from here? I do want to show a full-screen UI that overlaps the tab
    </Tabs>
  </Scene>

  <Scene>
    <Screen_3 /> // I wanna show this one.
  </Scene>
<Stack>
grahammendick commented 2 years ago

Tabs typically behave quite differently on iOS and Android apps. On iOS, the tabs always stay at the bottom of the UI no matter what scene you're on. On Android, the tabs usually only appear on the very first scene. So when I did the Twitter sample I wanted to show this typical behavior.

So the Home and Tweet scene in the stack under the Tab stack are only for iOS. And the Home and Tweet scenes in the root stack are only for Android. If you wanted the tabs to stay at the bottom of the screen always on Android, too, then you wouldn't need the Home and Tweet scenes in the root stack at all.

How do I navigate to a screen from the root stack

You have to make sure you navigate using the stateNavigator from the root stack. Each stack has its own stateNavigator - you pass it in the prop to the NavigationHandler - so make sure you use the one from the root.

In the Twitter sample, on Android all the navigation happens in the root stack because there is only one stack. But if you wanted to place a tweet scene over the top of the tabs on iOS then you would call the following from the App component

stateNavigator.navigate('tweet', {id: 1})

I hope that helps, but let me know if it doesn't đź‘Ť

anhtuank7c commented 2 years ago

Thank you for your reply. I tried to use the stateNavigator from the Root Stack, passing it down to Tabs > SubStack to trigger navigating to another scene in the Root Stack but it just does nothing. No action, no crash, nothing happened.

Can I hide the Tabbar from the SubStack? Can I override the Tabbar to customize it? Screen Shot 2022-09-03 at 07 26 02

grahammendick commented 2 years ago

It's not a good idea to pass stateNavigators from scene to scene. Always use the one from the NavigationContext. Here's how I'd navigate to a tweet in the root stack in the Twitter sample on iOS. In the Tabs scene get a hold of the state navigator (the root one)

const {stateNavigator} = useContext(NavigationContext);

The pass a goToTweet function prop to the Home scene, for example

<NavigationStack>
  <Scene stateKey="home">
    <Home goToTweet={(id) => stateNavigator.navigate('tweet', {id})} />
  </Scene>

Then when the user presses the tweet call goToTweet. In this video you can see that the navigation happens in the root stack because the tabs aren't visible on the tweet scene anymore

https://user-images.githubusercontent.com/1761227/188262181-72bf3ba4-9019-4011-95ad-c8f2a3590b30.mov

But there's a better way to do what you're trying to do. On iOS, you can hide the tab bar on particular scenes. So you can still navigate within the tab stack but hide the tab bar. For example, if I want to hide the tab bar on the tweet scene I can use the hidesTabBar prop of the NavigationStack. The prop takes a function and you return true from the function for the scenes you want to the hide the tab bar on,

<NavigationHandler stateNavigator={homeNavigator}>
  <NavigationStack hidesTabBar={(state) => state.key === 'tweet'}>
    <Scene stateKey="home"><Home /></Scene>
    <Scene stateKey="tweet"><Tweet /></Scene>
    <Scene stateKey="timeline"><Timeline /></Scene>
  </NavigationStack>
</NavigationHandler>

Here's a vid of what that navigation looks like. I've not changed anything else, just added the hidesTabBar prop. So all the navigation is happening within the tab stack and the tab bar disappears on the tweet scene. The native iOS platform handles making the tabs disappear and reappear smoothly as you navigate back and forward.

https://user-images.githubusercontent.com/1761227/188262515-6860da33-5321-4aad-9820-7e4e6c746aea.mov

anhtuank7c commented 2 years ago

Thank you so much @grahammendick Regarding customizing Tabbar, is there any way to do it?

grahammendick commented 2 years ago

Cool, I'm happy to help.

The aim of the Navigation router is to provide the same out-of-the-box experience you'd get if you were building a native app without React Native. For example, the TabBar component renders to a UITabBarController from UIKit on iOS and to the BottomNavigationView from Android Material Components. So the Navigation router only supports the props that those native components provide. If you want a customised tab bar then you’ll have to build it yourself or use a third party library.

grahammendick commented 2 years ago

Thanks for getting in touch! I noticed you đź‘Ť my answer so are you happy to close this?

anhtuank7c commented 2 years ago

Hi @grahammendick Sorry, I don't have any response during the weekend. I really want to find out how to customize the Tab Bar. Could you please help to make it in the Twitter sample? Let's say we have 2 tabs for home and notification, and one more add button like this.

image

grahammendick commented 2 years ago

I encourage you to dig a bit into the different design systems you’d use if you were building a native app without React Native. On IOS you use UIKit and on Android you use Material Components. These provide a completely different UX. The Navigation router renders to these 2 different design systems using a single component. So you end up with apps that look and feel like real native apps.

Your image of a customized tab bar is actually an image of a Floating Action Button anchored to a bottom App Bar. The Navigation router supports this on Android (see Cradling a Floating Action Button). But you can’t anchor a Floating Action Button to the TabBar because it isn’t part of Android’s Material Components.

If you want a customized TabBar that looks the same on Android and iOS then you’re going to have to build it yourself or use a third-party library.

burakgormek commented 2 years ago

Actually it can be done with bottomBar props on NavigationBar. But not sure the rest of it(buttons, margin etc).

<CoordinatorLayout>
  <Tweets tweets={getHome()} />
  <NavigationBar title="Home" bottomBar={true} barTintColor="#fff" />
  <FloatingActionButton
    anchor={'bottomNavigationBar'}
    image={require('./notifications.png')}
    gravity="bottom"
  />
</CoordinatorLayout>
image
anhtuank7c commented 2 years ago

Thank you so much @grahammendick @burakgormek