Draggable React Native Notification Card ⏲️ in 30 Mins

Master creating React Native UI components using ChatGPT

Mikael Ainalem
Stackademic

--

GIF showing dragging a notification card
Draggable notification card

This article is a quick tutorial on how to make a draggable notification card using nothing other than

  1. React Native
  2. ChatGPT

I.e. you don’t need an external library to make effect.

These draggable notification cards are quite handy as they can easily be dimissed with a simple swipe gesture. A prerequisite for the tutorial is to have a RN project up and running. Ok, let’s go!

Step 1️⃣— Set the stage

Create a new component with two views. The idea here is that this container can sit on top of any other content. What we need is two elements. They are:

  1. An absolute positioned full-screen container covering the whole screen
  2. A draggable pane.

Let’s ask ChatGPT to create the React Native elements we need: Can you create me a React Native component with a full screen absolute positioned container and with a full screen, pink (#f0f), pane inside?

Here’s the style that ChatGPT produced, with some slight modifications:

const styles = StyleSheet.create({
container: {
height: '100%',
position: 'absolute',
width: '100%',
},
pane: {
backgroundColor: '#f0f',
flex: 1,
width: '100%',
},
});

And the markup:

<View style={styles.container}>
<View style={styles.pane} />
</View>

And here’s what the result looks thus far:

Step1, create a pink pane
A pink pane

Step 2️⃣— Make it move

To add interaction, let’s again consult ChatGPT: Can you change the pane inside the component to a vertically draggable Animated.View?

Step 2, make the pane draggable
A now draggable pink pane

We basically, at this stage, have the following component:

const NotificationCard = () => {
// ...
const pan = useRef(new Animated.Value(0)).current;

const panResponder = useRef(
PanResponder.create({
onMoveShouldSetPanResponder: () => true,
onPanResponderGrant: _ => {},
onPanResponderMove: (evt, gestureState) => {
Animated.event([null, {dy: pan}], {
useNativeDriver: false,
})(evt, {dy: gestureState.dy});
},
onPanResponderRelease: () => {},
}),
).current;

return (
<View style={styles.container}>
<Animated.View
style={[styles.pane, {transform: [{translateY: pan}]}]}
{...panResponder.panHandlers}
/>
</View>
);
// ...
}

What ChatGPT did here was to insert a panResponder. As the name implies, the code enables panning of the pane. Or in other words, it allows for the pane to be draggable, as the onPanResponderMove callback animates its movement. It’s worth noting that the pane jumps when initiating the drag movement from a position other than the top. This behavior may seem a bit janky, it‘s ok for now. We’ll solve it in the next step.

Step 3️⃣ —Make it snap

Next up is adding a proper release handler in the PanHandler. Hey ChatpGPT: Can you add a threshold value at release that, if the user dragged more than 100 units downwards, animates the pane out of the view and, if not, animates the pane back to it’s original position? For the latter case, please use Animated.spring with the friction 5 to make the card a bit bouncy. ChatGPT gives us the following code:

  // ...
onPanResponderRelease: (_, gestureState) => {
if (gestureState.dy > 100) {
// Dragging exceeds threshold, animate pane out of screen
Animated.timing(pan, {
toValue: SCREEN_HEIGHT,
duration: 300,
useNativeDriver: false,
}).start();
} else {
// Dragging is less than threshold, animate pane back to the start position
Animated.spring(pan, {
toValue: 0,
useNativeDriver: false,
friction: 5,
}).start();
}
},
// ...
Step 3, add a threshold at release to animate the pane to any of its starting positions
Snappy pink pane

Step 4️⃣— Appearance is everything

Next, let’s add an entry animation. ChatGPT: Can you add a entry animation that animates the pane from below the fold, i.e. the complete screen height, to its starting position?

  // ...
const pan = useRef(new Animated.Value(SCREEN_HEIGHT)).current;

useEffect(() => {
Animated.timing(pan, {
toValue: 0,
duration: 500, // Adjust the duration as needed
useNativeDriver: false,
}).start();
}, []);
// ...
Step 4, add an entry animation
Entry animation

Step 5️⃣ — Rubber bands

Essentially, we don’t want the pane to be draggable upwards. However, it would be desirable to implement a rubber-band effect if the user attempts to drag the pane upwards. Let’s skip ChatGPT for this task; it’s quite straightforward if you know the trick to implement rubber-banding. All we need is a square root decay when the user drags upwards. Here’s what it looks in code:

  // ...
onPanResponderMove: (_, gestureState) => {
const newY =
gestureState.dy < 0 ? -Math.sqrt(-gestureState.dy) : gestureState.dy;

Animated.event([null, {dy: pan}], {
useNativeDriver: false,
})(_, {dy: newY});
},
// ...

and in practice

Step 5, add rubber-banding
The pink pane rubber-banding

Step 6️⃣ — The final touches

Now everything set for us to move to the final shape and style of the card that we want.

card: {
backgroundColor: '#113',
borderRadius: 30,
elevation: 5, // Elevation is for Android
height: 200,
padding: 20,
margin: 20,
// Add shadow properties
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
},
title: {
color: 'white',
fontSize: 30,
fontWeight: 'bold',
marginBottom: 20,
marginTop: 10,
},
body: {
color: 'white',
fontSize: 18,
},

And the JSX markup:

  // ...
return (
<View style={styles.container}>
<Animated.View
style={[styles.card, {transform: [{translateY: pan}]}]}
{...panResponder.panHandlers}>
<Text style={styles.title}>Floating notification</Text>
<Text style={styles.body}>Drag me 👆 and 👇</Text>
</Animated.View>
</View>
);

Here’s the final result

Step 6, the whole effect put together
The effect

Next steps

There are of course many ways to go from here. The obvious ones to me are adding a close X in one of the corners to allow users to close the notification and dim the background. Below of adding more elements into the card. Like e.g. a button.

An example of what else to do with the notification card
A notification card with a button

Conclusions

ChatGPT and the PanResponder are quite useful when it comes to create draggable UI elements in React Native.

You can find the code here: https://github.com/ainalem/ReactNativeDraggableNoficiationCard

Good luck with your notification cards!

Stackademic 🎓

Thank you for reading until the end. Before you go:

--

--

Enthusiastic about software & design, father of 3, freelancer and currently CTO at Norban | twitter: https://twitter.com/mikaelainalem