flutter_mtp_picker 0.0.1 copy "flutter_mtp_picker: ^0.0.1" to clipboard
flutter_mtp_picker: ^0.0.1 copied to clipboard

A Flutter Windows plugin for browsing Android and camera storage over USB MTP using Windows Portable Devices.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'dart:async';

import 'package:flutter/services.dart';
import 'package:flutter_mtp_picker/flutter_mtp_picker.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  List<MtpDevice> _devices = const <MtpDevice>[];
  MtpDevice? _selectedDevice;
  List<MtpObject> _children = const <MtpObject>[];
  List<MtpFile> _mediaFiles = const <MtpFile>[];
  final List<MtpObject> _path = <MtpObject>[];
  MtpFolderSelection? _pickedFolder;
  bool _loadingChildren = false;
  bool _loadingMedia = false;
  String? _error;

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

  Future<void> initPlatformState() async {
    List<MtpDevice> devices;
    String? error;
    try {
      devices = await MtpPicker.getDevices();
    } on PlatformException catch (e) {
      devices = const <MtpDevice>[];
      error = e.message ?? 'Failed to enumerate MTP devices.';
    }

    if (!mounted) return;

    setState(() {
      _devices = devices;
      _error = error;
    });
  }

  Future<void> _openDevice(MtpDevice device) async {
    setState(() {
      _selectedDevice = device;
      _children = const <MtpObject>[];
      _mediaFiles = const <MtpFile>[];
      _path.clear();
      _loadingChildren = true;
      _loadingMedia = false;
      _error = null;
    });

    await _loadChildren(deviceId: device.id, objectId: 'ROOT');
  }

  Future<void> _openFolder(MtpObject folder) async {
    final device = _selectedDevice;
    if (device == null || !folder.isFolder) return;

    setState(() {
      _path.add(folder);
      _children = const <MtpObject>[];
      _mediaFiles = const <MtpFile>[];
      _loadingChildren = true;
      _loadingMedia = false;
      _error = null;
    });

    await _loadChildren(deviceId: device.id, objectId: folder.id);
  }

  Future<void> _goBack() async {
    final device = _selectedDevice;
    if (device == null || _path.isEmpty) return;

    setState(() {
      _path.removeLast();
      _children = const <MtpObject>[];
      _mediaFiles = const <MtpFile>[];
      _loadingChildren = true;
      _loadingMedia = false;
      _error = null;
    });

    await _loadChildren(
      deviceId: device.id,
      objectId: _path.isEmpty ? 'ROOT' : _path.last.id,
    );
  }

  Future<void> _loadChildren({
    required String deviceId,
    required String objectId,
  }) async {
    List<MtpObject> children;
    String? error;
    try {
      children = await MtpPicker.listChildren(
        deviceId: deviceId,
        objectId: objectId,
      );
    } on PlatformException catch (e) {
      children = const <MtpObject>[];
      error = e.message ?? 'Failed to list MTP folder.';
    }

    if (!mounted) return;

    setState(() {
      _children = children;
      _loadingChildren = false;
      _error = error;
    });
  }

  Future<void> _scanVideos() async {
    final device = _selectedDevice;
    if (device == null) return;

    setState(() {
      _mediaFiles = const <MtpFile>[];
      _loadingMedia = true;
      _error = null;
    });

    List<MtpFile> files;
    String? error;
    try {
      files = await MtpPicker.listMediaFiles(
        deviceId: device.id,
        folderId: _path.isEmpty ? 'ROOT' : _path.last.id,
        extensions: const <String>['mp4', 'mkv', 'avi'],
      );
    } on PlatformException catch (e) {
      files = const <MtpFile>[];
      error = e.message ?? 'Failed to scan media files.';
    }

    if (!mounted) return;

    setState(() {
      _mediaFiles = files;
      _loadingMedia = false;
      _error = error;
    });
  }

  Future<void> _pickFolder() async {
    final selection = await MtpPicker.pickFolder(context);
    if (!mounted || selection == null) return;

    setState(() {
      _pickedFolder = selection;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('MTP devices')),
        body: ListView(
          padding: const EdgeInsets.all(16),
          children: <Widget>[
            Align(
              alignment: Alignment.centerLeft,
              child: FilledButton.icon(
                onPressed: _pickFolder,
                icon: const Icon(Icons.folder_open_outlined),
                label: const Text('Pick MTP folder'),
              ),
            ),
            if (_pickedFolder != null)
              Padding(
                padding: const EdgeInsets.only(top: 12),
                child: Text(
                  'Picked: ${_pickedFolder!.device.name} / '
                  '${_pickedFolder!.folder.name}\n'
                  'Folder ID: ${_pickedFolder!.folder.id}',
                ),
              ),
            const Divider(height: 32),
            if (_error != null)
              Text(_error!, style: Theme.of(context).textTheme.bodyLarge)
            else if (_devices.isEmpty)
              const Text('No MTP devices connected.')
            else
              for (final device in _devices)
                ListTile(
                  title: Text(device.name),
                  subtitle: Text(device.id),
                  selected: _selectedDevice?.id == device.id,
                  onTap: () => _openDevice(device),
                ),
            if (_selectedDevice != null) ...<Widget>[
              const Divider(height: 32),
              Row(
                children: <Widget>[
                  IconButton(
                    onPressed: _path.isEmpty || _loadingChildren
                        ? null
                        : _goBack,
                    icon: const Icon(Icons.arrow_back),
                    tooltip: 'Back',
                  ),
                  Expanded(
                    child: Text(
                      _path.isEmpty
                          ? _selectedDevice!.name
                          : _path
                                .map((MtpObject object) => object.name)
                                .join(' / '),
                    ),
                  ),
                  TextButton.icon(
                    onPressed: _loadingChildren || _loadingMedia
                        ? null
                        : _scanVideos,
                    icon: const Icon(Icons.video_file_outlined),
                    label: const Text('Scan videos'),
                  ),
                ],
              ),
              if (_loadingChildren)
                const Padding(
                  padding: EdgeInsets.all(16),
                  child: Center(child: CircularProgressIndicator()),
                )
              else if (_children.isEmpty)
                const ListTile(title: Text('No child objects.'))
              else
                for (final child in _children)
                  ListTile(
                    leading: Icon(
                      child.isFolder
                          ? Icons.folder_outlined
                          : Icons.insert_drive_file_outlined,
                    ),
                    title: Text(child.name),
                    subtitle: Text(child.id),
                    enabled: child.isFolder,
                    onTap: child.isFolder ? () => _openFolder(child) : null,
                  ),
              if (_loadingMedia)
                const Padding(
                  padding: EdgeInsets.all(16),
                  child: Center(child: CircularProgressIndicator()),
                )
              else if (_mediaFiles.isNotEmpty) ...<Widget>[
                const Divider(height: 32),
                Text('Videos', style: Theme.of(context).textTheme.titleMedium),
                for (final file in _mediaFiles)
                  ListTile(
                    leading: const Icon(Icons.movie_outlined),
                    title: Text(file.name),
                    subtitle: Text('${file.size} bytes\n${file.id}'),
                  ),
              ],
            ],
          ],
        ),
      ),
    );
  }
}
0
likes
0
points
343
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter Windows plugin for browsing Android and camera storage over USB MTP using Windows Portable Devices.

Repository (GitHub)
View/report issues

Topics

#mtp #windows #android #usb #file-picker

License

unknown (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on flutter_mtp_picker

Packages that implement flutter_mtp_picker