Line data Source code
1 : import 'dart:convert';
2 : import 'dart:typed_data';
3 : import 'dart:ui' as ui;
4 : import 'package:flutter/material.dart';
5 : import 'package:flutter/rendering.dart';
6 : import 'package:image/image.dart' as img;
7 : import 'package:flutter_ume/flutter_ume.dart';
8 : import 'icon.dart' as icon;
9 :
10 : class ColorSucker extends StatefulWidget implements Pluggable {
11 : final double scale;
12 : final Size size;
13 :
14 1 : const ColorSucker({
15 : Key? key,
16 : this.scale = 10.0,
17 : this.size = const Size(100, 100),
18 1 : }) : super(key: key);
19 :
20 1 : @override
21 1 : _ColorSuckerState createState() => _ColorSuckerState();
22 :
23 1 : @override
24 : Widget buildWidget(BuildContext? context) => this;
25 :
26 1 : @override
27 : String get name => 'ColorSucker';
28 :
29 0 : @override
30 : String get displayName => 'ColorSucker';
31 :
32 1 : @override
33 : void onTrigger() {}
34 :
35 1 : @override
36 : ImageProvider<Object> get iconImageProvider =>
37 2 : MemoryImage(base64Decode(icon.iconData));
38 : }
39 :
40 : class _ColorSuckerState extends State<ColorSucker> {
41 : late Size _magnifierSize;
42 : double? _scale;
43 : BorderRadius? _radius;
44 : Color _currentColor = Colors.white;
45 : img.Image? _snapshot;
46 : Offset _magnifierPosition = Offset.zero;
47 : double _toolBarY = 60.0;
48 : Matrix4 _matrix = Matrix4.identity();
49 : late Size _windowSize;
50 : bool _excuting = false;
51 :
52 1 : @override
53 : void initState() {
54 6 : _windowSize = ui.window.physicalSize / ui.window.devicePixelRatio;
55 3 : _magnifierSize = widget.size;
56 3 : _scale = widget.scale;
57 4 : _radius = BorderRadius.circular(_magnifierSize.longestSide);
58 5 : _matrix = Matrix4.identity()..scale(widget.scale);
59 1 : _magnifierPosition =
60 5 : _windowSize.center(Offset.zero) - _magnifierSize.center(Offset.zero);
61 1 : super.initState();
62 : }
63 :
64 0 : @override
65 : void didUpdateWidget(ColorSucker oldWidget) {
66 0 : if (oldWidget.size != widget.size) {
67 0 : _magnifierSize = widget.size;
68 0 : _radius = BorderRadius.circular(_magnifierSize.longestSide);
69 : }
70 0 : if (oldWidget.scale != widget.scale) {
71 0 : _scale = widget.scale;
72 0 : _matrix = Matrix4.identity()..scale(_scale);
73 : }
74 0 : super.didUpdateWidget(oldWidget);
75 : }
76 :
77 1 : void _onPanUpdate(DragUpdateDetails dragDetails) {
78 1 : _magnifierPosition =
79 4 : dragDetails.globalPosition - _magnifierSize.center(Offset.zero);
80 2 : double newX = dragDetails.globalPosition.dx;
81 2 : double newY = dragDetails.globalPosition.dy;
82 1 : final Matrix4 newMatrix = Matrix4.identity()
83 1 : ..translate(newX, newY)
84 3 : ..scale(_scale, _scale)
85 3 : ..translate(-newX, -newY);
86 1 : _matrix = newMatrix;
87 2 : _searchPixel(dragDetails.globalPosition);
88 2 : setState(() {});
89 : }
90 :
91 1 : void _toolBarPanUpdate(DragUpdateDetails dragDetails) {
92 4 : _toolBarY = dragDetails.globalPosition.dy - 40;
93 2 : setState(() {});
94 : }
95 :
96 1 : void _onPanStart(DragStartDetails dragDetails) async {
97 3 : if (_snapshot == null && _excuting == false) {
98 1 : _excuting = true;
99 2 : await _captureScreen();
100 : }
101 : }
102 :
103 1 : void _onPanEnd(DragEndDetails dragDetails) {
104 1 : _snapshot = null;
105 : }
106 :
107 1 : void _searchPixel(Offset globalPosition) {
108 1 : _calculatePixel(globalPosition);
109 : }
110 :
111 1 : Future<void> _captureScreen() async {
112 : try {
113 : RenderRepaintBoundary boundary =
114 3 : rootKey.currentContext!.findRenderObject() as RenderRepaintBoundary;
115 0 : ui.Image image = await boundary.toImage();
116 : ByteData? byteData =
117 0 : await image.toByteData(format: ui.ImageByteFormat.png);
118 : if (byteData == null) {
119 : return;
120 : }
121 0 : Uint8List pngBytes = byteData.buffer.asUint8List();
122 0 : _snapshot = img.decodeImage(pngBytes);
123 0 : _excuting = false;
124 0 : image.dispose();
125 : } catch (e) {
126 3 : debugPrint(e.toString());
127 : }
128 : }
129 :
130 1 : void _calculatePixel(Offset globalPosition) {
131 1 : if (_snapshot == null) return;
132 0 : double px = globalPosition.dx;
133 0 : double py = globalPosition.dy;
134 0 : int pixel32 = _snapshot!.getPixelSafe(px.toInt(), py.toInt());
135 0 : int hex = _abgrToArgb(pixel32);
136 0 : _currentColor = Color(hex);
137 : }
138 :
139 0 : int _abgrToArgb(int argbColor) {
140 0 : int r = (argbColor >> 16) & 0xFF;
141 0 : int b = argbColor & 0xFF;
142 0 : return (argbColor & 0xFF00FF00) | (b << 16) | r;
143 : }
144 :
145 1 : @override
146 : Widget build(BuildContext context) {
147 2 : if (_windowSize.isEmpty) {
148 0 : _windowSize = MediaQuery.of(context).size;
149 0 : _magnifierPosition =
150 0 : _windowSize.center(Offset.zero) - _magnifierSize.center(Offset.zero);
151 : }
152 1 : Widget toolBar = Container(
153 4 : width: MediaQuery.of(context).size.width - 32,
154 1 : decoration: BoxDecoration(
155 : color: Colors.white,
156 1 : borderRadius: BorderRadius.circular(16),
157 1 : boxShadow: [
158 : const BoxShadow(
159 : color: Colors.black26, blurRadius: 6, offset: Offset(2, 2))
160 : ]),
161 : margin: const EdgeInsets.only(left: 16, right: 16),
162 1 : child: Row(
163 1 : children: <Widget>[
164 1 : Container(
165 : margin: const EdgeInsets.only(left: 16, top: 10, bottom: 10),
166 : width: 60,
167 : height: 60,
168 1 : decoration: BoxDecoration(
169 : shape: BoxShape.circle,
170 1 : color: _currentColor,
171 1 : border: Border.all(width: 2.0, color: Colors.white),
172 1 : boxShadow: [
173 : const BoxShadow(
174 : color: Colors.black12,
175 : blurRadius: 4,
176 : offset: Offset(2, 2))
177 : ]),
178 : ),
179 1 : Container(
180 : margin: const EdgeInsets.only(left: 40, right: 16),
181 : child:
182 6 : Text("#${_currentColor.value.toRadixString(16).substring(2)}",
183 : style: const TextStyle(
184 : fontSize: 25,
185 : color: Colors.grey,
186 : )),
187 : ),
188 : ],
189 : ),
190 : );
191 :
192 1 : return Stack(
193 : alignment: Alignment.center,
194 1 : children: [
195 1 : Positioned(
196 : left: 0,
197 1 : top: _toolBarY,
198 1 : child: GestureDetector(
199 1 : onVerticalDragUpdate: _toolBarPanUpdate, child: toolBar)),
200 1 : Positioned(
201 2 : left: _magnifierPosition.dx,
202 2 : top: _magnifierPosition.dy,
203 1 : child: ClipRRect(
204 1 : borderRadius: _radius,
205 1 : child: GestureDetector(
206 1 : onPanStart: _onPanStart,
207 1 : onPanEnd: _onPanEnd,
208 1 : onPanUpdate: _onPanUpdate,
209 1 : child: BackdropFilter(
210 3 : filter: ui.ImageFilter.matrix(_matrix.storage,
211 : filterQuality: FilterQuality.none),
212 1 : child: Container(
213 1 : child: Center(
214 1 : child: Container(
215 : height: 1,
216 : width: 1,
217 : decoration: const BoxDecoration(
218 : color: Colors.grey, shape: BoxShape.circle),
219 : ),
220 : ),
221 2 : height: _magnifierSize.height,
222 2 : width: _magnifierSize.width,
223 1 : decoration: BoxDecoration(
224 1 : borderRadius: _radius,
225 1 : border: Border.all(color: Colors.grey, width: 3)),
226 : ),
227 : ),
228 : ),
229 : ),
230 : )
231 : ],
232 : );
233 : }
234 : }
|