Komponenty w React Native
Komponenty w React to podstawowe elementy, z których buduje się aplikacje. Każdy komponent ma swoją logikę i wygląd, dzięki czemu można łatwo go tworzyć, modyfikować i używać wielokrotnie w różnych miejscach aplikacji. Mogą być proste (np. wyświetlające tekst) albo złożone (np. zawierające wiele innych komponentów). Dzięki komponentom kod jest bardziej uporządkowany i łatwiej go zrozumieć i rozwijać. W poniższym artykule przedstawiamy podstawowe komponenty i API dostępne w React Native, które umożliwiają deweloperom szybkie i efektywne tworzenie interfejsów użytkownika w aplikacjach mobilnych.
React Native oferuje zestaw tzw. Core Components, które stanowią podstawowe elementy aplikacji mobilnych. Komponenty te są zorganizowane w różne kategorie, takie jak: podstawowe komponenty, komponenty UI, widoki list, komponenty specyficzne dla Androida i iOS.
1. Najważniejsze podstawowe komponenty w React Native
View
Służy jako kontener dla innych komponentów. Można go używać do grupowania komponentów i tworzenia struktur layoutu, przykład:
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
const App = () => {
return (
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Hello from inside the View!</Text>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
box: {
width: 150,
height: 150,
backgroundColor: 'lightblue',
justifyContent: 'center',
alignItems: 'center',
},
text: {
color: 'white',
},
});
export default App;
Text
Komponent Text jest używany do wyświetlania tekstu. Może być stylizowany za pomocą StyleSheet
, poniżej przykład:
import React from 'react';
import { Text, View, StyleSheet } from 'react-native';
const App = () => {
return (
<View style={styles.container}>
<Text style={styles.text}>Hello, this is a Text component!</Text>
<Text style={styles.boldText}>Another Text component with bold styling.</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
text: {
fontSize: 20,
},
boldText: {
fontSize: 20,
fontWeight: 'bold',
},
});
export default App;
Image
Komponent Image jest używany do wyświetlania obrazów w aplikacji.
import React from 'react';
import { Image, View, StyleSheet } from 'react-native';
const App = () => {
return (
<View style={styles.container}>
<Image
source={{ uri: 'https://reactnative.dev/img/tiny_logo.png' }}
style={styles.image}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
image: {
width: 100,
height: 100,
},
});
export default App;
TextInput
Komponent TextInput służy do umożliwienia użytkownikowi wpisywania tekstu.
import React, { useState } from 'react';
import { TextInput, View, Text, StyleSheet } from 'react-native';
const App = () => {
const [text, setText] = useState('');
return (
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder="Enter some text"
value={text}
onChangeText={(newText) => setText(newText)}
/>
<Text>You typed: {text}</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
input: {
height: 40,
borderColor: 'gray',
borderWidth: 1,
padding: 10,
width: '80%',
marginBottom: 20,
},
});
export default App;
Scrollview
Komponent ScrollView umożliwia przewijanie zawartości, zwłaszcza jeśli jest jej więcej niż może pomieścić ekran.
import React from 'react';
import { ScrollView, View, Text, StyleSheet } from 'react-native';
const App = () => {
return (
<ScrollView style={styles.scrollView}>
{Array.from({ length: 20 }, (_, i) => (
<View key={i} style={styles.item}>
<Text>Item {i + 1}</Text>
</View>
))}
</ScrollView>
);
};
const styles = StyleSheet.create({
scrollView: {
marginVertical: 20,
},
item: {
padding: 20,
marginVertical: 10,
backgroundColor: 'lightgray',
alignItems: 'center',
},
});
export default App;
StyleSheet
Komponent StyleSheet jest używany do definiowania stylów w React Native, które można następnie przypisać do komponentów.
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
const App = () => {
return (
<View style={styles.container}>
<Text style={styles.title}>Hello, StyleSheet Example!</Text>
<Text style={styles.subtitle}>This is how you define reusable styles in React Native.</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 16,
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 10,
},
subtitle: {
fontSize: 16,
color: 'gray',
},
});
export default App;
Komponenty UI
W poniższej sekcji przedstawiamy kilka istotnych komponentów UI, które są dostępne w React Native i mogą być używane do budowy interaktywnych i estetycznych interfejsów użytkownika.
CustomButton
Przycisk, który użytkownik może nacisnąć, aby wywołać określoną akcję (np. zapisać dane, przejść do kolejnego ekranu itp.). Poniżej przykład takiego komponentu:
import React from 'react';
import { TouchableOpacity, Text, StyleSheet } from 'react-native';
// CustomButton - Przycisk UI
const CustomButton = ({ title, onPress }) => {
return (
<TouchableOpacity style={styles.button} onPress={onPress}>
<Text style={styles.buttonText}>{title}</Text>
</TouchableOpacity>
);
};
// Stylizacje przycisku
const styles = StyleSheet.create({
button: {
backgroundColor: '#4CAF50',
padding: 15,
borderRadius: 8,
alignItems: 'center',
marginVertical: 10,
},
buttonText: {
color: '#FFFFFF',
fontSize: 16,
fontWeight: 'bold',
},
});
export default CustomButton;
// Przykład użycia komponentu CustomButton
// import CustomButton from './CustomButton';
//
// const App = () => {
// return (
// <CustomButton
// title="Kliknij mnie!"
// onPress={() => alert('Przycisk został kliknięty!')}
// />
// );
// };
Card
Card to komponent wyświetlający informacje w czytelny i atrakcyjny wizualnie sposób. Najczęściej składa się z tytułu, opisu, a czasem także dodatkowych elementów, jak obrazek czy przyciski. Używana jest np. do prezentacji pojedynczego produktu, artykułu, czy elementu listy.
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
// Card - Karta UI
const Card = ({ title, description }) => {
return (
<View style={styles.card}>
<Text style={styles.cardTitle}>{title}</Text>
<Text style={styles.cardDescription}>{description}</Text>
</View>
);
};
// Stylizacje komponentu Card
const styles = StyleSheet.create({
card: {
backgroundColor: '#FFFFFF',
padding: 15,
borderRadius: 10,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.8,
shadowRadius: 2,
elevation: 5,
marginVertical: 10,
},
cardTitle: {
fontSize: 18,
fontWeight: 'bold',
},
cardDescription: {
fontSize: 14,
color: '#777777',
marginTop: 5,
},
});
export default Card;
// Przykład użycia komponentu Card
// import Card from './Card';
//
// const App = () => {
// return (
// <Card
// title="Tytuł Karty"
// description="To jest przykładowy opis dla tej karty."
// />
// );
// };
Modal
Komponent Modal służy do wyświetlania zawartości, która przykuwa uwagę użytkownika, np. komunikatów czy formularzy. Modal zazwyczaj wyświetla dodatkowe informacje na tle aktualnego widoku i wymaga interakcji ze strony użytkownika, aby kontynuować.
import React, { useState } from 'react';
import { View, Text, Modal, TouchableOpacity, StyleSheet } from 'react-native';
// CustomModal - Komponent Modal
const CustomModal = ({ visible, onClose, title, content }) => {
return (
<Modal
transparent={true}
animationType="slide"
visible={visible}
onRequestClose={onClose}
>
<View style={styles.modalOverlay}>
<View style={styles.modalContent}>
<Text style={styles.modalTitle}>{title}</Text>
<Text style={styles.modalText}>{content}</Text>
<TouchableOpacity style={styles.closeButton} onPress={onClose}>
<Text style={styles.closeButtonText}>Zamknij</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
);
};
// Stylizacje komponentu Modal
const styles = StyleSheet.create({
modalOverlay: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0, 0, 0, 0.5)',
},
modalContent: {
width: 300,
backgroundColor: '#FFF',
padding: 20,
borderRadius: 10,
alignItems: 'center',
},
modalTitle: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 10,
},
modalText: {
fontSize: 16,
color: '#777',
textAlign: 'center',
marginBottom: 20,
},
closeButton: {
backgroundColor: '#4CAF50',
paddingVertical: 10,
paddingHorizontal: 20,
borderRadius: 5,
},
closeButtonText: {
color: '#FFF',
fontSize: 16,
},
});
// Przykład użycia komponentu CustomModal
const App = () => {
const [isModalVisible, setIsModalVisible] = useState(false);
const toggleModal = () => {
setIsModalVisible(!isModalVisible);
};
return (
<View style={styles.appContainer}>
<TouchableOpacity style={styles.openButton} onPress={toggleModal}>
<Text style={styles.openButtonText}>Otwórz Modal</Text>
</TouchableOpacity>
<CustomModal
visible={isModalVisible}
onClose={toggleModal}
title="Przykładowy Modal"
content="To jest przykładowa treść modala. Możesz zamknąć modal, naciskając przycisk poniżej."
/>
</View>
);
};
// Stylizacje dla przykładu użycia
const stylesApp = StyleSheet.create({
appContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
openButton: {
backgroundColor: '#4CAF50',
padding: 15,
borderRadius: 8,
},
openButtonText: {
color: '#FFFFFF',
fontSize: 16,
fontWeight: 'bold',
},
});
export default App;
Switch
Switch to natywny komponent React Native, który służy do renderowania przełącznika logicznego, reprezentującego wartość typu boolean
(true
lub false
). Zazwyczaj jest używany do włączania lub wyłączania jakiejś opcji.
Poniżej znajdziesz przykładowy kod dla komponentu Switch:
import React, { useState } from 'react';
import { View, Text, Switch, StyleSheet } from 'react-native';
// Komponent CustomSwitch
const CustomSwitch = ({ label, value, onValueChange }) => {
return (
<View style={styles.switchContainer}>
<Text style={styles.switchLabel}>{label}</Text>
<Switch
value={value} // Kontrolowane przez stan rodzica
onValueChange={onValueChange} // Callback, który zmienia stan
thumbColor={value ? '#4CAF50' : '#f4f3f4'}
trackColor={{ false: '#767577', true: '#81b0ff' }}
/>
</View>
);
};
// Przykład użycia komponentu CustomSwitch
const App = () => {
// Zarządzanie stanem przełącznika - kontrolowanie wartości `value` Switcha
const [isEnabled, setIsEnabled] = useState(false);
// Funkcja callback do zmiany stanu
const toggleSwitch = () => setIsEnabled(previousState => !previousState);
return (
<View style={stylesApp.container}>
<CustomSwitch
label="Włącz Tryb Ciemny"
value={isEnabled} // Wartość przełącznika, przekazywana jako prop
onValueChange={toggleSwitch} // Callback, który zmienia stan, aby `Switch` zaktualizował wartość
/>
</View>
);
};
// Stylizacje komponentów
const styles = StyleSheet.create({
switchContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginVertical: 10,
paddingHorizontal: 20,
},
switchLabel: {
fontSize: 16,
color: '#333',
},
});
const stylesApp = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
});
export default App;
Komponenty dla Androida w React Native
React Native oferuje różne narzędzia specyficzne dla systemu Android, które pozwalają na bardziej natywne doświadczenie użytkownika oraz integrację z funkcjami systemu operacyjnego. Poniżej przedstawiamy komponenty i API, które są specyficzne dla Androida, co oznacza, że pomagają budować aplikacje dedykowane tej platformie.
BackHandler
Służy do wykrywania naciśnięć przycisku „wstecz” na urządzeniu z Androidem. Jest używany, aby kontrolować, co się dzieje, gdy użytkownik próbuje wyjść z aplikacji lub wrócić do poprzedniego ekranu.
Przykład kodu komponentu BackHandler:
import React, { useEffect } from 'react';
import { View, Text, StyleSheet, BackHandler, Alert } from 'react-native';
// BackHandler Example
const BackHandlerExample = () => {
useEffect(() => {
// Funkcja obsługująca naciśnięcie przycisku "wstecz"
const backAction = () => {
Alert.alert("Uwaga!", "Czy na pewno chcesz wyjść?", [
{
text: "Nie",
onPress: () => null,
style: "cancel",
},
{ text: "Tak", onPress: () => BackHandler.exitApp() },
]);
return true; // Zatrzymuje domyślne działanie przycisku "wstecz"
};
// Dodanie listenera do naciśnięcia przycisku "wstecz"
const backHandler = BackHandler.addEventListener(
"hardwareBackPress",
backAction
);
// Czyszczenie listenera przy odmontowaniu komponentu
return () => backHandler.remove();
}, []);
return (
<View style={styles.container}>
<Text style={styles.infoText}>
Naciśnij przycisk "wstecz", aby wyświetlić komunikat.
</Text>
</View>
);
};
// Stylizacje komponentu BackHandlerExample
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
infoText: {
fontSize: 16,
textAlign: 'center',
marginHorizontal: 20,
},
});
export default BackHandlerExample;
DrawerLayoutAndroid
Komponent, który pozwala na renderowanie widoku szuflady bocznej (Drawer Layout) na Androidzie. Drawer jest wysuwanym menu, które zwykle zawiera opcje nawigacji. Używany jest często do implementacji menu nawigacyjnego aplikacji. DrawerLayoutAndroid jest specyficzny dla Androida, ale podobne efekty można uzyskać przy użyciu bibliotek multiplatformowych jak React Navigation.
Przykład:
import React, { useRef } from 'react';
import { View, Text, StyleSheet, DrawerLayoutAndroid, TouchableOpacity } from 'react-native';
const DrawerLayoutExample = () => {
// Użycie hooka useRef, aby uzyskać referencję do DrawerLayoutAndroid
const drawer = useRef(null);
// Widok zawartości szuflady
const renderDrawerContent = () => (
<View style={styles.drawerContainer}>
<Text style={styles.drawerHeader}>Menu Nawigacyjne</Text>
<TouchableOpacity style={styles.drawerItem} onPress={() => drawer.current.closeDrawer()}>
<Text style={styles.drawerItemText}>Strona Główna</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.drawerItem} onPress={() => drawer.current.closeDrawer()}>
<Text style={styles.drawerItemText}>Profil</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.drawerItem} onPress={() => drawer.current.closeDrawer()}>
<Text style={styles.drawerItemText}>Ustawienia</Text>
</TouchableOpacity>
</View>
);
return (
<DrawerLayoutAndroid
ref={drawer}
drawerWidth={250}
drawerPosition="left"
renderNavigationView={renderDrawerContent}
>
<View style={styles.container}>
<TouchableOpacity onPress={() => drawer.current.openDrawer()} style={styles.openDrawerButton}>
<Text style={styles.openDrawerText}>Otwórz Menu</Text>
</TouchableOpacity>
<Text style={styles.mainText}>To jest główny widok aplikacji.</Text>
</View>
</DrawerLayoutAndroid>
);
};
// Stylizacje komponentów
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
openDrawerButton: {
backgroundColor: '#4CAF50',
padding: 15,
borderRadius: 8,
marginBottom: 20,
},
openDrawerText: {
color: '#FFFFFF',
fontSize: 16,
fontWeight: 'bold',
},
mainText: {
fontSize: 18,
textAlign: 'center',
color: '#333',
},
drawerContainer: {
flex: 1,
backgroundColor: '#FFF',
padding: 16,
},
drawerHeader: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 20,
},
drawerItem: {
marginVertical: 10,
},
drawerItemText: {
fontSize: 16,
color: '#000',
},
});
export default DrawerLayoutExample;
PermissionsAndroid
Komponent PermissionsAndroid pozwala sprawdzać i prosić użytkowników o dostęp do zasobów urządzenia, takich jak aparat, lokalizacja czy pamięć. Jest to kluczowe, aby zapewnić aplikacji odpowiednie uprawnienia do działania na Androidzie.
Przykład:
import React, { useState } from 'react';
import { View, Text, TouchableOpacity, StyleSheet, PermissionsAndroid, Alert } from 'react-native';
const PermissionsExample = () => {
const [locationPermission, setLocationPermission] = useState(false);
// Funkcja do proszenia użytkownika o uprawnienia lokalizacji
const requestLocationPermission = async () => {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
title: 'Uprawnienia Lokalizacji',
message:
'Aplikacja potrzebuje dostępu do Twojej lokalizacji, aby świadczyć lepsze usługi.',
buttonNeutral: 'Zapytaj później',
buttonNegative: 'Anuluj',
buttonPositive: 'OK',
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
setLocationPermission(true);
Alert.alert("Uprawnienia przyznane", "Masz teraz dostęp do lokalizacji.");
} else {
setLocationPermission(false);
Alert.alert("Uprawnienia odrzucone", "Nie przyznano dostępu do lokalizacji.");
}
} catch (err) {
console.warn(err);
}
};
return (
<View style={styles.container}>
<Text style={styles.infoText}>
{locationPermission
? "Uprawnienia lokalizacji są przyznane."
: "Uprawnienia lokalizacji nie są przyznane."}
</Text>
<TouchableOpacity style={styles.permissionButton} onPress={requestLocationPermission}>
<Text style={styles.buttonText}>Poproś o dostęp do lokalizacji</Text>
</TouchableOpacity>
</View>
);
};
// Stylizacje komponentu PermissionsExample
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
padding: 20,
},
infoText: {
fontSize: 18,
textAlign: 'center',
marginBottom: 20,
},
permissionButton: {
backgroundColor: '#4CAF50',
padding: 15,
borderRadius: 8,
},
buttonText: {
color: '#FFFFFF',
fontSize: 16,
fontWeight: 'bold',
},
});
export default PermissionsExample;
ToastAndroid
Komponent ToastAndroid umożliwia tworzenie natywnych powiadomień typu „Toast” w Androidzie. Toast to małe, nieinwazyjne powiadomienie, które pojawia się na krótki czas na dole ekranu i automatycznie znika.
import React from 'react';
import { View, Text, TouchableOpacity, StyleSheet, ToastAndroid } from 'react-native';
const ToastExample = () => {
// Funkcja do wyświetlania Toast
const showToast = () => {
ToastAndroid.show("To jest wiadomość Toast", ToastAndroid.SHORT);
};
const showToastWithGravity = () => {
ToastAndroid.showWithGravity(
"To jest Toast z grawitacją!",
ToastAndroid.LONG,
ToastAndroid.CENTER
);
};
const showToastWithGravityAndOffset = () => {
ToastAndroid.showWithGravityAndOffset(
"Toast z grawitacją i przesunięciem!",
ToastAndroid.LONG,
ToastAndroid.BOTTOM,
0, // Przesunięcie w poziomie
50 // Przesunięcie w pionie
);
};
return (
<View style={styles.container}>
<Text style={styles.header}>Przykłady ToastAndroid</Text>
<TouchableOpacity style={styles.button} onPress={showToast}>
<Text style={styles.buttonText}>Pokaż Toast (krótki)</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={showToastWithGravity}>
<Text style={styles.buttonText}>Pokaż Toast z grawitacją</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={showToastWithGravityAndOffset}>
<Text style={styles.buttonText}>Pokaż Toast z przesunięciem</Text>
</TouchableOpacity>
</View>
);
};
// Stylizacje komponentu ToastExample
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
header: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 20,
},
button: {
backgroundColor: '#4CAF50',
padding: 15,
borderRadius: 8,
marginVertical: 10,
},
buttonText: {
color: '#FFFFFF',
fontSize: 16,
fontWeight: 'bold',
},
});
export default ToastExample;
Komponenty specyficzne dla iOS w React Native
Komponenty specyficzne dla iOS w React Native służą do zapewnienia bardziej natywnej integracji z interfejsem użytkownika tego systemu. Wraz z rozwojem React Native, niektóre komponenty dedykowane iOS zostały uznane za przestarzałe, a w ich miejsce pojawiły się komponenty wieloplatformowe, np.: ActionSheetIOS.
ActionSheetIOS.
ActionSheetIOS to komponent API używany do wyświetlania akcji do wyboru użytkownikowi. Działa w formie wysuwanego arkusza z dołu ekranu, zawierającego listę opcji, które użytkownik może wybrać, np. „Anuluj” lub „Usuń”. Może być wykorzystywany do oferowania użytkownikowi wyboru z kilku opcji. Typowym przypadkiem użycia jest pytanie użytkownika o to, co chce zrobić z danym elementem.
Poniżej przykład komponentu ActionSheetIOS:
import React from 'react';
import { View, Text, TouchableOpacity, StyleSheet, ActionSheetIOS } from 'react-native';
const ActionSheetExample = () => {
// Funkcja otwierająca ActionSheet
const showActionSheet = () => {
ActionSheetIOS.showActionSheetWithOptions(
{
options: ["Anuluj", "Opcja 1", "Opcja 2", "Usuń"],
destructiveButtonIndex: 3, // Indeks przycisku destruktywnego
cancelButtonIndex: 0, // Indeks przycisku anulowania
title: "Wybierz opcję",
message: "Wybierz jedną z dostępnych opcji poniżej:",
},
(buttonIndex) => {
// Obsługa wyboru przycisku przez użytkownika
if (buttonIndex === 0) {
console.log("Użytkownik anulował akcję");
} else if (buttonIndex === 1) {
console.log("Wybrano Opcję 1");
} else if (buttonIndex === 2) {
console.log("Wybrano Opcję 2");
} else if (buttonIndex === 3) {
console.log("Wybrano Usuń");
}
}
);
};
return (
<View style={styles.container}>
<Text style={styles.header}>Przykład użycia ActionSheetIOS</Text>
<TouchableOpacity style={styles.button} onPress={showActionSheet}>
<Text style={styles.buttonText}>Pokaż Arkusz Akcji</Text>
</TouchableOpacity>
</View>
);
};
// Stylizacje komponentu ActionSheetExample
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
padding: 20,
},
header: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 20,
},
button: {
backgroundColor: '#4CAF50',
padding: 15,
borderRadius: 8,
},
buttonText: {
color: '#FFFFFF',
fontSize: 16,
fontWeight: 'bold',
},
});
export default ActionSheetExample;