natysoz / expo-images-picker

Multiple Asset Photos Videos selecting package for Expo SDK
MIT License
95 stars 35 forks source link
expo images multiple native photos picker react selector videos

expo-images-picker

Multiple Asset Photos | Videos selecting package for Expo SDK 43+. For users who use React native and managed workflow + Styled Components.

MediaLibrary.

Image-manipulator.

styled-components

Test permissions with SDK 47 working without issues.

Best Practice just watch the video or Copy the snack :)

How to Video => https://youtu.be/xcMcVZTw6xA

Copy & Paste => https://snack.expo.dev/@natysoz/expo-images-picker

Features

Sample Sample

Usage

  1. Install with

    $ npm install expo-images-picker

    or

    $ yarn add expo-images-picker

    then

    $ expo install expo-image-manipulator expo-media-library
  2. import to the top of your file like

    import { AssetsSelector } from 'expo-images-picker'
  3. install @expo-vectors package and send icons as props to the widget

    import { Ionicons } from '@expo/vector-icons'
  4. Use the imported as Following =>

    <AssetsSelector
       Settings={widgetSettings}
       Errors={widgetErrors}
       Styles={widgetStyles}
       Resize={widgetResize}       // optional
       Navigator={widgetNavigator} // optional
       CustomNavigator={{          // optional
            Component: CustomNavigator,
            props: {
                backFunction: true,
                onSuccess,
                text: T.ACTIONS.SELECT,
           },
        }}
     />

[📚 Params]

Settings :

you better create this const out of your component , if you do need it inside your component , use useMemo from react.

    const widgetSettings = useMemo(
        () => ({
            getImageMetaData: false,
            initialLoad: 100,
            assetsType: [MediaType.photo, MediaType.video],
            minSelection: 1,
            maxSelection: 3,
            existingSelectionIds: ["<selected Id 1>", "<selected Id 2>", "<selected Id N>"],
            portraitCols: 4,
            landscapeCols: 4,
        }),
        []
    )

Errors :

    const widgetErrors = useMemo(
        () => ({
            errorTextColor: polar_text_2,
            errorMessages: {
                hasErrorWithPermissions: translator(
                    T.ERROR.HAS_PERMISSIONS_ERROR
                ),
                hasErrorWithLoading: translator(T.ERROR.HAS_INTERNAL_ERROR),
                hasErrorWithResizing: translator(T.ERROR.HAS_INTERNAL_ERROR),
                hasNoAssets: translator(T.ERROR.HAS_NO_ASSETS),
            },
        }),
        []
    )

Styles :

    const widgetStyles = useMemo(
        () => ({
            margin: 2,
            bgColor: bg,
            spinnerColor: main,
            widgetWidth: 99,
            screenStyle:{
                borderRadius: 5,
                overflow: "hidden",
            },
            widgetStyle:{
                margin: 10
            },
            videoIcon: {
                Component: Ionicons,
                iconName: 'ios-videocam',
                color: polar_text_1,
                size: 20,
            },
            selectedIcon: {
                Component: Ionicons,
                iconName: 'ios-checkmark-circle-outline',
                color: 'white',
                bg: mainWithOpacity,
                size: 26,
            },
        }),
        [polar_text_1, mainWithOpacity]
    )

Navigator :

    const widgetNavigator = useMemo(
        () => ({
            Texts: {
                finish: 'finish',
                back: 'back',
                selected: 'selected',
            },
            midTextColor: polar_text_2,
            minSelection: 3,
            buttonTextStyle: _textStyle,
            buttonStyle: _buttonStyle,
            onBack: () => navigation.goBack(),
            onSuccess: (data: Asset[]) => onSuccess(data),
        }),
        []
    )

Resize :

    const widgetResize = useMemo(
        () => ({
            width: 512,
            height: 384,
            majorAxis: 512,
            compress: 0.7,
            base64: false,
            saveTo: SaveType.JPG,
        }),
        []
    )

**Note that using manipulate might result with crash or slow loading times on older phones.

CustomNavigator :

Make sure your CustomTopNavigator can receive onSuccess function. And bind this onFinish function on the correct button. This is useful for integrating with React Navigation header.

Example with React Navigation

type CustomNavImageSelectionProps = {
  navigation: CustomNavigationProp
  onSuccess: () => void
  backFunction: boolean
  text: string
};

function CustomNavImageSelection({ navigation, onSuccess, backFunction, text }: CustomNavImageSelectionProps) {
  useEffect(() => {
    navigation.setOptions({
      headerRight: () => <Button title={text} onPress={onSuccess} />,
    })
  }, [navigation, onSuccess, text])

  return null
}

<AssetsSelector
  options={{
    ...otherProps,
    CustomTopNavigator: {
      Component: CustomNavImageSelection,
      props: {
        navigation,
        onSuccess: (data: Asset[]) => {
            onDone(data)
            navigation.goBack()
        },
        backFunction: true,
        text: T.ACTIONS.SELECT
      },
    },
  }}
/>