local_image_pager

A plugin for showing your device's local images with pagination.

SVID_20201019_170821_1 mp4_1280_606_606_1280_606_1280

Features

  • Get total number of images in your device.
  • Fetch local images with pagination.

Getting started

In the pubspec.yaml of your flutter project, add the following dependency:

dependencies:
  ...
  local_image_pager:

In your library add the following import:

import 'package:local_image_pager/local_image_pager.dart';

For help getting started with Flutter, view the online documentation.

How to use

See the example below. For more info, check the example project.


  @override
  Widget build(BuildContext context) {
    // Calculate the thumbnail's size first to improve performance.
    final itemSize = ((MediaQuery.of(context).size.width -
        spacing * (cross_axis_count - 1) - horizontal_padding * 2) /
        cross_axis_count);

    final cacheSize = (itemSize * 1.5).round();

    final pager = LocalImagePager();

    return MaterialApp(
      home: Scaffold(
          appBar: AppBar(
            title: const Text('Local images pager'),
          ),
          body: Column(children: [
            const SizedBox(height: 100,
                child: Center(child: Text('Your own widget here.', style: TextStyle(fontSize: 20, color: Colors.blue, fontWeight: FontWeight.bold),),)),
            Expanded(child: FutureBuilder(
              future: _initPermissions,
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  if (snapshot.data) {
                    return FutureBuilder(
                      future: LocalImagePager.totalNumber,
                      builder: (context, snapshot) {
                        if (snapshot.hasData) {
                          final count = snapshot.data;
                          if (count == 0) {
                            return const Center(
                              child: Text('No images'),
                            );
                          } else {
                            return GridView.builder(
                                padding: const EdgeInsets.symmetric(horizontal: horizontal_padding),
                                gridDelegate:
                                const SliverGridDelegateWithFixedCrossAxisCount(
                                  crossAxisCount: cross_axis_count,
                                  mainAxisSpacing: spacing,
                                  crossAxisSpacing: spacing,
                                ),
                                itemCount: count,
                                itemBuilder: (context, index) =>
                                    _itemBuilder(
                                        pager, count, index, itemSize, cacheSize));
                          }
                        } else if (snapshot.hasError) {
                          return Center(
                            child: Text(
                              snapshot.error.toString(),
                              style: const TextStyle(color: Colors.red),
                            ),
                          );
                        } else {
                          return const Center(
                              child: CircularProgressIndicator());
                        }
                      },
                    );
                  } else {
                    return Column(
                      children: [
                        const Text(
                          'Permission not granted, try again.',
                          style: const TextStyle(color: Colors.red),
                        ),
                        FlatButton(
                            onPressed: () {
                              setState(() {});
                            },
                            child: const Text('OK'))
                      ],
                    );
                  }
                } else if (snapshot.hasError) {
                  return Center(
                    child: Text(
                      snapshot.error.toString(),
                      style: const TextStyle(color: Colors.red),
                    ),
                  );
                } else {
                  return const Center(child: CircularProgressIndicator());
                }
              },
            ),)
          ])),
    );
  }

  Widget _itemBuilder(
      LocalImagePager paginate, int totalNumber, int index, double itemSize, int cacheSize) {
    _load(paginate, totalNumber, index);

    return FutureBuilder(
      future: _completers[index].future,
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return Image.file(
            File(snapshot.data),
            fit: BoxFit.cover,
            height: itemSize,
            cacheHeight: cacheSize,
          );
        } else if (snapshot.hasError) {
          print('_itemBuilder:error:${snapshot.error.toString()}');
          return const SizedBox(
            width: 0,
            height: 0,
          );
        } else {
          return const SizedBox(
            width: 0,
            height: 0,
          );
        }
      },
    );
  }

  _load(LocalImagePager paginate, int count, int itemIndex) async {
    if (itemIndex % per_load_count != 0) {
      return;
    }

    if (itemIndex >= _completers.length) {
      final toLoad = min(count - itemIndex, per_load_count);
      if (toLoad > 0) {
        _completers.addAll(List.generate(toLoad, (index) {
          return Completer();
        }));

        try {
          final images =
          await paginate.latestImages(itemIndex, itemIndex + toLoad - 1);
          images.asMap().forEach((index, item) {
            _completers[itemIndex + index].complete(item);
          });
        } catch (e) {
          print('_load:error:${e.toString()}');
          _completers
              .sublist(itemIndex, itemIndex + toLoad)
              .forEach((completer) {
            completer.completeError(e);
          });
        }
      }
    }
  }

Permissions

Applications using this plugin require the following user permissions.

iOS

Add the following key to your Info.plist file, located in <project root>/ios/Runner/Info.plist:

  • NSPhotoLibraryUsageDescription - describe why your app needs permission for the photo library. This is called Privacy - Photo Library Usage Description in the visual editor. This permission is required for the app to read the image and album information.

Android

Add the storage permission to your AndroidManifest.xml file, located in <project root>/android/app/src/main/AndroidManifest.xml:

  • android.permission.READ_EXTERNAL_STORAGE - this allows the app to query and read the image and album information.

Libraries

local_image_pager