okwasniewski / react-native-bottom-tabs

Native Bottom Tabs for React Native
https://okwasniewski.github.io/react-native-bottom-tabs/
MIT License
628 stars 24 forks source link

Support Page Animations / Transitions on Android #100

Open shovel-kun opened 3 weeks ago

shovel-kun commented 3 weeks ago

Problem

By default, BottomNavigationViewshould animate when navigating. The default animations can be found here, and their default durations here.

This GitHub issue showed me how to configure the page animation of BottomNavigationView. Configuring res/animator/in a Native Android app works, but not with react-native-bottom-tabs. Presumably, this is because react-native-screens handles navigation and their animations.

Proposed Solution

The way react-native-screens handles stack animation on navigation can be found here. It's technically native, but they are hand-rolling their own navigation, which is why there are some inconsistencies from native default. Nonetheless, if we want to go the full native approach like SwiftUI UIView (@react-navigation/bottom-tabs uses RN Animated for page animations), I think this is the place to start.

Currently, I'm not sure how react-native-bottom-tabs can tap into react-native-screen's animations. A look into how react-navigation implements Native Stack might be helpful. Though it seems like the API is unstable at the moment.

okwasniewski commented 3 weeks ago

Hey,

Thanks for opening the issue.

By default, BottomNavigationViewshould animate when navigating. The default animations can be found here, and their default durations here.

I'm not sure if this is true. Android views animate when using "stack" navigation, but tabs don't have any animation defined.

For example, in the Clock app in Android 14, they just immediately change. Can you point me to some Material design guidelines that describe this or show a native Android app that does this (without custom logic)?

I'm not saying no to adding animations but it would be good to check what's the actual native behaviour. Also its worth noting that BottomNavigationView is responsible only for rendering the bottom bar, it doesn't encapsulate and manage views as SwiftUI's tab view does.

shovel-kun commented 3 weeks ago

Hi, thanks for asking for clarification. I'm new to Open Source and software development in general, so it really helps to be asked these questions.


Can you point me to some Material design guidelines that describe this

In Navigation bar page > Guidelines > Behavior, the docs state:

When you select a navigation bar item (one that’s not currently selected), the app navigates to that destination’s screen using a top level transition pattern.

Unfortunately, the link they provided is broken lol, but its supposed to link to Top Level header under Transitions Patterns page, which states:

This pattern is used to navigate between top-level destinations of an app, like tapping a destination in a Navigation bar.

Commonly used with: Navigation bar, navigation rail, and navigation drawer Read more: Guidelines, Specs, Android implementation

The exiting screen quickly fades out and then the entering screen fades in. Since the content of top level destinations isn't necessarily related, the motion intentionally does not use grouping or persistent elements to create a strong relationship between screens.

That is what is described by the M3 docs.

or show a native Android app that does this (without custom logic)

You can prove this transition animations are default behavior for BottomNavigationView by creating a native Android app with Bottom Navigation Views Activity preset in Android Studio:

  1. Open Android Studio
  2. Click on New Project (Top Right)
  3. Templates > Phone and Tablet > Bottom Navigation Views Activity
  4. Build and run the project
  5. Notice that FadeIn/FadeOut transition animation plays when navigating between tab bars.

Tested on Android Studio version 2024.2.1, with API 28 and API 35.

With these reasons in mind, I believe that navigating between bottom tabs should play page animations by default.


Also its worth noting that BottomNavigationView is responsible only for rendering the bottom bar, it doesn't encapsulate and manage views as SwiftUI's tab view does.

You're right. I'm not sure what's the best way of handling this.

From what I understand, NavigationBarView has the setupWithNavController method, which wraps around NavigationUI's setupWithNavController. It seems like NavigationUI is responsible for handling navigating as well as animation transition logic, which can be seen in onNavDestinationSelected.

But since it is react-navigation that is actually handling navigation logic, and not NavigationUI, the logic for playing transition animations on navigation is lost, and hence why my earlier method of configuring anims in res/animator/ won't work.

What are your thoughts?

okwasniewski commented 3 weeks ago

Thanks for providing additional details! Indeed when you create a new app from Bottom Navigation Views Activity there is a slight fade animation between views (The spec seems to have a different animation tho 😆) but I think this could be added as a JS animation to views on Android (no need to change native components).

This should be a fairly simple change where we do a small fade animation when the view changes.