Skip to content
2 changes: 2 additions & 0 deletions lib/auto_size_text.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ library auto_size_text;

import 'dart:async';

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart' show SelectableText;
import 'package:flutter/widgets.dart';

part 'src/auto_size_text.dart';
Expand Down
270 changes: 268 additions & 2 deletions lib/src/auto_size_text.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,21 @@ class AutoSizeText extends StatefulWidget {
this.maxLines,
this.semanticsLabel,
}) : textSpan = null,
autofocus = false,
showCursor = false,
cursorWidth = 2.0,
cursorHeight = null,
cursorRadius = null,
focusNode = null,
cursorColor = null,
enableInteractiveSelection = false,
selectionControls = null,
dragStartBehavior = DragStartBehavior.start,
toolbarOptions = null,
onTap = null,
scrollPhysics = null,
onSelectionChanged = null,
_isSelectableText = false,
super(key: key);

/// Creates a [AutoSizeText] widget with a [TextSpan].
Expand All @@ -58,6 +73,108 @@ class AutoSizeText extends StatefulWidget {
this.maxLines,
this.semanticsLabel,
}) : data = null,
autofocus = false,
showCursor = false,
cursorWidth = 2.0,
cursorHeight = null,
cursorRadius = null,
focusNode = null,
cursorColor = null,
enableInteractiveSelection = false,
selectionControls = null,
dragStartBehavior = DragStartBehavior.start,
toolbarOptions = null,
onTap = null,
scrollPhysics = null,
onSelectionChanged = null,
_isSelectableText = false,
super(key: key);

/// Creates a selectable [AutoSizeText] widget with a [TextSpan].
const AutoSizeText.richSelectable(
this.textSpan, {
Key? key,
this.textKey,
this.style,
this.strutStyle,
this.minFontSize = 12,
this.maxFontSize = double.infinity,
this.stepGranularity = 1,
this.presetFontSizes,
this.group,
this.textAlign,
this.textDirection,
this.wrapWords = true,
this.textScaleFactor,
this.maxLines,
this.autofocus = false,
this.showCursor = false,
this.cursorWidth = 2.0,
this.cursorHeight,
this.cursorRadius,
this.focusNode,
this.cursorColor,
this.enableInteractiveSelection = true,
this.selectionControls,
this.dragStartBehavior = DragStartBehavior.start,
this.toolbarOptions,
this.onTap,
this.scrollPhysics,
this.onSelectionChanged,
}) : assert(textSpan != null,
'A non-null TextSpan must be provided to a AutoSizeText.rich widget.'),
data = null,
locale = null,
softWrap = null,
overflow = null,
overflowReplacement = null,
semanticsLabel = null,
_isSelectableText = true,
super(key: key);

/// Creates a selectable [AutoSizeText] widget.
///
/// If the [style] argument is null, the text will use the style from the
/// closest enclosing [DefaultTextStyle].
const AutoSizeText.selectable(
this.data, {
Key? key,
this.textKey,
this.style,
this.strutStyle,
this.minFontSize = 12,
this.maxFontSize = double.infinity,
this.stepGranularity = 1,
this.presetFontSizes,
this.group,
this.textAlign,
this.textDirection,
this.wrapWords = true,
this.textScaleFactor,
this.maxLines,
this.autofocus = false,
this.showCursor = false,
this.cursorWidth = 2.0,
this.cursorHeight,
this.cursorRadius,
this.focusNode,
this.cursorColor,
this.enableInteractiveSelection = true,
this.selectionControls,
this.dragStartBehavior = DragStartBehavior.start,
this.toolbarOptions,
this.onTap,
this.scrollPhysics,
this.onSelectionChanged,
}) : assert(data != null,
'A non-null String must be provided to a AutoSizeText widget.'),
textSpan = null,
locale = null,
softWrap = null,
overflow = null,
overflowReplacement = null,
semanticsLabel = null,
_isSelectableText = true,
super(key: key);

/// Sets the key for the resulting [Text] widget.
Expand Down Expand Up @@ -215,6 +332,99 @@ class AutoSizeText extends StatefulWidget {
/// ```
final String? semanticsLabel;

final bool _isSelectableText;

/// {@macro flutter.widgets.editableText.autofocus}
final bool autofocus;

/// The color to use when painting the cursor.
///
/// Defaults to the theme's `cursorColor` when null.
final Color? cursorColor;

/// {@macro flutter.widgets.editableText.cursorHeight}
final double? cursorHeight;

/// {@macro flutter.widgets.editableText.cursorRadius}
final Radius? cursorRadius;

/// {@macro flutter.widgets.editableText.cursorWidth}
final double cursorWidth;

/// {@macro flutter.widgets.scrollable.dragStartBehavior}
final DragStartBehavior dragStartBehavior;

/// Whether to enable user interface affordances for changing the text selection.
///
/// For example, setting this to true will enable features such as long-pressing the TextField to select text and show the cut/copy/paste menu, and tapping to move the text caret.
///
/// When this is false, the text selection cannot be adjusted by the user, text cannot be copied, and the user cannot paste into the text field from the clipboard.
final bool enableInteractiveSelection;

/// Defines the focus for this widget.
///
/// Text is only selectable when widget is focused.
///
/// The [focusNode] is a long-lived object that's typically managed by a
/// [StatefulWidget] parent. See [FocusNode] for more information.
///
/// To give the focus to this widget, provide a [focusNode] and then
/// use the current [FocusScope] to request the focus:
///
/// ```dart
/// FocusScope.of(context).requestFocus(myFocusNode);
/// ```
///
/// This happens automatically when the widget is tapped.
///
/// To be notified when the widget gains or loses the focus, add a listener
/// to the [focusNode]:
///
/// ```dart
/// focusNode.addListener(() { print(myFocusNode.hasFocus); });
/// ```
///
/// If null, this widget will create its own [FocusNode].
final FocusNode? focusNode;

/// {@macro flutter.widgets.editableText.onSelectionChanged}
final SelectionChangedCallback? onSelectionChanged;

/// Called when the user taps on this selectable text.
///
/// The selectable text builds a [GestureDetector] to handle input events like tap,
/// to trigger focus requests, to move the caret, adjust the selection, etc.
/// Handling some of those events by wrapping the selectable text with a competing
/// GestureDetector is problematic.
///
/// To unconditionally handle taps, without interfering with the selectable text's
/// internal gesture detector, provide this callback.
///
/// To be notified when the text field gains or loses the focus, provide a
/// [focusNode] and add a listener to that.
///
/// To listen to arbitrary pointer events without competing with the
/// selectable text's internal gesture detector, use a [Listener].
final GestureTapCallback? onTap;

/// The ScrollPhysics to use when vertically scrolling the input.
///
/// If not specified, it will behave according to the current platform.
final ScrollPhysics? scrollPhysics;

/// {@macro flutter.widgets.editableText.selectionControls}
final TextSelectionControls? selectionControls;

/// {@macro flutter.widgets.editableText.showCursor}
final bool showCursor;

/// Configuration of toolbar options.
///
/// Paste and cut will be disabled regardless.
///
/// If not set, select all and copy will be enabled by default.
final ToolbarOptions? toolbarOptions;

@override
_AutoSizeTextState createState() => _AutoSizeTextState();
}
Expand Down Expand Up @@ -276,8 +486,10 @@ class _AutoSizeTextState extends State<AutoSizeText> {
}

void _validateProperties(TextStyle style, int? maxLines) {
assert(widget.overflow == null || widget.overflowReplacement == null,
'Either overflow or overflowReplacement must be null.');
if (!widget._isSelectableText) {
assert(widget.overflow == null || widget.overflowReplacement == null,
'Either overflow or overflowReplacement must be null.');
}
assert(maxLines == null || maxLines > 0,
'MaxLines must be greater than or equal to 1.');
assert(widget.key == null || widget.key != widget.textKey,
Expand Down Expand Up @@ -411,6 +623,60 @@ class _AutoSizeTextState extends State<AutoSizeText> {
}

Widget _buildText(double fontSize, TextStyle style, int? maxLines) {
if (widget._isSelectableText) {
if (widget.data != null) {
return SelectableText(
widget.data!,
key: widget.textKey,
style: style.copyWith(fontSize: fontSize),
strutStyle: widget.strutStyle,
textAlign: widget.textAlign,
textDirection: widget.textDirection,
textScaleFactor: 1,
maxLines: maxLines,
autofocus: widget.autofocus,
cursorColor: widget.cursorColor,
cursorHeight: widget.cursorHeight,
cursorRadius: widget.cursorRadius,
cursorWidth: widget.cursorWidth,
dragStartBehavior: widget.dragStartBehavior,
enableInteractiveSelection: widget.enableInteractiveSelection,
focusNode: widget.focusNode,
onSelectionChanged: widget.onSelectionChanged,
onTap: widget.onTap,
scrollPhysics: widget.scrollPhysics,
selectionControls: widget.selectionControls,
showCursor: widget.showCursor,
toolbarOptions: widget.toolbarOptions,
);
} else {
return SelectableText.rich(
widget.textSpan!,
key: widget.textKey,
style: style,
strutStyle: widget.strutStyle,
textAlign: widget.textAlign,
textDirection: widget.textDirection,
textScaleFactor: fontSize / style.fontSize!,
maxLines: maxLines,
autofocus: widget.autofocus,
cursorColor: widget.cursorColor,
cursorHeight: widget.cursorHeight,
cursorRadius: widget.cursorRadius,
cursorWidth: widget.cursorWidth,
dragStartBehavior: widget.dragStartBehavior,
enableInteractiveSelection: widget.enableInteractiveSelection,
focusNode: widget.focusNode,
onSelectionChanged: widget.onSelectionChanged,
onTap: widget.onTap,
scrollPhysics: widget.scrollPhysics,
selectionControls: widget.selectionControls,
showCursor: widget.showCursor,
toolbarOptions: widget.toolbarOptions,
);
}
}

if (widget.data != null) {
return Text(
widget.data!,
Expand Down