Flutter 如何在“文本选择”工具栏中添加新选项
我想在文本选择工具栏中添加一个新选项,这是经典剪切、复制、粘贴和selectAll之外的一个额外选项。 我使用了SelectableText,但它的工具栏选项只允许激活/取消激活经典选项,而不是创建新选项。所以我尝试使用EditableText并创建我自己的text\u selection.dart复制 我发现调用我的文本选择类时出错,因为工具栏没有显示,也没有任何错误消息。 这是我的小部件,它使用我的文本选择类Flutter 如何在“文本选择”工具栏中添加新选项,flutter,textselection,Flutter,Textselection,我想在文本选择工具栏中添加一个新选项,这是经典剪切、复制、粘贴和selectAll之外的一个额外选项。 我使用了SelectableText,但它的工具栏选项只允许激活/取消激活经典选项,而不是创建新选项。所以我尝试使用EditableText并创建我自己的text\u selection.dart复制 我发现调用我的文本选择类时出错,因为工具栏没有显示,也没有任何错误消息。 这是我的小部件,它使用我的文本选择类 import 'package:flutter/cupertino.dart'
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'my_text_selection_controls.dart';
class PageContent extends StatefulWidget {
final String page = 'a text just for testing';
PageContent();
@override
_PageContentState createState() => _PageContentState();
}
class _PageContentState extends State<PageContent> {
var textController = new TextEditingController();
FocusNode _textfieldFocusNode;
@override
void initState(){
super.initState();
_textfieldFocusNode = FocusNode();
textController.text = widget.page;
}
@override
void dispose() {
_textfieldFocusNode.dispose();
textController.dispose();
super.dispose();
}
void screenTapped(){
print('calling a callback');
}
@override
Widget build(BuildContext context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: GestureDetector(
onTap: () => screenTapped(),
child: Container(
alignment: Alignment.center,
child:
EditableText(
focusNode: _textfieldFocusNode,
controller: textController,
backgroundCursorColor: Colors.lightGreen,
selectionColor: Colors.blue,
style: TextStyle(color: Colors.black, fontSize: 17),
cursorColor: Colors.blue,
textInputAction: TextInputAction.newline,
maxLines: null,
enableInteractiveSelection: true,
selectionControls: mymaterialTextSelectionControls, //USING MY TEXT SELECTION CLASS
),
color: Color(0xfffdf5e6),
)
),
),
);
}
}
import'包装:flift/cupertino.dart';
进口“包装:颤振/材料.省道”;
导入'my_text_selection_controls.dart';
类PageContent扩展StatefulWidget{
最终字符串页='仅用于测试的文本';
页面内容();
@凌驾
_PageContentState createState()=>\u PageContentState();
}
类_PageContentState扩展状态{
var textController=新的TextEditingController();
FocusNode _textfieldFocusNode;
@凌驾
void initState(){
super.initState();
_textfieldFocusNode=FocusNode();
textController.text=widget.page;
}
@凌驾
无效处置(){
_textfieldFocusNode.dispose();
textController.dispose();
super.dispose();
}
void screenstapped(){
打印(“调用回调”);
}
@凌驾
小部件构建(构建上下文){
扩大回报(
孩子:填充(
填充:常数边集全部(10.0),
儿童:手势检测器(
onTap:()=>屏幕点击(),
子:容器(
对齐:对齐.center,
儿童:
可编辑文本(
focusNode:_textfieldFocusNode,
控制器:textController,
背景光标颜色:颜色。浅绿色,
选择颜色:Colors.blue,
样式:TextStyle(颜色:Colors.black,字体大小:17),
光标颜色:颜色。蓝色,
textInputAction:textInputAction.newline,
maxLines:null,
enableInteractiveSelection:true,
selectionControls:mymaterialTextSelectionControls,//使用我的文本选择类
),
颜色:颜色(0xfffdf5e6),
)
),
),
);
}
}
这是我的课文选择课。只是材质类的副本
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/rendering.dart';
const double _kHandleSize = 22.0;
// Minimal padding from all edges of the selection toolbar to all edges of the
// viewport.
const double _kToolbarScreenPadding = 8.0;
const double _kToolbarHeight = 44.0;
// Padding when positioning toolbar below selection.
const double _kToolbarContentDistanceBelow = 16.0;
const double _kToolbarContentDistance = 8.0;
/// Manages a copy/paste text selection toolbar.
class _TextSelectionToolbar extends StatelessWidget {
const _TextSelectionToolbar({
Key key,
this.handleCut,
this.handleCopy,
this.handlePaste,
this.handleSelectAll,
}) : super(key: key);
final VoidCallback handleCut;
final VoidCallback handleCopy;
final VoidCallback handlePaste;
final VoidCallback handleSelectAll;
@override
Widget build(BuildContext context) {
final MaterialLocalizations localizations = MaterialLocalizations.of(context);
final List<Widget> items = <Widget>[
if (handleCut != null) FlatButton(child: Text(localizations.cutButtonLabel), onPressed: handleCut),
if (handleCopy != null) FlatButton(child: Text(localizations.copyButtonLabel), onPressed: handleCopy),
if (handlePaste != null) FlatButton(child: Text(localizations.pasteButtonLabel), onPressed: handlePaste),
if (handleSelectAll != null) FlatButton(child: Text(localizations.selectAllButtonLabel), onPressed: handleSelectAll),
];
// If there is no option available, build an empty widget.
if (items.isEmpty) {
return Container(width: 0.0, height: 0.0);
}
return Material(
elevation: 1.0,
child: Container(
height: _kToolbarHeight,
child: Row(mainAxisSize: MainAxisSize.min, children: items),
),
);
}
}
/// Centers the toolbar around the given position, ensuring that it remains on
/// screen.
class _TextSelectionToolbarLayout extends SingleChildLayoutDelegate {
_TextSelectionToolbarLayout(this.screenSize, this.globalEditableRegion, this.position);
/// The size of the screen at the time that the toolbar was last laid out.
final Size screenSize;
/// Size and position of the editing region at the time the toolbar was last
/// laid out, in global coordinates.
final Rect globalEditableRegion;
/// Anchor position of the toolbar, relative to the top left of the
/// [globalEditableRegion].
final Offset position;
@override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
return constraints.loosen();
}
@override
Offset getPositionForChild(Size size, Size childSize) {
final Offset globalPosition = globalEditableRegion.topLeft + position;
double x = globalPosition.dx - childSize.width / 2.0;
double y = globalPosition.dy - childSize.height;
if (x < _kToolbarScreenPadding)
x = _kToolbarScreenPadding;
else if (x + childSize.width > screenSize.width - _kToolbarScreenPadding)
x = screenSize.width - childSize.width - _kToolbarScreenPadding;
if (y < _kToolbarScreenPadding)
y = _kToolbarScreenPadding;
else if (y + childSize.height > screenSize.height - _kToolbarScreenPadding)
y = screenSize.height - childSize.height - _kToolbarScreenPadding;
return Offset(x, y);
}
@override
bool shouldRelayout(_TextSelectionToolbarLayout oldDelegate) {
return position != oldDelegate.position;
}
}
/// Draws a single text selection handle which points up and to the left.
class _TextSelectionHandlePainter extends CustomPainter {
_TextSelectionHandlePainter({ this.color });
final Color color;
@override
void paint(Canvas canvas, Size size) {
final Paint paint = Paint()..color = color;
final double radius = size.width/2.0;
canvas.drawCircle(Offset(radius, radius), radius, paint);
canvas.drawRect(Rect.fromLTWH(0.0, 0.0, radius, radius), paint);
}
@override
bool shouldRepaint(_TextSelectionHandlePainter oldPainter) {
return color != oldPainter.color;
}
}
class _MaterialTextSelectionControls extends TextSelectionControls {
/// Returns the size of the Material handle.
@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 position,
List<TextSelectionPoint> endpoints,
TextSelectionDelegate delegate,
) {
assert(debugCheckHasMediaQuery(context));
assert(debugCheckHasMaterialLocalizations(context));
// The toolbar should appear below the TextField
// when there is not enough space above the TextField to show it.
final TextSelectionPoint startTextSelectionPoint = endpoints[0];
final double toolbarHeightNeeded = MediaQuery.of(context).padding.top
+ _kToolbarScreenPadding
+ _kToolbarHeight
+ _kToolbarContentDistance;
final double availableHeight = globalEditableRegion.top + endpoints.first.point.dy - textLineHeight;
final bool fitsAbove = toolbarHeightNeeded <= availableHeight;
final double y = fitsAbove
? startTextSelectionPoint.point.dy - _kToolbarContentDistance - textLineHeight
: startTextSelectionPoint.point.dy + _kToolbarHeight + _kToolbarContentDistanceBelow;
final Offset preciseMidpoint = Offset(position.dx, y);
return ConstrainedBox(
constraints: BoxConstraints.tight(globalEditableRegion.size),
child: CustomSingleChildLayout(
delegate: _TextSelectionToolbarLayout(
MediaQuery.of(context).size,
globalEditableRegion,
preciseMidpoint,
),
child: _TextSelectionToolbar(
handleCut: canCut(delegate) ? () => handleCut(delegate) : null,
handleCopy: canCopy(delegate) ? () => handleCopy(delegate) : null,
handlePaste: canPaste(delegate) ? () => handlePaste(delegate) : null,
handleSelectAll: canSelectAll(delegate) ? () => handleSelectAll(delegate) : null,
),
),
);
}
/// Builder for material-style text selection handles.
@override
Widget buildHandle(BuildContext context, TextSelectionHandleType type, double textHeight) {
final Widget handle = SizedBox(
width: _kHandleSize,
height: _kHandleSize,
child: CustomPaint(
painter: _TextSelectionHandlePainter(
color: Theme.of(context).textSelectionHandleColor
),
),
);
// [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 / 2.0,
child: handle,
);
case TextSelectionHandleType.right: // points up-left
return handle;
case TextSelectionHandleType.collapsed: // points up
return Transform.rotate(
angle: math.pi / 4.0,
child: handle,
);
}
assert(type != null);
return null;
}
/// Gets anchor for material-style text selection handles.
///
/// See [TextSelectionControls.getHandleAnchor].
@override
Offset getHandleAnchor(TextSelectionHandleType type, double textLineHeight) {
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);
}
}
/// Text selection controls that follow the Material Design specification.
final TextSelectionControls mymaterialTextSelectionControls = _MaterialTextSelectionControls();
//Chromium作者版权所有。版权所有。
//此源代码的使用受BSD样式许可证的约束,该许可证可以
//在许可证文件中找到。
导入'dart:math'作为数学;
进口“包装:颤振/材料.省道”;
导入“package:flatter/widgets.dart”;
导入“package:flatter/rendering.dart”;
常数double _kHandleSize=22.0;
//从“选择”工具栏的所有边缘到工具栏的所有边缘的最小填充
//视口。
常数double ktoolbarscreenpaidding=8.0;
const double kToolbarHeight=44.0;
//将工具栏定位在所选内容下方时填充。
const double ktoolbarcontentdistance低于=16.0;
const double kToolbarContentDistance=8.0;
///管理复制/粘贴文本选择工具栏。
类TextSelectionToolbar扩展了无状态小部件{
常量文本选择工具栏({
关键点,
这是我的手稿,
这个手镜,
这个手帕,
这是我的选择,
}):super(key:key);
最终手切;
最终手检;
最后的手钉;
最终无效回调句柄selectall;
@凌驾
小部件构建(构建上下文){
最终材料本地化=材料本地化(上下文);
最终清单项目=[
如果(handleCut!=null)FlatButton(子项:Text(localizations.cutButtonLabel)),则按On:handleCut,
如果(handleCopy!=null)FlatButton(子项:Text(localizations.copyButtonLabel),则ON按下:handleCopy),
if(handlePaste!=null)FlatButton(子项:Text(localizations.pasteButtonLabel),on按下:handlePaste),
如果(handleSelectAll!=null)扁平按钮(子项:文本(本地化。selectAllButtonLabel),按下时:handleSelectAll),
];
//如果没有可用的选项,则构建一个空的小部件。
如果(items.isEmpty){
返回容器(宽度:0.0,高度:0.0);
}
退货(
标高:1.0,
子:容器(
高度:kToolbarHeight,
子项:行(mainAxisSize:mainAxisSize.min,子项:项),
),
);
}
}
///将工具栏围绕给定位置居中,确保其保持打开状态
///屏幕。
类_TextSelectionToolbarLayout扩展了SingleChildLayoutDelegate{
_text选择工具栏布局(this.screenSize、this.globalEditableRegion、this.position);
///上次布置工具栏时屏幕的大小。
最终尺寸屏幕尺寸;
///上次使用工具栏时编辑区域的大小和位置
///在全球坐标系中布局。
最终矩形全局可编辑区域;
///相对于工具栏左上角的工具栏定位位置
///[全球可编辑区域]。
最终偏移位置;
@凌驾
BoxConstraints getConstraintsForChild(BoxConstraints约束){
返回约束;
}
@凌驾
偏移量getPositionForChild(大小,大小childSize){
最终偏移全局位置=全局可编辑区域。左上角+位置;
double x=globalPosition.dx-childSize.width/2.0;
双y=全局位置.dy-儿童尺寸.height;
如果(x<\u ktoolbarscreenpaidding)
x=ktoolbarscreenpaidding;
else if(x+childSize.width>screenSize.width-ktoolbarScreenPaidding)
x=screenSize.width-childSize.width-工具栏屏幕填充;
如果(y<\u ktoolbar屏幕填充)
y=ktoolbar屏幕填充;
否则如果(y+childSize.height>screenSize.height-ktoolbarScreenPaidding)
y=screenSize.height-childSize.height-工具栏屏幕填充;
返回偏移量(x,y);
}
@凌驾
布尔应该重新显示(_TextSelectionTool