selection_area.dart 4.73 KB
Newer Older
1 2 3 4 5
// Copyright 2014 The Flutter 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 'package:flutter/cupertino.dart';
6
import 'package:flutter/rendering.dart';
7

8
import 'adaptive_text_selection_toolbar.dart';
9
import 'debug.dart';
10
import 'desktop_text_selection.dart';
11
import 'magnifier.dart';
12 13 14 15 16 17 18 19 20 21 22 23 24
import 'text_selection.dart';
import 'theme.dart';

/// A widget that introduces an area for user selections with adaptive selection
/// controls.
///
/// This widget creates a [SelectableRegion] with platform-adaptive selection
/// controls.
///
/// Flutter widgets are not selectable by default. To enable selection for
/// a specific screen, consider wrapping the body of the [Route] with a
/// [SelectionArea].
///
25 26 27 28
/// The [SelectionArea] widget must have a [Localizations] ancestor that
/// contains a [MaterialLocalizations] delegate; using the [MaterialApp] widget
/// ensures that such an ancestor is present.
///
29 30 31
/// {@tool dartpad}
/// This example shows how to make a screen selectable.
///
32
/// ** See code in examples/api/lib/material/selection_area/selection_area.0.dart **
33 34 35 36 37 38 39 40 41 42 43 44
/// {@end-tool}
///
/// See also:
///  * [SelectableRegion], which provides an overview of the selection system.
class SelectionArea extends StatefulWidget {
  /// Creates a [SelectionArea].
  ///
  /// If [selectionControls] is null, a platform specific one is used.
  const SelectionArea({
    super.key,
    this.focusNode,
    this.selectionControls,
45
    this.contextMenuBuilder = _defaultContextMenuBuilder,
46
    this.magnifierConfiguration,
47
    this.onSelectionChanged,
48 49 50
    required this.child,
  });

51
  /// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.intro}
52 53 54
  ///
  /// {@macro flutter.widgets.magnifier.intro}
  ///
55
  /// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.details}
56
  ///
57 58 59
  /// By default, builds a [CupertinoTextMagnifier] on iOS and [TextMagnifier]
  /// on Android, and builds nothing on all other platforms. If it is desired to
  /// suppress the magnifier, consider passing [TextMagnifierConfiguration.disabled].
60 61
  final TextMagnifierConfiguration? magnifierConfiguration;

62 63 64 65 66 67 68 69
  /// {@macro flutter.widgets.Focus.focusNode}
  final FocusNode? focusNode;

  /// The delegate to build the selection handles and toolbar.
  ///
  /// If it is null, the platform specific selection control is used.
  final TextSelectionControls? selectionControls;

70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
  /// {@macro flutter.widgets.EditableText.contextMenuBuilder}
  ///
  /// If not provided, will build a default menu based on the ambient
  /// [ThemeData.platform].
  ///
  /// {@tool dartpad}
  /// This example shows how to build a custom context menu for any selected
  /// content in a SelectionArea.
  ///
  /// ** See code in examples/api/lib/material/context_menu/selectable_region_toolbar_builder.0.dart **
  /// {@end-tool}
  ///
  /// See also:
  ///
  ///  * [AdaptiveTextSelectionToolbar], which is built by default.
  final SelectableRegionContextMenuBuilder? contextMenuBuilder;

87 88 89
  /// Called when the selected content changes.
  final ValueChanged<SelectedContent?>? onSelectionChanged;

90 91 92 93 94
  /// The child widget this selection area applies to.
  ///
  /// {@macro flutter.widgets.ProxyWidget.child}
  final Widget child;

95 96 97 98 99 100
  static Widget _defaultContextMenuBuilder(BuildContext context, SelectableRegionState selectableRegionState) {
    return AdaptiveTextSelectionToolbar.selectableRegion(
      selectableRegionState: selectableRegionState,
    );
  }

101 102 103 104 105
  @override
  State<StatefulWidget> createState() => _SelectionAreaState();
}

class _SelectionAreaState extends State<SelectionArea> {
106
  FocusNode get _effectiveFocusNode => widget.focusNode ?? (_internalNode ??= FocusNode());
107 108 109 110 111 112 113 114 115 116
  FocusNode? _internalNode;

  @override
  void dispose() {
    _internalNode?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
117
    assert(debugCheckHasMaterialLocalizations(context));
118 119 120 121 122 123
    final TextSelectionControls controls = widget.selectionControls ?? switch (Theme.of(context).platform) {
      TargetPlatform.android || TargetPlatform.fuchsia => materialTextSelectionHandleControls,
      TargetPlatform.linux || TargetPlatform.windows   => desktopTextSelectionHandleControls,
      TargetPlatform.iOS                               => cupertinoTextSelectionHandleControls,
      TargetPlatform.macOS                             => cupertinoDesktopTextSelectionHandleControls,
    };
124 125
    return SelectableRegion(
      selectionControls: controls,
126 127
      focusNode: _effectiveFocusNode,
      contextMenuBuilder: widget.contextMenuBuilder,
128
      magnifierConfiguration: widget.magnifierConfiguration ?? TextMagnifier.adaptiveMagnifierConfiguration,
129
      onSelectionChanged: widget.onSelectionChanged,
130 131 132 133
      child: widget.child,
    );
  }
}