Commit a9f1cb8c authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

More documentation (#10606)

- How do you handle a tap on text?
- Why is AnimatedOpacity expensive?
- Why would you use a gesture arena team?
...and other minor fixes
parent 9ac16680
......@@ -11,6 +11,20 @@ import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
class LinkTextSpan extends TextSpan {
// Beware!
//
// This class is only safe because the TapGestureRecognizer is not
// given a deadline and therefore never allocates any resources.
//
// In any other situation -- setting a deadline, using any of the less trivial
// recognizers, etc -- you would have to manage the gesture recognizer's
// lifetime and call dispose() when the TextSpan was no longer being rendered.
//
// Since TextSpan itself is @immutable, this means that you would have to
// manage the recognizer from outside the TextSpan, e.g. in the State of a
// stateful widget that then hands the recognizer to the TextSpan.
LinkTextSpan({ TextStyle style, String url, String text }) : super(
style: style,
text: text ?? url,
......
......@@ -92,7 +92,7 @@ abstract class MultiDragPointerState {
/// Called when the gesture was rejected.
///
/// [dispose()] will be called immediately following this.
/// The [dispose] method will be called immediately following this.
@protected
@mustCallSuper
void rejected() {
......
......@@ -147,7 +147,9 @@ abstract class OneSequenceGestureRecognizer extends GestureRecognizer {
/// is shortly after creating the recognizer.
GestureArenaTeam get team => _team;
GestureArenaTeam _team;
/// The [team] can only be set once.
set team(GestureArenaTeam value) {
assert(value != null);
assert(_entries.isEmpty);
assert(_trackedPointers.isEmpty);
assert(_team == null);
......
......@@ -80,15 +80,33 @@ class _CombiningGestureArenaMember extends GestureArenaMember {
}
}
/// A group of [GestureArenaMember] objects that are competing as a unit in the [GestureArenaManager].
/// A group of [GestureArenaMember] objects that are competing as a unit in the
/// [GestureArenaManager].
///
/// Normally, a recognizer competes directly in the [GestureArenaManager] to
/// recognize a sequence of pointer events as a gesture. With a
/// [GestureArenaTeam], recognizers can compete in the arena in a group with
/// other recognizers.
///
/// To assign a gesture recognizer to a team, see
/// [OneSequenceGestureRecognizer.team].
/// When gesture recognizers are in a team together, then once there are no
/// other competing gestures in the arena, the first gesture to have been added
/// to the team automatically wins, instead of the gestures continuing to
/// compete against each other.
///
/// For example, [Slider] uses this to support both a
/// [HorizontalDragGestureRecognizer] and a [TapGestureRecognizer], but without
/// the drag recognizer having to wait until the user has dragged outside the
/// slop region of the tap gesture before triggering. Since they compete as a
/// team, as soon as any other recognizers are out of the arena, the drag
/// recognizer wins, even if the user has not actually dragged yet. On the other
/// hand, if the tap can win outright, before the other recognizers are taken
/// out of the arena (e.g. if the slider is in a vertical scrolling list and the
/// user places their finger on the touch surface then lifts it, so that neither
/// the horizontal nor vertical drag recognizers can claim victory) the tap
/// recognizer still actually wins, despite being in the team.
///
/// To assign a gesture recognizer to a team, set
/// [OneSequenceGestureRecognizer.team] to an instance of [GestureArenaTeam].
class GestureArenaTeam {
final Map<int, _CombiningGestureArenaMember> _combiners = <int, _CombiningGestureArenaMember>{};
......
......@@ -11,13 +11,13 @@ import 'package:flutter/services.dart';
import 'basic_types.dart';
import 'text_style.dart';
// TODO(abarth): Should this be somewhere more general?
// TODO(ianh): This should be on List itself.
bool _deepEquals(List<Object> a, List<Object> b) {
if (a == null)
return b == null;
if (b == null || a.length != b.length)
return false;
for (int i = 0; i < a.length; ++i) {
for (int i = 0; i < a.length; i += 1) {
if (a[i] != b[i])
return false;
}
......@@ -40,11 +40,25 @@ bool _deepEquals(List<Object> a, List<Object> b) {
/// span in a widget, use a [RichText]. For text with a single style, consider
/// using the [Text] widget.
///
/// ## Sample code
///
/// The text "Hello world!", in black:
///
/// ```dart
/// new TextSpan(
/// text: 'Hello world!',
/// style: new TextStyle(color: Colors.black),
/// )
/// ```
///
/// _There is some more detailed sample code in the documentation for the
/// [recognizer] property._
///
/// See also:
///
/// * [Text]
/// * [RichText]
/// * [TextPainter]
/// * [Text], a widget for showing uniformly-styled text.
/// * [RichText], a widget for finer control of text rendering.
/// * [TextPainter], a class for painting [TextSpan] objects on a [Canvas].
@immutable
class TextSpan {
/// Creates a [TextSpan] with the given values.
......@@ -55,7 +69,7 @@ class TextSpan {
this.style,
this.text,
this.children,
this.recognizer
this.recognizer,
});
/// The style to apply to the [text] and the [children].
......@@ -80,11 +94,75 @@ class TextSpan {
/// A gesture recognizer that will receive events that hit this text span.
///
/// [TextSpan] itself does not implement hit testing or event
/// dispatch. The owner of the [TextSpan] tree to which the object
/// belongs is responsible for dispatching events.
/// [TextSpan] itself does not implement hit testing or event dispatch. The
/// object that manages the [TextSpan] painting is also responsible for
/// dispatching events. In the rendering library, that is the
/// [RenderParagraph] object, which corresponds to the [RichText] widget in
/// the widgets layer.
///
/// [TextSpan] also does not manage the lifetime of the gesture recognizer.
/// The code that owns the [GestureRecognizer] object must call
/// [GestureRecognizer.dispose] when the [TextSpan] object is no longer used.
///
/// ## Sample code
///
/// This example shows how to manage the lifetime of a gesture recognizer
/// provided to a [TextSpan] object. It defines a [BuzzingText] widget which
/// uses the [HapticFeedback] class to vibrate the device when the user
/// long-presses the "find the" span, which is underlined in wavy green. The
/// hit-testing is handled by the [RichText] widget.
///
/// ```dart
/// class BuzzingText extends StatefulWidget {
/// @override
/// _BuzzingTextState createState() => new _BuzzingTextState();
/// }
///
/// class _BuzzingTextState extends State<BuzzingText> {
/// LongPressGestureRecognizer _longPressRecognizer;
///
/// @override
/// void initState() {
/// super.initState();
/// _longPressRecognizer = new LongPressGestureRecognizer()
/// ..onLongPress = _handlePress;
/// }
///
/// @override
/// void dispose() {
/// _longPressRecognizer.dispose();
/// super.dispose();
/// }
///
/// For an example, see [RenderParagraph] in the Flutter rendering library.
/// void _handlePress() {
/// HapticFeedback.vibrate();
/// }
///
/// @override
/// Widget build(BuildContext context) {
/// return new RichText(
/// text: new TextSpan(
/// text: 'Can you ',
/// style: new TextStyle(color: Colors.black),
/// children: <TextSpan>[
/// new TextSpan(
/// text: 'find the',
/// style: new TextStyle(
/// color: Colors.green,
/// decoration: TextDecoration.underline,
/// decorationStyle: TextDecorationStyle.wavy,
/// ),
/// recognizer: _longPressRecognizer,
/// ),
/// new TextSpan(
/// text: ' secret?',
/// ),
/// ],
/// ),
/// );
/// }
/// }
/// ```
final GestureRecognizer recognizer;
/// Apply the [style], [text], and [children] of this object to the
......@@ -111,7 +189,8 @@ class TextSpan {
builder.pop();
}
/// Walks this text span and its decendants in pre-order and calls [visitor] for each span that has text.
/// Walks this text span and its decendants in pre-order and calls [visitor]
/// for each span that has text.
bool visitTextSpan(bool visitor(TextSpan span)) {
if (text != null) {
if (!visitor(this))
......@@ -162,6 +241,7 @@ class TextSpan {
}
/// Returns the UTF-16 code unit at the given index in the flattened string.
///
/// Returns null if the index is out of bounds.
int codeUnitAt(int index) {
if (index < 0)
......@@ -208,8 +288,9 @@ class TextSpan {
/// valid configuration. Otherwise, returns true.
///
/// This is intended to be used as follows:
///
/// ```dart
/// assert(myTextSpan.debugAssertIsValid());
/// assert(myTextSpan.debugAssertIsValid());
/// ```
bool debugAssertIsValid() {
assert(() {
......@@ -238,7 +319,7 @@ class TextSpan {
bool operator ==(dynamic other) {
if (identical(this, other))
return true;
if (other is! TextSpan)
if (other.runtimeType != runtimeType)
return false;
final TextSpan typedOther = other;
return typedOther.text == text
......
......@@ -18,7 +18,7 @@ class HapticFeedback {
/// On iOS devices that support haptic feedback, this uses the default system
/// vibration value (`kSystemSoundID_Vibrate`).
///
/// On Android, this uses the platform haptic feedback API to simulates a
/// On Android, this uses the platform haptic feedback API to simulate a
/// short tap on a virtual keyboard.
static Future<Null> vibrate() async {
await SystemChannels.platform.invokeMethod('HapticFeedback.vibrate');
......
......@@ -3005,7 +3005,7 @@ class Flow extends MultiChildRenderObjectWidget {
/// which is less verbose and integrates with [DefaultTextStyle] for default
/// styling.
///
/// Example:
/// ## Sample code
///
/// ```dart
/// new RichText(
......@@ -3022,9 +3022,9 @@ class Flow extends MultiChildRenderObjectWidget {
///
/// See also:
///
/// * [Text]
/// * [TextSpan]
/// * [DefaultTextStyle]
/// * [TextSpan], which is used to describe the text in a paragraph.
/// * [Text], which automatically applies the ambient styles described by a
/// [DefaultTextStyle] to a single string.
class RichText extends LeafRenderObjectWidget {
/// Creates a paragraph of rich text.
///
......
......@@ -601,7 +601,8 @@ class _AnimatedPositionedState extends AnimatedWidgetBaseState<AnimatedPositione
/// Animated version of [Opacity] which automatically transitions the child's
/// opacity over a given duration whenever the given opacity changes.
///
/// Animating an opacity is relatively expensive.
/// Animating an opacity is relatively expensive because it requires painting
/// the child into an intermediate buffer.
class AnimatedOpacity extends ImplicitlyAnimatedWidget {
/// Creates a widget that animates its opacity implicitly.
///
......@@ -654,10 +655,10 @@ class _AnimatedOpacityState extends AnimatedWidgetBaseState<AnimatedOpacity> {
}
}
/// Animated version of [DefaultTextStyle] which automatically
/// transitions the default text style (the text style to apply to
/// descendant [Text] widgets without explicit style) over a given
/// duration whenever the given style changes.
/// Animated version of [DefaultTextStyle] which automatically transitions the
/// default text style (the text style to apply to descendant [Text] widgets
/// without explicit style) over a given duration whenever the given style
/// changes.
class AnimatedDefaultTextStyle extends ImplicitlyAnimatedWidget {
/// Creates a widget that animates the default text style implicitly.
///
......@@ -708,6 +709,15 @@ class _AnimatedDefaultTextStyleState extends AnimatedWidgetBaseState<AnimatedDef
}
/// Animated version of [PhysicalModel].
///
/// The [borderRadius] and [elevation] are animated.
///
/// The [color] is animated if the [animateColor] property is set; otherwise,
/// the color changes immediately at the start of the animation for the other
/// two properties. This allows the color to be animated independently (e.g.
/// because it is being driven by an [AnimatedTheme]).
///
/// The [shape] is not animated.
class AnimatedPhysicalModel extends ImplicitlyAnimatedWidget {
/// Creates a widget that animates the properties of a [PhysicalModel].
///
......
......@@ -139,10 +139,34 @@ class DefaultTextStyle extends InheritedWidget {
/// To display text that uses multiple styles (e.g., a paragraph with some bold
/// words), use [RichText].
///
/// ## Sample code
///
/// ```dart
/// new Text(
/// 'Hello, $name! How are you?',
/// textAlign: TextAlign.center,
/// overflow: TextOverflow.ellipsis,
/// style: new TextStyle(fontWeight: FontWeight.bold),
/// )
/// ```
///
/// ## Interactivity
///
/// To make [Text] react to touch events, wrap it in a [GestureDetector] widget
/// with a [GestureDetector.onTap] handler.
///
/// In a material design application, consider using a [FlatButton] instead, or
/// if that isn't appropriate, at least using an [InkWell] instead of
/// [GestureDetector].
///
/// To make sections of the text interactive, use [RichText] and specify a
/// [TapGestureRecognizer] as the [TextSpan.recognizer] of the relevant part of
/// the text.
///
/// See also:
///
/// * [RichText]
/// * [DefaultTextStyle]
/// * [RichText], which gives you more control over the text styles.
/// * [DefaultTextStyle], which sets default styles for [Text] widgets.
class Text extends StatelessWidget {
/// Creates a text widget.
///
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment