Home > front end >  How to get color of a point in a LinearGradient in Flutter
How to get color of a point in a LinearGradient in Flutter

Time:03-02

Suppose we have a enter image description here

CodePudding user response:

RenderRepaintBoundary solution

  • This solution retrieves an Image (dart:ui) from the RenderRepaintBoundary converts it to another Image type (from the image package),
  • We then obtain the touched widget's relative coordinates through an Listener widget.
  • Then we retrieve the color from the getPixel method from the image-package.

Make sure to run flutter pub add image, to get the package.

Solution:

import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:image/image.dart' as img;

import 'dart:ui' as ui show Image;

class GradientColorPicker extends StatefulWidget {
  const GradientColorPicker({Key? key, required this.gradientKey})
      : super(key: key);

  final GlobalKey gradientKey;

  @override
  State<GradientColorPicker> createState() => _GradientColorPickerState();
}

class _GradientColorPickerState extends State<GradientColorPicker> {

  Color? color;
  String? info;

  @override
  Widget build(BuildContext context) {

    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        RepaintBoundary(
          key: widget.gradientKey,
          child: Listener(
            onPointerMove: (event) async {
              // Get position touched
              int x = event.localPosition.dx.round();
              int y = event.localPosition.dy.round();

              // Create an ui.image from the RenderRepaintBoundary, which we
              // convert to another image format from the image-package.
              RenderRepaintBoundary boundary = widget.gradientKey.currentContext!
                  .findRenderObject()! as RenderRepaintBoundary;
              ui.Image image = await boundary.toImage();
              var byteData = await image.toByteData();
              Uint8List bytes = byteData!.buffer.asUint8List();
              img.Image imgImage = img.Image.fromBytes(200, 200, bytes);

              // Retrieve pixel information.
              int pixel = imgImage.getPixel(x, y); // Warning: #AABBGGRR
              pixel = abrgToARGB(pixel);

              setState(() {
                color = Color(pixel);
                info = "Color at ($x, $y) is $color";
              });
            },
            child: Container(
              width: 200,
              height: 200,
              decoration: const BoxDecoration(
                  gradient: LinearGradient(
                  colors: [Colors.black, Colors.white],
                  begin: Alignment.topLeft,
                  end: Alignment.bottomRight
              )
              ),
            ),
          ),
        ),

        if (color != null)
          ...[
            Text(info!),
            Container(
              width: 50,
              height: 50,
              color: color!,
            )
          ]

      ],
    );
  }

  // Function from: https://stackoverflow.com/a/42133405/3000503
  int abrgToARGB(int argbColor) {
    int r = (argbColor >> 16) & 0xFF;
    int b = argbColor & 0xFF;
    return (argbColor & 0xFF00FF00) | (b << 16) | r;
  }
}

// Some code to run the above example.
class App extends StatelessWidget {
  const App({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: GradientColorPicker(
            gradientKey: GlobalKey(),
          ),
        ),
      ),
    );
  }
}

void main() => runApp(const App());

Maybe there is a smarter way to do this but it works, nonetheless.

PS: You might want to do some bound-checking on the listener coordinates btw, as it may in some cases be outside the range 0-200.

CodePudding user response:

You can add stops to control how each color is spread out through the gradient. Example below:

     Container(
           decoration: BoxDecoration(
           gradient: LinearGradient(
           begin: Alignment.bottomLeft,
           end: Alignment.topRight,
          // Add one stop for each color 
          // Values should increase from 0.0 to 1.0
          stops: [0.1, 0.5, 0.8, 0.9],
          colors: [Colors.red, Colors.yellow, Colors.blue, Colors.purple]
    )
  )
),
  • Related