Android Auto and CarPlay library support for ReactNative. This library only supports Android Auto at the moment.
This library is not published to NPM yet. So, you will have to add it like this.
yarn add https://github.com/susonthapa/react-native-vehicle
This library uses a custom reconciler to expose declarative API to the JS side. Here is how you would create a List Template.
const ListTemplate = () => {
const navigation = useCarNavigation()
return (
<list-template title={'List Template'} headerAction={{ actionType: ActionType.BACK }}>
<sectioned-item-list header='Sectioned Item List'>
<item-list>
<row key={1} title='Row One' onPress={navigation.pop} />
</item-list>
<item-list>
<row key={2} title='Row Two' onPress={navigation.pop} />
</item-list>
<item-list>
<row key={3} title='Row Three' onPress={navigation.pop} />
</item-list>
</sectioned-item-list>
</list-template>
);
};
This library exposes ScreenManager
and Screen
components to handle the navigation. If you're familiar with react-navigation
then you should feel right at home.
This is how you define your navigation stack. Note the main component should use the name root
.
const Vehicle = () => {
return (
<ScreenManager>
<Screen name="root" render={GridTemplate} />
<Screen name="navigation-template" render={NavigationTemplate} />
<Screen name="list-template" render={ListTemplate} />
<Screen name="pane-template" render={PaneTemplate} />
<Screen name="place-list-map-template" render={PlaceListMapTemplate} />
</ScreenManager>
);
};
![image](https://private-user-images.githubusercontent.com/33973551/279970732-687eeb5c-20d7-44e7-b245-2f32f4fc77af.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjU3Nzk5OTUsIm5iZiI6MTcyNTc3OTY5NSwicGF0aCI6Ii8zMzk3MzU1MS8yNzk5NzA3MzItNjg3ZWViNWMtMjBkNy00NGU3LWIyNDUtMmYzMmY0ZmM3N2FmLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA5MDglMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwOTA4VDA3MTQ1NVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTNmZjBkMmI4MDM0NDljNzQwYjc1ODg2YzdhYmJhYzc1NGFlYjlkY2I4ZTlkYzUxMTQ4NDQ3MWMxZmMyYjUzZGYmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.vQfGjDVc_u8ib9Z8lQoZoOE51KfCnPQbUATfG_7087s)
const GridTemplate = () => {
const navigation = useCarNavigation()
return (
<grid-template title="Home" headerAction={{ actionType: ActionType.APP_ICON }}>
<item-list>
<grid-item
title="List Template"
image={Image.resolveAssetSource(require('./../images/gear.png'))}
onPress={() => navigation.push('list-template')} />
<grid-item
title="Place List Map Template"
image={Image.resolveAssetSource(require('./../images/gear.png'))}
onPress={() => navigation.push('place-list-map-template')}
/>
<grid-item
title="Pane Template"
image={Image.resolveAssetSource(require('./../images/gear.png'))}
onPress={() => navigation.push('pane-template')}
/>
<grid-item
title="Navigation Template"
image={Image.resolveAssetSource(require('./../images/gear.png'))}
onPress={() => navigation.push('navigation-template')}
/>
</item-list>
</grid-template>
)
}
![image](https://private-user-images.githubusercontent.com/33973551/279971186-697ae5e9-b3d8-4fa8-aa24-966f014d5de6.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjU3Nzk5OTUsIm5iZiI6MTcyNTc3OTY5NSwicGF0aCI6Ii8zMzk3MzU1MS8yNzk5NzExODYtNjk3YWU1ZTktYjNkOC00ZmE4LWFhMjQtOTY2ZjAxNGQ1ZGU2LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA5MDglMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwOTA4VDA3MTQ1NVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTViNDk4YmRhODcyZGMzOWZhYjM5MGQ4Mjk3ZGRhYjUxNTM4ODg0MTVmNWI3MGI2YThiMTJhOTVkOTg5N2MxOGUmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.7J0m77-7TAQyQTM6vqJtmAWiTqrcrIZ6Lkc2b3d087Y)
const ListTemplate = () => {
const navigation = useCarNavigation()
return (
<list-template title={'List Template'} headerAction={{ actionType: ActionType.BACK }}>
<sectioned-item-list header='Sectioned Item List'>
<item-list>
<row key={1} title='Row One' onPress={navigation.pop} />
</item-list>
<item-list>
<row key={2} title='Row Two' onPress={navigation.pop} />
</item-list>
<item-list>
<row key={3} title='Row Three' onPress={navigation.pop} />
</item-list>
</sectioned-item-list>
</list-template>
);
};
![image](https://private-user-images.githubusercontent.com/33973551/279971503-89aa19dd-b0ba-40a7-8ec1-18c7f0609d56.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjU3Nzk5OTUsIm5iZiI6MTcyNTc3OTY5NSwicGF0aCI6Ii8zMzk3MzU1MS8yNzk5NzE1MDMtODlhYTE5ZGQtYjBiYS00MGE3LThlYzEtMThjN2YwNjA5ZDU2LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA5MDglMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwOTA4VDA3MTQ1NVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTBiNTg4YTc4NjRkMTEwNTZmNTY0ZDNhYmU5ZTQxODE5ZjRlNjFkNzFkZjBlM2Q3YzViYTE4ZjQyYTk4MDkzZTkmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.-X6PvyAzdvdXXzgcbZrvrbDuXbl57-snvYI0EQ4FOtw)
const PaneTemplate = () => {
return (
<pane-template title="Pane Template" headerAction={{ actionType: ActionType.BACK }}>
<pane>
<row title="Pane Item One" />
<row title="Pane Item Two" />
<row title="Pane Item Three" />
<row title="Pane Item Four" />
</pane>
</pane-template>
)
}
![image](https://private-user-images.githubusercontent.com/33973551/279971807-e6a81e48-1af8-46b4-b751-868cba837394.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjU3Nzk5OTUsIm5iZiI6MTcyNTc3OTY5NSwicGF0aCI6Ii8zMzk3MzU1MS8yNzk5NzE4MDctZTZhODFlNDgtMWFmOC00NmI0LWI3NTEtODY4Y2JhODM3Mzk0LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA5MDglMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwOTA4VDA3MTQ1NVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWVlNjVhNzU1MTI4MzkwZmVlZDI5MGFlZTE3Yzc0ZGYzNzA2Y2UwNWVkZTMyMGFmMDI0NTBlYTA0YmNhOGQ2MzEmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.WIpaow7Wq5A2UMhCiwxB3BzVcZ5IQw8U-aMeJRTo_zs)
const PlaceListMapTemplate = () => {
return (
<place-list-map-template title='Hello World' headerAction={{ actionType: ActionType.BACK }}>
<item-list>
<row title='This is row One %d' metadata={{
place: {
location: {
lat: 39.3266,
lng: -110.9646
},
marker: {
icon: Image.resolveAssetSource(require('./../images/click.png')),
iconType: MarkerIconType.IMAGE,
}
},
distance: {
displayDistance: 100,
displayUnit: DistanceUnit.UNIT_KILOMETERS,
},
}} onPress={() => { }} />
<row title='This is row Two %d' metadata={{
place: {
location: {
lat: 29.3266,
lng: -110.9646
},
marker: {
icon: Image.resolveAssetSource(require('./../images/click.png')),
iconType: MarkerIconType.IMAGE,
}
},
distance: {
displayDistance: 100,
displayUnit: DistanceUnit.UNIT_KILOMETERS,
},
}} onPress={() => { }} />
</item-list>
</place-list-map-template>
)
}
![image](https://private-user-images.githubusercontent.com/33973551/279972051-2da8aef0-fd7d-486e-8119-27a9a588e370.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjU3Nzk5OTUsIm5iZiI6MTcyNTc3OTY5NSwicGF0aCI6Ii8zMzk3MzU1MS8yNzk5NzIwNTEtMmRhOGFlZjAtZmQ3ZC00ODZlLTgxMTktMjdhOWE1ODhlMzcwLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA5MDglMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwOTA4VDA3MTQ1NVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWRjZWJkNDFhNGM0NmJjNGY5MWM0NmExOTE0OGFlMWNhMGMzNzk5ZDkwMjlhM2NhNjU1MzNmNjI1MDM0MWZkZjcmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.bexOuJL0k6cX83hISweuYPwTsuP2dOh_sSQvf5RNXtU)
Please make sure the template has a proper ID set.
const TestMap = () => {
const [count, setCount] = useState(0)
useEffect(() => {
const interval = setInterval(() => {
setCount((count) => count + 1)
}, 1000)
return () => {
clearInterval(interval)
}
}, [])
return (
<View style={{ width: 100, height: 100, backgroundColor: 'red' }}>
<Text>This is a TestMap {count}</Text>
</View>
)
}
const NavigationTemplate = () => {
const navigation = useCarNavigation()
return (
<navigation-template actionStrip={{
actions: [
{
actionType: ActionType.BACK,
},
{
title: 'Action One',
onPress: () => navigation.push('grid-menu'),
},
{
icon: Image.resolveAssetSource(require('./../images/click.png')),
},
{
icon: Image.resolveAssetSource(require('./../images/click.png')),
},
]
}}
mapActionStrip={{
actions: [
{
icon: Image.resolveAssetSource(require('./../images/click.png')),
},
{
icon: Image.resolveAssetSource(require('./../images/click.png')),
},
{
icon: Image.resolveAssetSource(require('./../images/click.png')),
},
{
icon: Image.resolveAssetSource(require('./../images/click.png')),
},
]
}}
destinationTravelEstimate={{
remainingDistance: {
displayDistance: 10,
displayUnit: 3,
},
destinationTime: {
timeSinceEpochMillis: 1666056013736,
id: 'America/Cayman',
},
remainingTimeSeconds: 60000,
}}
id='test-vehicle'
component={TestMap}
/>
);
};
See the contributing guide to learn how to contribute to the repository and the development workflow.
MIT
Made with create-react-native-library