filmkit_picker 0.2.0 copy "filmkit_picker: ^0.2.0" to clipboard
filmkit_picker: ^0.2.0 copied to clipboard

Instagram-style photo and video picker for Flutter: gallery grid, albums, single or multiple selection with a crop preview, then filmkit's editor.

filmkit_picker #

Instagram-style photo and video picker for Flutter: a crop preview over the gallery grid, albums, single or multiple selection, then filmkit's editor on each picked media.

Status: 0.2. Tested on the Android emulator and the iOS simulator.

Picker Multiple selection Editing in turn A video in the editor

Photos from Wikimedia Commons, CC0: beach at sunset, mountain lake, flower field, Chain Bridge at night, coffee, forest path, cat, waterfall (the video), dunes, harbor, autumn leaves, cake, snowy mountains, lighthouse, bicycle, Château Frontenac.

Pick and edit #

final results = await FilmkitPicker.pickAndEdit(
  context,
  options: const PickerOptions(maxCount: 10),
);
if (results != null) {
  for (final result in results) {
    print(result.export!.path); // the exported JPEG or MP4
  }
}
  • The top of the screen previews the selected media, playing videos muted. Drag and pinch it to choose the crop. The ratio button goes through PickerOptions.aspects (1:1, 4:5 and the media's own ratio by default). The ratio is shared by all the picked media; each one keeps its own crop.
  • With maxCount above 1, a button turns on multiple selection. The media are numbered in the order they were picked. Tapping the previewed media again deselects it.
  • pickAndEdit opens the editor on each media in turn, titled "1/3", "2/3"…, with Next until the last one. Closing an editor goes back to the previous media with its edits, or to the picker from the first one. The results come back in the order of selection. Pass editorOptions: for the editor's looks, ratios, export size, etc.
  • With several media, each one is exported when the user moves on to the next, to a new temporary file (EditorOptions.outputPath is only used for a single media). Exports that are redone or abandoned are deleted.

Pick only #

final media = await FilmkitPicker.pick(context, options: const PickerOptions(type: PickerMediaType.photos));
for (final m in media ?? []) {
  print('${m.path}: ${m.aspect.label}, ${m.cropRect}');
  // Later: FilmkitEditor.open(context, path: m.path, initialState: m.editorState)
}

PickedMedia has:

  • path: the file path.
  • item: the library item. Its source is the photo_manager AssetEntity.
  • crop, and cropRect, normalized as EditSpec.crop.
  • editorState: opens filmkit's editor with that crop.

Camera #

FilmkitPicker.pickAndEdit(context, options: const PickerOptions(camera: true));
  • camera: true adds Photo and Video tabs next to the gallery. With type: photos or type: videos, only the matching tab is added.
  • A photo taken or a video recorded is picked at once, on its own, with the current crop ratio (centered). pickAndEdit opens the editor on it. Closing the editor goes back to the camera and deletes the capture.
  • saveCaptures: true also adds the captures to the device's photo library.
  • The flash is off, auto or on. The camera button switches between the front and back cameras. Recording stops by itself after maxVideoDuration (60 s by default, null for no limit).
  • The camera is released while the editor is open and while the app is in the background.

Options #

  • PickerOptions:
    • type: all, photos or videos.
    • maxCount: 1 by default.
    • aspects: the crop ratios.
    • columns: of the grid.
    • pageSize: media loaded at a time.
    • camera, saveCaptures, maxVideoDuration: see Camera.
    • texts: to translate the labels (PickerTexts).
  • FilmkitPickerPage is the screen itself, for apps that handle navigation themselves. Its onNext callback runs when the user taps Next: return a value to close the picker with it, or null to keep it open.
  • MediaLibrary is where the media come from, and where captures are saved. It is PhotoManagerLibrary (the device's photo library) by default. Pass library: to serve other media, or fake ones in tests.

Setup #

Android #

Add the permissions to AndroidManifest.xml:

<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />

The camera plugin adds the CAMERA and RECORD_AUDIO permissions to the app, even if it doesn't use the camera. To drop them, add xmlns:tools="http://schemas.android.com/tools" to the <manifest> tag and declare:

<uses-permission android:name="android.permission.CAMERA" tools:node="remove" />
<uses-permission android:name="android.permission.RECORD_AUDIO" tools:node="remove" />

photo_manager still applies the Kotlin Gradle plugin itself: keep android.builtInKotlin=false in gradle.properties until it migrates.

iOS #

Add NSPhotoLibraryUsageDescription to Info.plist. With the camera, also add NSCameraUsageDescription and NSMicrophoneUsageDescription (for the sound of videos).

Access to the library #

  • On first open, the picker asks for access.
  • If the user refuses, it explains why and offers a button to the system settings. It reloads when the app comes back to the foreground with access given.
  • With limited access (iOS 14+, Android 14+), a banner lets the user change the selection.
  • The grid follows changes to the library: media added or deleted, or a new selection.

Development #

  • Dart tests: flutter test. The picker runs against a fake MediaLibrary and video player, see test/fakes.dart.

  • Integration tests run on a device against the real photo library: flutter test integration_test/picker_test.dart -d <device> in example. Access must be given first, because a test can't answer the system prompt:

    • Android: install the app (flutter build apk --debug, adb install -r build/app/outputs/flutter-apk/app-debug.apk), then adb shell pm grant dev.noegnh.filmkit_picker_example android.permission.READ_MEDIA_IMAGES, and the same for READ_MEDIA_VIDEO. flutter test reinstalls the app and keeps the access, but uninstalls it at the end: repeat before each run.
    • iOS simulator: xcrun simctl privacy <device> grant photos dev.noegnh.filmkitPickerExample.

    For the camera test, also give android.permission.CAMERA and android.permission.RECORD_AUDIO. The test is skipped on devices without a camera, such as the iOS simulator.

    The tests add two samples to the library (filmkit_picker_sample.jpg and .mp4) once, and find them again on later runs.

    On iOS simulators, flutter test often never sees the app start (flutter/flutter#181771). The CI runs the same tests through XCTest instead, with example/ios/RunnerTests/RunnerTests.m: flutter build ios --config-only --simulator --debug integration_test/picker_test.dart, then xcodebuild test -workspace ios/Runner.xcworkspace -scheme Runner -destination 'platform=iOS Simulator,name=<device>' -only-testing:RunnerTests.

  • CI (.github/workflows/ci.yml): format, analysis and Dart tests; Android build; the integration tests on an Android emulator and an iOS simulator (through XCTest).

1
likes
160
points
278
downloads
screenshot

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Instagram-style photo and video picker for Flutter: gallery grid, albums, single or multiple selection with a crop preview, then filmkit's editor.

Repository (GitHub)
View/report issues

Topics

#picker #gallery #video #editor #camera

License

MIT (license)

Dependencies

camera, filmkit, flutter, photo_manager, video_player

More

Packages that depend on filmkit_picker