native_image_picker_macos 0.0.2 copy "native_image_picker_macos: ^0.0.2" to clipboard
native_image_picker_macos: ^0.0.2 copied to clipboard

PlatformmacOS

A macOS platform implementation of image_picker using the native system picker instead of file_selector

example/lib/main.dart

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:image_picker_macos/image_picker_macos.dart';
import 'package:native_image_picker_macos/native_image_picker_macos.dart';
import 'package:video_player/video_player.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await NativeImagePickerMacOS.registerWithIfSupported();

  runApp(const MainApp());
}

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.light(useMaterial3: true),
      darkTheme: ThemeData.dark(useMaterial3: true),
      themeMode: ThemeMode.system,
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: Padding(
            padding: EdgeInsets.all(64.0),
            child: SingleChildScrollView(child: Buttons()),
          ),
        ),
      ),
    );
  }
}

class Buttons extends StatefulWidget {
  const Buttons({super.key});

  @override
  State<Buttons> createState() => _ButtonsState();
}

class _ButtonsState extends State<Buttons> {
  late bool _useMacOSNativePicker;
  late Future<bool> _macOSNativePickerSupportedFuture;

  final imagePicker = ImagePicker();

  final TextEditingController _imageMaxWidthController =
      TextEditingController();
  final TextEditingController _imageMaxHeightController =
      TextEditingController();

  final TextEditingController _imageQualityController = TextEditingController();

  final _mediaFiles = <XFile>[];

  @override
  void initState() {
    super.initState();

    _useMacOSNativePicker = NativeImagePickerMacOS.isRegistered();
    _macOSNativePickerSupportedFuture = NativeImagePickerMacOS.isSupported();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      spacing: 12,
      children: [
        Form(
          autovalidateMode: AutovalidateMode.always,
          child: Column(
            spacing: 8,
            children: [
              if (_useMacOSNativePicker)
                ElevatedButton.icon(
                  label: Text('Open Photos App'),
                  icon: Icon(Icons.photo_album),
                  // Using instanceOrThrow since we assume the current instance
                  // is NativeImagePickerMacOS due to _useMacOSNativePicker check.
                  onPressed: () =>
                      NativeImagePickerMacOS.instanceOrThrow.openPhotosApp(),
                ),
              TextFormField(
                controller: _imageMaxWidthController,
                decoration: InputDecoration(labelText: 'Image Max Width'),
                textInputAction: TextInputAction.next,
                validator: validateNumber,
              ),
              TextFormField(
                controller: _imageMaxHeightController,
                decoration: InputDecoration(labelText: 'Image Max Height'),
                textInputAction: TextInputAction.next,
                validator: validateNumber,
              ),
              TextFormField(
                controller: _imageQualityController,
                decoration: InputDecoration(labelText: 'Image Quality'),
                textInputAction: TextInputAction.next,
                validator: validateNumber,
              ),
            ],
          ),
        ),
        ElevatedButton.icon(
          icon: Icon(Icons.photo),
          label: Text('Pick Image'),
          onPressed: () async {
            final imageFile = await imagePicker.pickImage(
              source: ImageSource.gallery,
              imageQuality: int.tryParse(_imageQualityController.text) ?? 100,
              maxWidth: double.tryParse(_imageMaxWidthController.text),
              maxHeight: double.tryParse(_imageMaxHeightController.text),
            );
            _setMediaFileListFromFile(imageFile);
          },
        ),
        ElevatedButton.icon(
          icon: Icon(Icons.photo_library),
          label: Text('Pick Images'),
          onPressed: () async {
            final imageFiles = await imagePicker.pickMultiImage(
              imageQuality: int.tryParse(_imageQualityController.text) ?? 100,
              maxWidth: double.tryParse(_imageMaxWidthController.text),
              maxHeight: double.tryParse(_imageMaxHeightController.text),
            );
            _setMediaFileListFromFiles(imageFiles);
          },
        ),
        ElevatedButton.icon(
          icon: Icon(Icons.movie),
          label: Text('Pick Video'),
          onPressed: () async {
            final videoFile = await imagePicker.pickVideo(
              source: ImageSource.gallery,
            );
            _setMediaFileListFromFile(videoFile);
          },
        ),
        ElevatedButton.icon(
          icon: Icon(Icons.perm_media),
          label: Text('Pick Media'),
          onPressed: () async {
            final mediaFiles = await imagePicker.pickMultipleMedia(
              imageQuality: int.tryParse(_imageQualityController.text) ?? 100,
              maxWidth: double.tryParse(_imageMaxWidthController.text),
              maxHeight: double.tryParse(_imageMaxHeightController.text),
            );
            _setMediaFileListFromFiles(mediaFiles);
          },
        ),
        FutureBuilder(
          future: _macOSNativePickerSupportedFuture,
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return CircularProgressIndicator.adaptive();
            }
            if (snapshot.hasError) {
              return Text(
                'Error while checking whether the native macOS picker is supported: ${snapshot.error.toString()}',
              );
            }
            final bool nativeMacOSPickerSupported = snapshot.requireData;
            if (nativeMacOSPickerSupported) {
              return SwitchListTile.adaptive(
                title: Text('Use native macOS picker'),
                value: _useMacOSNativePicker,
                onChanged: (bool value) {
                  if (value) {
                    NativeImagePickerMacOS.registerWith();
                  } else {
                    ImagePickerMacOS.registerWith();
                  }
                  setState(() => _useMacOSNativePicker = value);
                },
              );
            } else {
              return Text(
                  'The current macOS version does not supports PHPicker.');
            }
          },
        ),
        ..._mediaFiles.nonNulls.map(
          (e) => isImage(e.path)
              ? Image.file(File(e.path))
              : _buildInlineVideoPlayer(e.path),
        )
      ],
    );
  }

  void _setMediaFileListFromFile(XFile? file) {
    setState(() {
      _mediaFiles.clear();
      if (file != null) {
        _mediaFiles.add(file);
      }
    });
  }

  void _setMediaFileListFromFiles(List<XFile> files) {
    setState(() {
      _mediaFiles.clear();
      _mediaFiles.addAll(files);
    });
  }

  Widget _buildInlineVideoPlayer(String videoFilePath) {
    final VideoPlayerController controller =
        VideoPlayerController.file(File(videoFilePath));
    const double volume = 1.0;
    controller.setVolume(volume);
    controller.initialize();
    controller.setLooping(true);
    controller.play();
    return Center(child: AspectRatioVideo(controller));
  }

  String? validateNumber(String? value) {
    if (value == null || value.trim().isEmpty) {
      return null;
    }
    return int.tryParse(value) == null ? 'Invalid number.' : null;
  }

  bool isImage(String filePath) {
    final ext = filePath.split('.').last.toLowerCase();
    return {'jpg', 'jpeg', 'png', 'gif', 'bmp'}.contains(ext);
  }

  @override
  void dispose() {
    _imageMaxWidthController.dispose();
    _imageMaxHeightController.dispose();
    _imageQualityController.dispose();
    super.dispose();
  }
}

class AspectRatioVideo extends StatefulWidget {
  const AspectRatioVideo(this.controller, {super.key});

  final VideoPlayerController? controller;

  @override
  AspectRatioVideoState createState() => AspectRatioVideoState();
}

class AspectRatioVideoState extends State<AspectRatioVideo> {
  VideoPlayerController? get controller => widget.controller;
  bool initialized = false;

  void _onVideoControllerUpdate() {
    if (!mounted) {
      return;
    }
    if (initialized != controller!.value.isInitialized) {
      initialized = controller!.value.isInitialized;
      setState(() {});
    }
  }

  @override
  void initState() {
    super.initState();
    controller!.addListener(_onVideoControllerUpdate);
  }

  @override
  void dispose() {
    controller!.removeListener(_onVideoControllerUpdate);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (initialized) {
      return Center(
        child: AspectRatio(
          aspectRatio: controller!.value.aspectRatio,
          child: VideoPlayer(controller!),
        ),
      );
    } else {
      return CircularProgressIndicator.adaptive();
    }
  }
}
copied to clipboard
1
likes
160
points
59
downloads

Publisher

verified publishercompilekernel.dev

Weekly Downloads

2024.09.21 - 2025.04.05

A macOS platform implementation of image_picker using the native system picker instead of file_selector

Repository (GitHub)
View/report issues

Topics

#image-picker #files #file-selection

Documentation

Documentation
API reference

License

MIT (license)

Dependencies

flutter, image_picker_platform_interface, meta

More

Packages that depend on native_image_picker_macos