Simple Eye Dropper

A simple eye dropper widget that depends only on standard libraries.

🌏 English | 日本語

Table of Contents

Use with asset images

Build the EyeDropper widget as follows:

final byteData = await rootBundle.load('asset/example.png');

(snip)
      
EyeDropper.of(
  // Encoded Uint8List.
  bytes: byteData.buffer.asUint8List(),
  // Size to display.
  size: const Size(200, 400),
  // Callback called when color is selected.
  onSelected: (color) => print('Selected color is $color'),
);
  • bytes argument is a Uint8List of image bytes obtained by image_picker etc. If bytes argument is null, a blank area is displayed.
  • onSelected specifies a callback to be called when the color is selected.

Pointer

Choose a Pointer implementation

By default, the following pointers with magnification are specified.

EyeDropper.of(
  bytes: bytes,
  size: const Size(200, 400),
  // By default.
  pointerBuilder: MagnifierPointer.new,
  onSelected: (color) => print('Selected color is $color'),
);

You can also specify a simple pointer as follows:

EyeDropper.of(
  bytes: bytes,
  size: const Size(200, 400),
  // Simple small square pointer without magnification.
  pointerBuilder: (_, __) => SimplePointer(),
  onSelected: (color) => print('Selected color is $color'),
);

Or CircularMagnifierPointer is the following:

EyeDropper.of(
  bytes: bytes,
  size: const Size(200, 400),
  pointerBuilder: CircularMagnifierPointer.new,
  onSelected: (color) => print('Selected color is $color'),
);

Both of these pointers have several parameters providing for some customization.

EyeDropper.of(
  bytes: bytes,
  size: const Size(200, 400),
  // Customize the pointer with magnification.
  pointerBuilder: (uiImage, ratio) => MagnifierPointer(
    uiImage,
    ratio,
    magnification: 2.5,
    outerRectSize: 101,
    outerStrokeWidth: 3,
    innerRectSize: 9,
    innerStrokeWidth: 3,
  ),
  onSelected: (color) => print('Selected color is $color'),
);

Implement Pointer

You can also create your own pointers by inheriting from Pointer classes.
Refer to the code of the CircleMagnifierPointer class for how to implement Pointer.

Supported image formats and error handling

Supported image formats are similar to instantiateImageCodec function of dart:ui.
At least the following image formats are supported: JPEG, PNG, GIF, Animated GIF, WebP, Animated WebP, BMP, and WBMP.

When passing an unsupported image format Uint8List to bytes, errorBuilder is called.
The usage of errorBuilder is the same as in Image.errorBuilder. By default, a gray error icon is displayed.

If you pass null for bytes, a blank area will be displayed.

Use with image_picker

final picker = ImagePicker();
final image = await picker.pickImage(source: ImageSource.gallery);
if(image == null) {
  return;
}
final bytes = await image.readAsBytes();

(snip)
    
EyeDropper.of(
  bytes: bytes,
  size: const Size(200, 400),
  onSelected: (color) => print('Selected color is $color'),
);

In practice, you will probably use FutureBuilder for async/await support.
See example/lib/main.dart for detailed coding examples.

Use with network images

For example, if you use the http package, you can do the following:

import 'package:http/http.dart' as http;

(snip)

final response = await http.get(Uri.parse('https://example.org/sample.jpg'));

(snip)

EyeDropper.of(
  bytes: response.bodyBytes,
  size: const Size(200, 400),
  onSelected: (color) => print('Selected color is $color'),
);

Just pass the response body as is to bytes.

Use with Dart Image Library

If you want to pass an image processed with image (Dart Image Library) to EyeDropper, pass a Uint8List that has been re-encoded with img.encodeXXX as shown below.

import 'package:image/image.dart' as img;

(snip)

final imgImage = img.decodeImage(bytes);
final grayImage = img.grayscale(imgImage!);
grayBytes = img.encodeJpg(grayImage);

(snip)

EyeDropper.of(
  bytes: grayBytes,
  size: const Size(200, 400),
  onSelected: (color) => print('Selected color is $color'),
);

Use with Riverpod

When EyeDropper is used with ConsumerWidget or ConsumerStatefulWidget of Riverpod, the pointer may not be displayed because it is redrawn in its entirety.

// BAD example.
import 'package:flutter_riverpod/flutter_riverpod.dart';

final colorProvider = StateProvider<Color>((ref) => Colors.white);

(snip)

class MyHomePage extends ConsumerWidget {

(snip)

  // Display color code.
  Text(ref.watch(colorProvider).toString()),

(snip)

}

In such cases, instead of using ConsumerWidget or ConsumerStatefulWidget's ref as is, use Consumer to specify the redraw range.

// GOOD example.
import 'package:flutter_riverpod/flutter_riverpod.dart';

final colorProvider = StateProvider<Color>((ref) => Colors.white);

class MyHomePage extends ConsumerWidget {

(snip)

  // Display color code.
  Consumer(
    builder: (_, ref, __) {
      return Text(ref.watch(colorProvider).toString());
    },
  ),

(snip)

}

Libraries

simple_eye_dropper
A simple eye dropper widget that depends only on standard libraries.