my_extended_text_selection_controls.dart
10.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:url_launcher/url_launcher.dart';
///
/// create by zmtzawqlp on 2019/8/3
///
///
///
typedef MyCustomCallBack = void Function(Object object);
const double _kHandleSize = 22.0;
// Padding between the toolbar and the anchor.
const double _kToolbarContentDistanceBelow = _kHandleSize - 2.0;
const double _kToolbarContentDistance = 8.0;
/// Android Material styled text selection controls.
class MyTextSelectionControls extends TextSelectionControls {
/// Returns the size of the Material handle.
///
///
final MyCustomCallBack callback;
MyTextSelectionControls({this.callback});
@override
Size getHandleSize(double textLineHeight) =>
const Size(_kHandleSize, _kHandleSize);
/// Builder for material-style copy/paste text selection toolbar.
@override
Widget buildToolbar(
BuildContext context,
Rect globalEditableRegion,
double textLineHeight,
Offset selectionMidpoint,
List<TextSelectionPoint> endpoints,
TextSelectionDelegate delegate,
ClipboardStatusNotifier clipboardStatus,
Offset lastSecondaryTapDownPosition,
) {
return _TextSelectionControlsToolbar(
globalEditableRegion: globalEditableRegion,
textLineHeight: textLineHeight,
selectionMidpoint: selectionMidpoint,
endpoints: endpoints,
delegate: delegate,
clipboardStatus: clipboardStatus,
handleCut: canCut(delegate) ? () => handleCut(delegate, null) : null,
handleCopy: canCopy(delegate) ? () => handleCopy(delegate) : null,
handlePaste: () => null,
// handlePaste: canPaste(delegate) ? () => handlePaste(delegate) : null,
handleSelectAll:
canSelectAll(delegate) ? () => handleSelectAll(delegate) : null,
handleLike: () async {
print('dkjfkdfjk');
var clipboardData =
await Clipboard.getData(Clipboard.kTextPlain); //获取粘贴板中的文本
if (clipboardData != null) {
print(clipboardData.text); //打印内容
if (callback != null) {
callback(clipboardData.text);
}
} else {
if (callback != null) {
callback('');
}
}
delegate.hideToolbar();
// delegate.userUpdateTextEditingValue(
// delegate.textEditingValue
// .copyWith(selection: const TextSelection.collapsed(offset: 0)),
// SelectionChangedCause.toolbar,
// );
// launch(
// 'mailto:zmtzawqlp@live.com?subject=extended_text_share&body=${delegate.textEditingValue.text}');
// delegate.hideToolbar();
// delegate.userUpdateTextEditingValue(
// delegate.textEditingValue
// .copyWith(selection: const TextSelection.collapsed(offset: 0)),
// SelectionChangedCause.toolbar,
// );
},
);
}
/// Builder for material-style text selection handles.
@override
Widget buildHandle(
BuildContext context, TextSelectionHandleType type, double textLineHeight,
[VoidCallback onTap, double startGlyphHeight, double endGlyphHeight]) {
final Widget handle = SizedBox(
// width: _kHandleSize,
// height: _kHandleSize,
// child: Image.asset(
// 'assets/add_store.png',
// ),
);
// [handle] is a circle, with a rectangle in the top left quadrant of that
// circle (an onion pointing to 10:30). We rotate [handle] to point
// straight up or up-right depending on the handle type.
switch (type) {
case TextSelectionHandleType.left: // points up-right
return Transform.rotate(
angle: math.pi / 4.0,
child: handle,
);
case TextSelectionHandleType.right: // points up-left
return Transform.rotate(
angle: -math.pi / 4.0,
child: handle,
);
case TextSelectionHandleType.collapsed: // points up
return handle;
}
}
/// Gets anchor for material-style text selection handles.
///
/// See [TextSelectionControls.getHandleAnchor].
@override
Offset getHandleAnchor(TextSelectionHandleType type, double textLineHeight,
[double startGlyphHeight, double endGlyphHeight]) {
switch (type) {
case TextSelectionHandleType.left:
return const Offset(_kHandleSize, 0);
case TextSelectionHandleType.right:
return Offset.zero;
default:
return const Offset(_kHandleSize / 2, -4);
}
}
@override
bool canSelectAll(TextSelectionDelegate delegate) {
// Android allows SelectAll when selection is not collapsed, unless
// everything has already been selected.
final TextEditingValue value = delegate.textEditingValue;
return delegate.selectAllEnabled &&
value.text.isNotEmpty &&
!(value.selection.start == 0 &&
value.selection.end == value.text.length);
}
// @override
// bool canPaste(TextSelectionDelegate delegate) {
// // Android allows SelectAll when selection is not collapsed, unless
// // everything has already been selected.
// delegate.pasteEnabled
// final TextEditingValue value = delegate.textEditingValue;
// return delegate.selectAllEnabled &&
// value.text.isNotEmpty &&
// !(value.selection.start == 0 &&
// value.selection.end == value.text.length);
// }
}
// The label and callback for the available default text selection menu buttons.
class _TextSelectionToolbarItemData {
const _TextSelectionToolbarItemData({
this.label,
this.onPressed,
});
final String label;
final VoidCallback onPressed;
}
// The highest level toolbar widget, built directly by buildToolbar.
class _TextSelectionControlsToolbar extends StatefulWidget {
const _TextSelectionControlsToolbar({
this.clipboardStatus,
this.delegate,
this.endpoints,
this.globalEditableRegion,
this.handleCut,
this.handleCopy,
this.handlePaste,
this.handleSelectAll,
this.selectionMidpoint,
this.textLineHeight,
this.handleLike,
});
final ClipboardStatusNotifier clipboardStatus;
final TextSelectionDelegate delegate;
final List<TextSelectionPoint> endpoints;
final Rect globalEditableRegion;
final VoidCallback handleCut;
final VoidCallback handleCopy;
final VoidCallback handlePaste;
final VoidCallback handleSelectAll;
final VoidCallback handleLike;
final Offset selectionMidpoint;
final double textLineHeight;
@override
_TextSelectionControlsToolbarState createState() =>
_TextSelectionControlsToolbarState();
}
class _TextSelectionControlsToolbarState
extends State<_TextSelectionControlsToolbar> with TickerProviderStateMixin {
void _onChangedClipboardStatus() {
setState(() {
// Inform the widget that the value of clipboardStatus has changed.
});
}
@override
void initState() {
super.initState();
widget.clipboardStatus?.addListener(_onChangedClipboardStatus);
}
@override
void didUpdateWidget(_TextSelectionControlsToolbar oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.clipboardStatus != oldWidget.clipboardStatus) {
widget.clipboardStatus?.addListener(_onChangedClipboardStatus);
oldWidget.clipboardStatus?.removeListener(_onChangedClipboardStatus);
}
}
@override
void dispose() {
super.dispose();
widget.clipboardStatus?.removeListener(_onChangedClipboardStatus);
}
@override
Widget build(BuildContext context) {
// If there are no buttons to be shown, don't render anything.
if (widget.handleCut == null &&
widget.handleCopy == null &&
widget.handlePaste == null &&
widget.handleSelectAll == null) {
return const SizedBox.shrink();
}
// If the paste button is desired, don't render anything until the state of
// the clipboard is known, since it's used to determine if paste is shown.
if (widget.handlePaste != null &&
widget.clipboardStatus?.value == ClipboardStatus.unknown) {
return const SizedBox.shrink();
}
// Calculate the positioning of the menu. It is placed above the selection
// if there is enough room, or otherwise below.
final TextSelectionPoint startTextSelectionPoint = widget.endpoints[0];
final TextSelectionPoint endTextSelectionPoint =
widget.endpoints.length > 1 ? widget.endpoints[1] : widget.endpoints[0];
final Offset anchorAbove = Offset(
widget.globalEditableRegion.left + widget.selectionMidpoint.dx,
widget.globalEditableRegion.top +
startTextSelectionPoint.point.dy -
widget.textLineHeight -
_kToolbarContentDistance,
);
final Offset anchorBelow = Offset(
widget.globalEditableRegion.left + widget.selectionMidpoint.dx,
widget.globalEditableRegion.top +
endTextSelectionPoint.point.dy +
_kToolbarContentDistanceBelow,
);
// Determine which buttons will appear so that the order and total number is
// known. A button's position in the menu can slightly affect its
// appearance.
assert(debugCheckHasMaterialLocalizations(context));
final MaterialLocalizations localizations =
MaterialLocalizations.of(context);
final List<_TextSelectionToolbarItemData> itemDatas =
<_TextSelectionToolbarItemData>[
if (widget.handleCut != null)
_TextSelectionToolbarItemData(
label: localizations.cutButtonLabel,
onPressed: widget.handleCut,
),
if (widget.handleCopy != null)
_TextSelectionToolbarItemData(
label: localizations.copyButtonLabel,
onPressed: widget.handleCopy,
),
// if (widget.handlePaste != null &&
// widget.clipboardStatus?.value == ClipboardStatus.pasteable)
// _TextSelectionToolbarItemData(
// label: localizations.pasteButtonLabel,
// onPressed: widget.handlePaste,
// ),
if (widget.handleSelectAll != null)
_TextSelectionToolbarItemData(
label: localizations.selectAllButtonLabel,
onPressed: widget.handleSelectAll,
),
_TextSelectionToolbarItemData(
label: '粘贴',
onPressed: widget.handleLike,
),
];
// If there is no option available, build an empty widget.
if (itemDatas.isEmpty) {
return const SizedBox(width: 0.0, height: 0.0);
}
return TextSelectionToolbar(
anchorAbove: anchorAbove,
anchorBelow: anchorBelow,
children: itemDatas
.asMap()
.entries
.map((MapEntry<int, _TextSelectionToolbarItemData> entry) {
return TextSelectionToolbarTextButton(
padding: TextSelectionToolbarTextButton.getPadding(
entry.key, itemDatas.length),
onPressed: entry.value.onPressed,
child: Text(entry.value.label),
);
}).toList(),
);
}
}