Skip to content

React Native Examples ​

Complete examples for using Flowfull Clients with React Native (Expo) applications.

Architecture

  • @pubflow/react-native - For authentication with Flowless (managed auth service)
  • @pubflow/flowfull-client - For HTTP requests to your custom Flowfull backend

Installation ​

bash
# Install both packages
npm install @pubflow/react-native @pubflow/flowfull-client

# Install required peer dependencies
npx expo install @react-native-async-storage/async-storage

Basic Setup ​

1. Configure PubflowProvider ​

typescript
// App.tsx
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { PubflowProvider } from '@pubflow/react-native';
import LoginScreen from './src/screens/LoginScreen';
import DashboardScreen from './src/screens/DashboardScreen';
import UsersScreen from './src/screens/UsersScreen';

const Stack = createNativeStackNavigator();

export default function App() {
  return (
    <PubflowProvider
      config={{
        baseUrl: process.env.EXPO_PUBLIC_FLOWLESS_URL || 'https://your-flowless.pubflow.com',
        bridgeBasePath: '/bridge',
        authBasePath: '/auth'
      }}
      showSessionAlerts={true}
    >
      <NavigationContainer>
        <Stack.Navigator initialRouteName="Login">
          <Stack.Screen name="Login" component={LoginScreen} options={{ headerShown: false }} />
          <Stack.Screen name="Dashboard" component={DashboardScreen} />
          <Stack.Screen name="Users" component={UsersScreen} />
        </Stack.Navigator>
      </NavigationContainer>
    </PubflowProvider>
  );
}

2. Create Flowfull API Client ​

typescript
// src/api/flowfull.ts
import { createFlowfull } from '@pubflow/flowfull-client';

// Create client for your custom backend
export const flowfull = createFlowfull(
  process.env.EXPO_PUBLIC_BACKEND_URL || 'https://api.myapp.com',
  {
    headers: {
      'X-Client-Version': '1.0.0',
      'X-Platform': 'mobile'
    },
    retryAttempts: 3,
    timeout: 30000
  }
);

Authentication with Flowless ​

Login Screen ​

typescript
// src/screens/LoginScreen.tsx
import { useState } from 'react';
import { View, TextInput, Button, Text, StyleSheet, Alert, ActivityIndicator } from 'react-native';
import { useAuth } from '@pubflow/react-native';

export default function LoginScreen({ navigation }: any) {
  const { login, isLoading } = useAuth();
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  async function handleLogin() {
    if (!email || !password) {
      Alert.alert('Error', 'Please fill in all fields');
      return;
    }

    try {
      const result = await login({ email: email.toLowerCase(), password });

      if (result.success) {
        navigation.replace('Dashboard');
      } else {
        Alert.alert('Error', result.error || 'Login failed');
      }
    } catch (error: any) {
      Alert.alert('Error', error.message || 'An error occurred');
    }
  }

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Login</Text>
      
      <TextInput
        style={styles.input}
        placeholder="Email"
        value={email}
        onChangeText={setEmail}
        autoCapitalize="none"
        keyboardType="email-address"
      />
      
      <TextInput
        style={styles.input}
        placeholder="Password"
        value={password}
        onChangeText={setPassword}
        secureTextEntry
      />
      
      {isLoading ? (
        <ActivityIndicator size="large" color="#0000ff" />
      ) : (
        <Button title="Login" onPress={handleLogin} />
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    padding: 20,
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 20,
    textAlign: 'center',
  },
  input: {
    borderWidth: 1,
    borderColor: '#ddd',
    padding: 10,
    marginBottom: 10,
    borderRadius: 5,
  },
});

Dashboard Screen ​

typescript
// src/screens/DashboardScreen.tsx
import { View, Text, Button, StyleSheet, ActivityIndicator } from 'react-native';
import { useAuth } from '@pubflow/react-native';

export default function DashboardScreen({ navigation }: any) {
  const { user, isAuthenticated, logout, isLoading } = useAuth();

  async function handleLogout() {
    await logout();
    navigation.replace('Login');
  }

  if (isLoading) {
    return (
      <View style={styles.container}>
        <ActivityIndicator size="large" color="#0000ff" />
      </View>
    );
  }

  if (!isAuthenticated) {
    navigation.replace('Login');
    return null;
  }

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Dashboard</Text>
      <View style={styles.userInfo}>
        <Text><Text style={styles.label}>Name:</Text> {user?.name}</Text>
        <Text><Text style={styles.label}>Email:</Text> {user?.email}</Text>
        <Text><Text style={styles.label}>User Type:</Text> {user?.userType}</Text>
      </View>
      <Button title="Logout" onPress={handleLogout} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 20,
  },
  userInfo: {
    marginBottom: 20,
  },
  label: {
    fontWeight: 'bold',
  },
});

Data Fetching from Custom Backend ​

Use @pubflow/flowfull-client to fetch data from your custom Flowfull backend:

User List Screen ​

typescript
// src/screens/UsersScreen.tsx
import { useState, useEffect } from 'react';
import { View, Text, FlatList, StyleSheet, ActivityIndicator } from 'react-native';
import { flowfull } from '../api/flowfull';

interface User {
  id: string;
  name: string;
  email: string;
  role: string;
}

export default function UsersScreen() {
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState('');

  useEffect(() => {
    fetchUsers();
  }, []);

  async function fetchUsers() {
    try {
      const data = await flowfull.get<User[]>('/users');
      setUsers(data);
    } catch (err: any) {
      setError(err.message || 'Failed to fetch users');
    } finally {
      setLoading(false);
    }
  }

  if (loading) {
    return (
      <View style={styles.center}>
        <ActivityIndicator size="large" color="#0000ff" />
      </View>
    );
  }

  if (error) {
    return (
      <View style={styles.center}>
        <Text style={styles.error}>{error}</Text>
      </View>
    );
  }

  return (
    <View style={styles.container}>
      <FlatList
        data={users}
        keyExtractor={(item) => item.id}
        renderItem={({ item }) => (
          <View style={styles.userItem}>
            <Text style={styles.userName}>{item.name}</Text>
            <Text style={styles.userEmail}>{item.email}</Text>
            <Text style={styles.userRole}>{item.role}</Text>
          </View>
        )}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
  },
  center: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  error: {
    color: 'red',
    fontSize: 16,
  },
  userItem: {
    padding: 15,
    borderBottomWidth: 1,
    borderBottomColor: '#ddd',
  },
  userName: {
    fontSize: 18,
    fontWeight: 'bold',
  },
  userEmail: {
    fontSize: 14,
    color: '#666',
  },
  userRole: {
    fontSize: 12,
    color: '#999',
    marginTop: 5,
  },
});

Next Steps ​