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

A Flutter plugin for accessing media files (videos and audio) on the device with support for various formats and thumbnails.

example/lib/main.dart

import 'dart:async';

import 'package:device_media_finder/device_media_finder.dart';
import 'package:device_media_finder/models/media_file.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

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

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

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

class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
  String platformVersions = 'Unknown';
  final _deviceMediaFinderPlugin = DeviceMediaFinder();
  List<VideoFile> _videos = [];
  List<AudioFile> _audios = [];
  bool _isLoading = false;
  bool _hasSearchedVideos = false;
  bool _hasSearchedAudios = false;
  late TabController _tabController;

  final Map<String, Uint8List?> _thumbnailCache = {};

  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: 2, vsync: this);
    initPlatformState();
  }

  @override
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> initPlatformState() async {
    String platformVersion;
    // Platform messages may fail, so we use a try/catch PlatformException.
    // We also handle the message potentially returning null.
    try {
      platformVersion =
          await _deviceMediaFinderPlugin.getPlatformVersion() ??
          'Unknown platform version';
    } on PlatformException {
      platformVersion = 'Failed to get platform version.';
    }

    // If the widget was removed from the tree while the asynchronous platform
    // message was in flight, we want to discard the reply rather than calling
    // setState to update our non-existent appearance.
    if (!mounted) return;

    setState(() {
      platformVersions = platformVersion;
    });
  }

  Future<void> _loadVideos() async {
    if (_isLoading) return;

    setState(() {
      _isLoading = true;
    });

    try {
      // Use the new method to get videos by MIME type
      // This includes all common video formats
      final videos = await _deviceMediaFinderPlugin.getVideosByMimeType([
        'video/mp4',
        'video/3gpp',
        'video/webm',
        'video/quicktime',
        'video/x-matroska',
        'video/avi',
        'video/mpeg',
        'video/x-ms-wmv',
        'video/*', // Fallback to catch any other video types
      ]);

      if (!mounted) return;
      setState(() {
        _videos = videos;
        _isLoading = false;
        _hasSearchedVideos = true;
      });

      // Log the found video formats for debugging
      if (videos.isNotEmpty) {
        final formats = videos.map((v) => v.mimeType).toSet().toList();
        debugPrint('Found videos with formats: $formats');
      } else {
        debugPrint('No videos found');
      }
    } catch (e) {
      debugPrint('Error loading videos: $e');
      if (!mounted) return;
      setState(() {
        _isLoading = false;
        _hasSearchedVideos = true;
      });
      ScaffoldMessenger.of(
        context,
      ).showSnackBar(SnackBar(content: Text('Error loading videos: $e')));
    }
  }

  Future<void> _loadAudios() async {
    if (_isLoading) return;

    setState(() {
      _isLoading = true;
    });

    try {
      final audios = await _deviceMediaFinderPlugin.getAudios();
      if (!mounted) return;
      setState(() {
        _audios = audios;
        _isLoading = false;
        _hasSearchedAudios = true;
      });
    } catch (e) {
      if (!mounted) return;
      setState(() {
        _isLoading = false;
        _hasSearchedAudios = true;
      });
      ScaffoldMessenger.of(
        context,
      ).showSnackBar(SnackBar(content: Text('Error loading audios: $e')));
    }
  }

  Future<Uint8List?> _getThumbnail(String videoId) async {
    if (_thumbnailCache.containsKey(videoId)) {
      return _thumbnailCache[videoId];
    }

    try {
      final thumbnail = await _deviceMediaFinderPlugin.getVideoThumbnail(
        videoId,
      );
      _thumbnailCache[videoId] = thumbnail;
      return thumbnail;
    } catch (e) {
      debugPrint('Error loading thumbnail: $e');
      return null;
    }
  }

  String _formatDuration(int milliseconds) {
    final seconds = (milliseconds / 1000).floor();
    final minutes = (seconds / 60).floor();
    final hours = (minutes / 60).floor();

    final remainingMinutes = minutes % 60;
    final remainingSeconds = seconds % 60;

    if (hours > 0) {
      return '$hours:${remainingMinutes.toString().padLeft(2, '0')}:${remainingSeconds.toString().padLeft(2, '0')}';
    } else {
      return '$minutes:${remainingSeconds.toString().padLeft(2, '0')}';
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Device Media Finder'),
          bottom: TabBar(
            controller: _tabController,
            tabs: const [
              Tab(text: 'Videos', icon: Icon(Icons.video_library)),
              Tab(text: 'Music', icon: Icon(Icons.music_note)),
            ],
          ),
        ),
        body: TabBarView(
          controller: _tabController,
          children: [
            // Videos Tab
            _videos.isEmpty
                ? Center(
                  child:
                      _isLoading
                          ? const CircularProgressIndicator()
                          : _hasSearchedVideos
                          ? Column(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [
                              const Text(
                                'No videos found',
                                style: TextStyle(fontSize: 18),
                              ),
                              const SizedBox(height: 16),
                              ElevatedButton(
                                onPressed: _loadVideos,
                                child: const Text('Try Again'),
                              ),
                            ],
                          )
                          : ElevatedButton(
                            onPressed: _loadVideos,
                            child: const Text('Load Videos'),
                          ),
                )
                : ListView.builder(
                  itemCount: _videos.length,
                  itemBuilder: (context, index) {
                    final video = _videos[index];
                    return Card(
                      margin: const EdgeInsets.all(8.0),
                      child: ListTile(
                        leading: FutureBuilder<Uint8List?>(
                          future: _getThumbnail(video.id),
                          builder: (context, snapshot) {
                            if (snapshot.connectionState ==
                                ConnectionState.waiting) {
                              return const SizedBox(
                                width: 64,
                                height: 64,
                                child: Center(
                                  child: CircularProgressIndicator(),
                                ),
                              );
                            }

                            if (snapshot.hasData && snapshot.data != null) {
                              return Image.memory(
                                snapshot.data!,
                                width: 64,
                                height: 64,
                                fit: BoxFit.cover,
                              );
                            }

                            return const SizedBox(
                              width: 64,
                              height: 64,
                              child: Icon(Icons.video_file),
                            );
                          },
                        ),
                        title: Text(video.name),
                        subtitle: Text(_formatDuration(video.duration)),
                        trailing: Text(
                          '${(video.size / (1024 * 1024)).toStringAsFixed(1)} MB',
                        ),
                      ),
                    );
                  },
                ),

            // Music Tab
            _audios.isEmpty
                ? Center(
                  child:
                      _isLoading
                          ? const CircularProgressIndicator()
                          : _hasSearchedAudios
                          ? Column(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [
                              const Text(
                                'No music found',
                                style: TextStyle(fontSize: 18),
                              ),
                              const SizedBox(height: 16),
                              ElevatedButton(
                                onPressed: _loadAudios,
                                child: const Text('Try Again'),
                              ),
                            ],
                          )
                          : ElevatedButton(
                            onPressed: _loadAudios,
                            child: const Text('Load Music'),
                          ),
                )
                : ListView.builder(
                  itemCount: _audios.length,
                  itemBuilder: (context, index) {
                    final audio = _audios[index];
                    return Card(
                      margin: const EdgeInsets.all(8.0),
                      child: ListTile(
                        leading: const CircleAvatar(
                          child: Icon(Icons.music_note),
                        ),
                        title: Text(audio.name),
                        subtitle: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [Text(audio.artist), Text(audio.album)],
                        ),
                        trailing: Text(_formatDuration(audio.duration)),
                      ),
                    );
                  },
                ),
          ],
        ),
      ),
    );
  }
}
1
likes
160
points
17
downloads

Publisher

verified publisherswanflutterdev.com

Weekly Downloads

A Flutter plugin for accessing media files (videos and audio) on the device with support for various formats and thumbnails.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on device_media_finder

Packages that implement device_media_finder