Unverified Commit 4ceeca08 authored by Anthony Oleinik's avatar Anthony Oleinik Committed by GitHub

Added example for Magnifier and TextMagnifier (#110218)

parent dc2618bb
// 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/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({super.key});
static const Size loupeSize = Size(200, 200);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Offset dragGesturePositon = Offset.zero;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('Drag on the logo!'),
RepaintBoundary(
child: Stack(
children: <Widget>[
GestureDetector(
onPanUpdate: (DragUpdateDetails details) => setState(
() {
dragGesturePositon = details.localPosition;
},
),
child: const FlutterLogo(size: 200),
),
Positioned(
left: dragGesturePositon.dx,
top: dragGesturePositon.dy,
child: const RawMagnifier(
decoration: MagnifierDecoration(
shape: CircleBorder(
side: BorderSide(color: Colors.pink, width: 3),
),
),
size: Size(100, 100),
magnificationScale: 2,
),
)
],
),
),
],
),
),
),
);
}
}
// 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/foundation.dart';
import 'package:flutter/material.dart';
void main() => runApp(const MyApp(text: 'Hello world!'));
class MyApp extends StatelessWidget {
const MyApp({
super.key,
this.textDirection = TextDirection.ltr,
required this.text,
});
final TextDirection textDirection;
final String text;
static const Size loupeSize = Size(200, 200);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 48.0),
child: Center(
child: TextField(
textDirection: textDirection,
// Create a custom magnifier configuration that
// this `TextField` will use to build a magnifier with.
magnifierConfiguration: TextMagnifierConfiguration(
magnifierBuilder: (_, __, ValueNotifier<MagnifierInfo> magnifierInfo) => CustomMagnifier(
magnifierInfo: magnifierInfo,
),
),
controller: TextEditingController(text: text),
),
),
),
),
);
}
}
class CustomMagnifier extends StatelessWidget {
const CustomMagnifier({super.key, required this.magnifierInfo});
static const Size magnifierSize = Size(200, 200);
// This magnifier will consume some text data and position itself
// based on the info in the magnifier.
final ValueNotifier<MagnifierInfo> magnifierInfo;
@override
Widget build(BuildContext context) {
// Use a value listenable builder because we want to rebuild
// every time the text selection info changes.
// `CustomMagnifier` could also be a `StatefulWidget` and call `setState`
// when `magnifierInfo` updates. This would be useful for more complex
// positioning cases.
return ValueListenableBuilder<MagnifierInfo>(
valueListenable: magnifierInfo,
builder: (BuildContext context,
MagnifierInfo currentMagnifierInfo, _) {
// We want to position the magnifier at the global position of the gesture.
Offset magnifierPosition = currentMagnifierInfo.globalGesturePosition;
// You may use the `MagnifierInfo` however you'd like:
// In this case, we make sure the magnifier never goes out of the current line bounds.
magnifierPosition = Offset(
clampDouble(
magnifierPosition.dx,
currentMagnifierInfo.currentLineBoundaries.left,
currentMagnifierInfo.currentLineBoundaries.right,
),
clampDouble(
magnifierPosition.dy,
currentMagnifierInfo.currentLineBoundaries.top,
currentMagnifierInfo.currentLineBoundaries.bottom,
),
);
// Finally, align the magnifier to the bottom center. The inital anchor is
// the top left, so subtract bottom center alignment.
magnifierPosition -= Alignment.bottomCenter.alongSize(magnifierSize);
return Positioned(
left: magnifierPosition.dx,
top: magnifierPosition.dy,
child: RawMagnifier(
magnificationScale: 2,
// The focal point starts at the center of the magnifier.
// We probably want to point below the magnifier, so
// offset the focal point by half the magnifier height.
focalPointOffset: Offset(0, magnifierSize.height / 2),
// Decorate it however we'd like!
decoration: const MagnifierDecoration(
shape: StarBorder(
side: BorderSide(
color: Colors.green,
width: 2,
),
),
),
size: magnifierSize,
),
);
});
}
}
// 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/material.dart';
import 'package:flutter_api_samples/widgets/magnifier/magnifier.0.dart'
as example;
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('should update magnifier position on drag', (WidgetTester tester) async {
await tester.pumpWidget(const example.MyApp());
Matcher isPositionedAt(Offset at) {
return isA<Positioned>().having(
(Positioned positioned) => Offset(positioned.left!, positioned.top!),
'magnifier position',
at,
);
}
expect(
tester.widget(find.byType(Positioned)),
isPositionedAt(Offset.zero),
);
final Offset centerOfFlutterLogo = tester.getCenter(find.byType(Positioned));
final Offset topLeftOfFlutterLogo = tester.getTopLeft(find.byType(FlutterLogo));
const Offset dragDistance = Offset(10, 10);
await tester.dragFrom(centerOfFlutterLogo, dragDistance);
await tester.pump();
expect(
tester.widget(find.byType(Positioned)),
// Need to adjust by the topleft since the position is local.
isPositionedAt((centerOfFlutterLogo - topLeftOfFlutterLogo) + dragDistance),
);
});
testWidgets('should match golden', (WidgetTester tester) async {
await tester.pumpWidget(const example.MyApp());
final Offset centerOfFlutterLogo = tester.getCenter(find.byType(Positioned));
const Offset dragDistance = Offset(10, 10);
await tester.dragFrom(centerOfFlutterLogo, dragDistance);
await tester.pump();
await expectLater(
find.byType(RepaintBoundary).last,
matchesGoldenFile('magnifier.0_test.png'),
);
});
}
// 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/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_api_samples/widgets/text_magnifier/text_magnifier.0.dart'
as example;
import 'package:flutter_test/flutter_test.dart';
List<TextSelectionPoint> _globalize(
Iterable<TextSelectionPoint> points, RenderBox box) {
return points.map<TextSelectionPoint>((TextSelectionPoint point) {
return TextSelectionPoint(
box.localToGlobal(point.point),
point.direction,
);
}).toList();
}
RenderEditable _findRenderEditable<T extends State<StatefulWidget>>(WidgetTester tester) {
return (tester.state(find.byType(TextField))
as TextSelectionGestureDetectorBuilderDelegate)
.editableTextKey
.currentState!
.renderEditable;
}
Offset _textOffsetToPosition<T extends State<StatefulWidget>>(WidgetTester tester, int offset) {
final RenderEditable renderEditable = _findRenderEditable(tester);
final List<TextSelectionPoint> endpoints = renderEditable
.getEndpointsForSelection(
TextSelection.collapsed(offset: offset),
)
.map<TextSelectionPoint>((TextSelectionPoint point) => TextSelectionPoint(
renderEditable.localToGlobal(point.point),
point.direction,
))
.toList();
return endpoints[0].point + const Offset(0.0, -2.0);
}
void main() {
const Duration durationBetweenActons = Duration(milliseconds: 20);
const String defaultText = 'I am a magnifier, fear me!';
Future<void> showMagnifier(WidgetTester tester, String characterToTapOn) async {
final Offset tapOffset = _textOffsetToPosition(tester, defaultText.indexOf(characterToTapOn));
// Double tap 'Magnifier' word to show the selection handles.
final TestGesture testGesture = await tester.startGesture(tapOffset);
await tester.pump(durationBetweenActons);
await testGesture.up();
await tester.pump(durationBetweenActons);
await testGesture.down(tapOffset);
await tester.pump(durationBetweenActons);
await testGesture.up();
await tester.pumpAndSettle();
final TextSelection selection = tester
.firstWidget<TextField>(find.byType(TextField))
.controller!
.selection;
final RenderEditable renderEditable = _findRenderEditable(tester);
final List<TextSelectionPoint> endpoints = _globalize(
renderEditable.getEndpointsForSelection(selection),
renderEditable,
);
final Offset handlePos = endpoints.last.point + const Offset(10.0, 10.0);
final TestGesture gesture = await tester.startGesture(handlePos);
await gesture.moveTo(
_textOffsetToPosition(
tester,
defaultText.length - 2,
),
);
await tester.pump();
}
testWidgets('should show custom magnifier on drag', (WidgetTester tester) async {
await tester.pumpWidget(const example.MyApp(text: defaultText));
await showMagnifier(tester, 'e');
expect(find.byType(example.CustomMagnifier), findsOneWidget);
await expectLater(
find.byType(example.MyApp),
matchesGoldenFile('text_magnifier.0_test.png'),
);
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.android }));
for (final TextDirection textDirection in TextDirection.values) {
testWidgets('should show custom magnifier in $textDirection', (WidgetTester tester) async {
final String text = textDirection == TextDirection.rtl ? 'أثارت زر' : defaultText;
final String textToTapOn = textDirection == TextDirection.rtl ? 'ت' : 'e';
await tester.pumpWidget(example.MyApp(textDirection: textDirection, text: text));
await showMagnifier(tester, textToTapOn);
expect(find.byType(example.CustomMagnifier), findsOneWidget);
});
}
}
...@@ -9,7 +9,7 @@ import 'package:flutter/widgets.dart'; ...@@ -9,7 +9,7 @@ import 'package:flutter/widgets.dart';
/// finger may be blocking the point of interest, like a selection handle. /// finger may be blocking the point of interest, like a selection handle.
/// ///
/// Delegates styling to [CupertinoMagnifier] with its position depending on /// Delegates styling to [CupertinoMagnifier] with its position depending on
/// [magnifierOverlayInfoBearer]. /// [magnifierInfo].
/// ///
/// Specifically, the [CupertinoTextMagnifier] follows the following rules. /// Specifically, the [CupertinoTextMagnifier] follows the following rules.
/// [CupertinoTextMagnifier]: /// [CupertinoTextMagnifier]:
...@@ -21,7 +21,7 @@ import 'package:flutter/widgets.dart'; ...@@ -21,7 +21,7 @@ import 'package:flutter/widgets.dart';
/// then has vertical offset [dragResistance] * k. /// then has vertical offset [dragResistance] * k.
class CupertinoTextMagnifier extends StatefulWidget { class CupertinoTextMagnifier extends StatefulWidget {
/// Constructs a [RawMagnifier] in the Cupertino style, positioning with respect to /// Constructs a [RawMagnifier] in the Cupertino style, positioning with respect to
/// [magnifierOverlayInfoBearer]. /// [magnifierInfo].
/// ///
/// The default constructor parameters and constants were eyeballed on /// The default constructor parameters and constants were eyeballed on
/// an iPhone XR iOS v15.5. /// an iPhone XR iOS v15.5.
...@@ -32,7 +32,7 @@ class CupertinoTextMagnifier extends StatefulWidget { ...@@ -32,7 +32,7 @@ class CupertinoTextMagnifier extends StatefulWidget {
this.dragResistance = 10.0, this.dragResistance = 10.0,
this.hideBelowThreshold = 48.0, this.hideBelowThreshold = 48.0,
this.horizontalScreenEdgePadding = 10.0, this.horizontalScreenEdgePadding = 10.0,
required this.magnifierOverlayInfoBearer, required this.magnifierInfo,
}); });
/// The curve used for the in / out animations. /// The curve used for the in / out animations.
...@@ -63,9 +63,9 @@ class CupertinoTextMagnifier extends StatefulWidget { ...@@ -63,9 +63,9 @@ class CupertinoTextMagnifier extends StatefulWidget {
final double horizontalScreenEdgePadding; final double horizontalScreenEdgePadding;
/// [CupertinoTextMagnifier] will determine its own positioning /// [CupertinoTextMagnifier] will determine its own positioning
/// based on the [MagnifierOverlayInfoBearer] of this notifier. /// based on the [MagnifierInfo] of this notifier.
final ValueNotifier<MagnifierOverlayInfoBearer> final ValueNotifier<MagnifierInfo>
magnifierOverlayInfoBearer; magnifierInfo;
/// The duration that the magnifier drags behind its final position. /// The duration that the magnifier drags behind its final position.
static const Duration _kDragAnimationDuration = Duration(milliseconds: 45); static const Duration _kDragAnimationDuration = Duration(milliseconds: 45);
...@@ -95,7 +95,7 @@ class _CupertinoTextMagnifierState extends State<CupertinoTextMagnifier> ...@@ -95,7 +95,7 @@ class _CupertinoTextMagnifierState extends State<CupertinoTextMagnifier>
)..addListener(() => setState(() {})); )..addListener(() => setState(() {}));
widget.controller.animationController = _ioAnimationController; widget.controller.animationController = _ioAnimationController;
widget.magnifierOverlayInfoBearer widget.magnifierInfo
.addListener(_determineMagnifierPositionAndFocalPoint); .addListener(_determineMagnifierPositionAndFocalPoint);
_ioAnimation = Tween<double>( _ioAnimation = Tween<double>(
...@@ -111,16 +111,16 @@ class _CupertinoTextMagnifierState extends State<CupertinoTextMagnifier> ...@@ -111,16 +111,16 @@ class _CupertinoTextMagnifierState extends State<CupertinoTextMagnifier>
void dispose() { void dispose() {
widget.controller.animationController = null; widget.controller.animationController = null;
_ioAnimationController.dispose(); _ioAnimationController.dispose();
widget.magnifierOverlayInfoBearer widget.magnifierInfo
.removeListener(_determineMagnifierPositionAndFocalPoint); .removeListener(_determineMagnifierPositionAndFocalPoint);
super.dispose(); super.dispose();
} }
@override @override
void didUpdateWidget(CupertinoTextMagnifier oldWidget) { void didUpdateWidget(CupertinoTextMagnifier oldWidget) {
if (oldWidget.magnifierOverlayInfoBearer != widget.magnifierOverlayInfoBearer) { if (oldWidget.magnifierInfo != widget.magnifierInfo) {
oldWidget.magnifierOverlayInfoBearer.removeListener(_determineMagnifierPositionAndFocalPoint); oldWidget.magnifierInfo.removeListener(_determineMagnifierPositionAndFocalPoint);
widget.magnifierOverlayInfoBearer.addListener(_determineMagnifierPositionAndFocalPoint); widget.magnifierInfo.addListener(_determineMagnifierPositionAndFocalPoint);
} }
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
} }
...@@ -132,8 +132,8 @@ class _CupertinoTextMagnifierState extends State<CupertinoTextMagnifier> ...@@ -132,8 +132,8 @@ class _CupertinoTextMagnifierState extends State<CupertinoTextMagnifier>
} }
void _determineMagnifierPositionAndFocalPoint() { void _determineMagnifierPositionAndFocalPoint() {
final MagnifierOverlayInfoBearer textEditingContext = final MagnifierInfo textEditingContext =
widget.magnifierOverlayInfoBearer.value; widget.magnifierInfo.value;
// The exact Y of the center of the current line. // The exact Y of the center of the current line.
final double verticalCenterOfCurrentLine = final double verticalCenterOfCurrentLine =
...@@ -228,7 +228,7 @@ class _CupertinoTextMagnifierState extends State<CupertinoTextMagnifier> ...@@ -228,7 +228,7 @@ class _CupertinoTextMagnifierState extends State<CupertinoTextMagnifier>
/// ///
/// * [RawMagnifier], the backing implementation. /// * [RawMagnifier], the backing implementation.
/// * [CupertinoTextMagnifier], a widget that positions [CupertinoMagnifier] based on /// * [CupertinoTextMagnifier], a widget that positions [CupertinoMagnifier] based on
/// [MagnifierOverlayInfoBearer]. /// [MagnifierInfo].
/// * [MagnifierController], the controller for this magnifier. /// * [MagnifierController], the controller for this magnifier.
class CupertinoMagnifier extends StatelessWidget { class CupertinoMagnifier extends StatelessWidget {
/// Creates a [RawMagnifier] in the Cupertino style. /// Creates a [RawMagnifier] in the Cupertino style.
......
...@@ -798,9 +798,11 @@ class CupertinoTextField extends StatefulWidget { ...@@ -798,9 +798,11 @@ class CupertinoTextField extends StatefulWidget {
/// platforms. If it is desired to suppress the magnifier, consider passing /// platforms. If it is desired to suppress the magnifier, consider passing
/// [TextMagnifierConfiguration.disabled]. /// [TextMagnifierConfiguration.disabled].
/// ///
// TODO(antholeole): https://github.com/flutter/flutter/issues/108041 /// {@tool dartpad}
// once the magnifier PR lands, I should enrich this area of the /// This sample demonstrates how to customize the magnifier that this text field uses.
// docs with images of what a magnifier is. ///
/// ** See code in examples/api/lib/widgets/text_magnifier/text_magnifier.0.dart **
/// {@end-tool}
final TextMagnifierConfiguration? magnifierConfiguration; final TextMagnifierConfiguration? magnifierConfiguration;
/// {@macro flutter.widgets.EditableText.spellCheckConfiguration} /// {@macro flutter.widgets.EditableText.spellCheckConfiguration}
...@@ -873,14 +875,14 @@ class CupertinoTextField extends StatefulWidget { ...@@ -873,14 +875,14 @@ class CupertinoTextField extends StatefulWidget {
magnifierBuilder: ( magnifierBuilder: (
BuildContext context, BuildContext context,
MagnifierController controller, MagnifierController controller,
ValueNotifier<MagnifierOverlayInfoBearer> magnifierOverlayInfoBearer ValueNotifier<MagnifierInfo> magnifierInfo
) { ) {
switch (defaultTargetPlatform) { switch (defaultTargetPlatform) {
case TargetPlatform.android: case TargetPlatform.android:
case TargetPlatform.iOS: case TargetPlatform.iOS:
return CupertinoTextMagnifier( return CupertinoTextMagnifier(
controller: controller, controller: controller,
magnifierOverlayInfoBearer: magnifierOverlayInfoBearer, magnifierInfo: magnifierInfo,
); );
case TargetPlatform.fuchsia: case TargetPlatform.fuchsia:
case TargetPlatform.linux: case TargetPlatform.linux:
......
...@@ -45,17 +45,17 @@ class TextMagnifier extends StatefulWidget { ...@@ -45,17 +45,17 @@ class TextMagnifier extends StatefulWidget {
magnifierBuilder: ( magnifierBuilder: (
BuildContext context, BuildContext context,
MagnifierController controller, MagnifierController controller,
ValueNotifier<MagnifierOverlayInfoBearer> magnifierOverlayInfoBearer, ValueNotifier<MagnifierInfo> magnifierInfo,
) { ) {
switch (defaultTargetPlatform) { switch (defaultTargetPlatform) {
case TargetPlatform.iOS: case TargetPlatform.iOS:
return CupertinoTextMagnifier( return CupertinoTextMagnifier(
controller: controller, controller: controller,
magnifierOverlayInfoBearer: magnifierOverlayInfoBearer, magnifierInfo: magnifierInfo,
); );
case TargetPlatform.android: case TargetPlatform.android:
return TextMagnifier( return TextMagnifier(
magnifierInfo: magnifierOverlayInfoBearer, magnifierInfo: magnifierInfo,
); );
case TargetPlatform.fuchsia: case TargetPlatform.fuchsia:
case TargetPlatform.linux: case TargetPlatform.linux:
...@@ -75,7 +75,7 @@ class TextMagnifier extends StatefulWidget { ...@@ -75,7 +75,7 @@ class TextMagnifier extends StatefulWidget {
/// [TextMagnifier] positions itself based on [magnifierInfo]. /// [TextMagnifier] positions itself based on [magnifierInfo].
/// ///
/// {@macro widgets.material.magnifier.positionRules} /// {@macro widgets.material.magnifier.positionRules}
final ValueNotifier<MagnifierOverlayInfoBearer> final ValueNotifier<MagnifierInfo>
magnifierInfo; magnifierInfo;
@override @override
...@@ -132,7 +132,7 @@ class _TextMagnifierState extends State<TextMagnifier> { ...@@ -132,7 +132,7 @@ class _TextMagnifierState extends State<TextMagnifier> {
/// {@macro widgets.material.magnifier.positionRules} /// {@macro widgets.material.magnifier.positionRules}
void _determineMagnifierPositionAndFocalPoint() { void _determineMagnifierPositionAndFocalPoint() {
final MagnifierOverlayInfoBearer selectionInfo = final MagnifierInfo selectionInfo =
widget.magnifierInfo.value; widget.magnifierInfo.value;
final Rect screenRect = Offset.zero & MediaQuery.of(context).size; final Rect screenRect = Offset.zero & MediaQuery.of(context).size;
......
...@@ -436,6 +436,12 @@ class TextField extends StatefulWidget { ...@@ -436,6 +436,12 @@ class TextField extends StatefulWidget {
/// By default, builds a [CupertinoTextMagnifier] on iOS and [TextMagnifier] /// By default, builds a [CupertinoTextMagnifier] on iOS and [TextMagnifier]
/// on Android, and builds nothing on all other platforms. If it is desired to /// on Android, and builds nothing on all other platforms. If it is desired to
/// suppress the magnifier, consider passing [TextMagnifierConfiguration.disabled]. /// suppress the magnifier, consider passing [TextMagnifierConfiguration.disabled].
///
/// {@tool dartpad}
/// This sample demonstrates how to customize the magnifier that this text field uses.
///
/// ** See code in examples/api/lib/widgets/text_magnifier/text_magnifier.0.dart **
/// {@end-tool}
final TextMagnifierConfiguration? magnifierConfiguration; final TextMagnifierConfiguration? magnifierConfiguration;
/// Controls the text being edited. /// Controls the text being edited.
......
...@@ -18,7 +18,7 @@ import 'overlay.dart'; ...@@ -18,7 +18,7 @@ import 'overlay.dart';
/// {@template flutter.widgets.magnifier.MagnifierBuilder} /// {@template flutter.widgets.magnifier.MagnifierBuilder}
/// Signature for a builder that builds a [Widget] with a [MagnifierController]. /// Signature for a builder that builds a [Widget] with a [MagnifierController].
/// ///
/// Consuming [MagnifierController] or [ValueNotifier]<[MagnifierOverlayInfoBearer]> is not /// Consuming [MagnifierController] or [ValueNotifier]<[MagnifierInfo]> is not
/// required, although if a Widget intends to have entry or exit animations, it should take /// required, although if a Widget intends to have entry or exit animations, it should take
/// [MagnifierController] and provide it an [AnimationController], so that [MagnifierController] /// [MagnifierController] and provide it an [AnimationController], so that [MagnifierController]
/// can wait before removing it from the overlay. /// can wait before removing it from the overlay.
...@@ -26,28 +26,28 @@ import 'overlay.dart'; ...@@ -26,28 +26,28 @@ import 'overlay.dart';
/// ///
/// See also: /// See also:
/// ///
/// - [MagnifierOverlayInfoBearer], the data class that updates the /// - [MagnifierInfo], the data class that updates the
/// magnifier. /// magnifier.
typedef MagnifierBuilder = Widget? Function( typedef MagnifierBuilder = Widget? Function(
BuildContext context, BuildContext context,
MagnifierController controller, MagnifierController controller,
ValueNotifier<MagnifierOverlayInfoBearer> magnifierOverlayInfoBearer, ValueNotifier<MagnifierInfo> magnifierInfo,
); );
/// A data class that contains the geometry information of text layouts /// A data class that contains the geometry information of text layouts
/// and selection gestures, used to position magnifiers. /// and selection gestures, used to position magnifiers.
@immutable @immutable
class MagnifierOverlayInfoBearer { class MagnifierInfo {
/// Constructs a [MagnifierOverlayInfoBearer] from provided geometry values. /// Constructs a [MagnifierInfo] from provided geometry values.
const MagnifierOverlayInfoBearer({ const MagnifierInfo({
required this.globalGesturePosition, required this.globalGesturePosition,
required this.caretRect, required this.caretRect,
required this.fieldBounds, required this.fieldBounds,
required this.currentLineBoundaries, required this.currentLineBoundaries,
}); });
/// Const [MagnifierOverlayInfoBearer] with all values set to 0. /// Const [MagnifierInfo] with all values set to 0.
static const MagnifierOverlayInfoBearer empty = MagnifierOverlayInfoBearer( static const MagnifierInfo empty = MagnifierInfo(
globalGesturePosition: Offset.zero, globalGesturePosition: Offset.zero,
caretRect: Rect.zero, caretRect: Rect.zero,
currentLineBoundaries: Rect.zero, currentLineBoundaries: Rect.zero,
...@@ -73,7 +73,7 @@ class MagnifierOverlayInfoBearer { ...@@ -73,7 +73,7 @@ class MagnifierOverlayInfoBearer {
if (identical(this, other)) { if (identical(this, other)) {
return true; return true;
} }
return other is MagnifierOverlayInfoBearer return other is MagnifierInfo
&& other.globalGesturePosition == globalGesturePosition && other.globalGesturePosition == globalGesturePosition
&& other.caretRect == caretRect && other.caretRect == caretRect
&& other.currentLineBoundaries == currentLineBoundaries && other.currentLineBoundaries == currentLineBoundaries
...@@ -388,6 +388,12 @@ class MagnifierDecoration extends ShapeDecoration { ...@@ -388,6 +388,12 @@ class MagnifierDecoration extends ShapeDecoration {
/// A common base class for magnifiers. /// A common base class for magnifiers.
/// ///
/// {@tool dartpad}
/// This sample demonstrates what a magnifier is, and how it can be used.
///
/// ** See code in examples/api/lib/widgets/magnifier/magnifier.0.dart **
/// {@end-tool}
///
/// {@template flutter.widgets.magnifier.intro} /// {@template flutter.widgets.magnifier.intro}
/// This magnifying glass is useful for scenarios on mobile devices where /// This magnifying glass is useful for scenarios on mobile devices where
/// the user's finger may be covering part of the screen where a granular /// the user's finger may be covering part of the screen where a granular
......
...@@ -569,7 +569,7 @@ class _SelectableRegionState extends State<SelectableRegion> with TextSelectionD ...@@ -569,7 +569,7 @@ class _SelectableRegionState extends State<SelectableRegion> with TextSelectionD
)); ));
} }
MagnifierOverlayInfoBearer _buildInfoForMagnifier(Offset globalGesturePosition, SelectionPoint selectionPoint) { MagnifierInfo _buildInfoForMagnifier(Offset globalGesturePosition, SelectionPoint selectionPoint) {
final Vector3 globalTransform = _selectable!.getTransformTo(null).getTranslation(); final Vector3 globalTransform = _selectable!.getTransformTo(null).getTranslation();
final Offset globalTransformAsOffset = Offset(globalTransform.x, globalTransform.y); final Offset globalTransformAsOffset = Offset(globalTransform.x, globalTransform.y);
final Offset globalSelectionPointPosition = selectionPoint.localPosition + globalTransformAsOffset; final Offset globalSelectionPointPosition = selectionPoint.localPosition + globalTransformAsOffset;
...@@ -580,7 +580,7 @@ class _SelectableRegionState extends State<SelectableRegion> with TextSelectionD ...@@ -580,7 +580,7 @@ class _SelectableRegionState extends State<SelectableRegion> with TextSelectionD
selectionPoint.lineHeight selectionPoint.lineHeight
); );
return MagnifierOverlayInfoBearer( return MagnifierInfo(
globalGesturePosition: globalGesturePosition, globalGesturePosition: globalGesturePosition,
caretRect: caretRect, caretRect: caretRect,
fieldBounds: globalTransformAsOffset & _selectable!.size, fieldBounds: globalTransformAsOffset & _selectable!.size,
......
...@@ -543,7 +543,7 @@ class TextSelectionOverlay { ...@@ -543,7 +543,7 @@ class TextSelectionOverlay {
return endHandleRect?.height ?? renderObject.preferredLineHeight; return endHandleRect?.height ?? renderObject.preferredLineHeight;
} }
MagnifierOverlayInfoBearer _buildMagnifier({ MagnifierInfo _buildMagnifier({
required RenderEditable renderEditable, required RenderEditable renderEditable,
required Offset globalGesturePosition, required Offset globalGesturePosition,
required TextPosition currentTextPosition, required TextPosition currentTextPosition,
...@@ -567,7 +567,7 @@ class TextSelectionOverlay { ...@@ -567,7 +567,7 @@ class TextSelectionOverlay {
renderEditable.getLocalRectForCaret(positionAtEndOfLine).bottomCenter, renderEditable.getLocalRectForCaret(positionAtEndOfLine).bottomCenter,
); );
return MagnifierOverlayInfoBearer( return MagnifierInfo(
fieldBounds: globalRenderEditableTopLeft & renderEditable.size, fieldBounds: globalRenderEditableTopLeft & renderEditable.size,
globalGesturePosition: globalGesturePosition, globalGesturePosition: globalGesturePosition,
caretRect: localCaretRect.shift(globalRenderEditableTopLeft), caretRect: localCaretRect.shift(globalRenderEditableTopLeft),
...@@ -808,8 +808,8 @@ class SelectionOverlay { ...@@ -808,8 +808,8 @@ class SelectionOverlay {
final BuildContext context; final BuildContext context;
final ValueNotifier<MagnifierOverlayInfoBearer> _magnifierOverlayInfoBearer = final ValueNotifier<MagnifierInfo> _magnifierInfo =
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty); ValueNotifier<MagnifierInfo>(MagnifierInfo.empty);
/// [MagnifierController.show] and [MagnifierController.hide] should not be called directly, except /// [MagnifierController.show] and [MagnifierController.hide] should not be called directly, except
/// from inside [showMagnifier] and [hideMagnifier]. If it is desired to show or hide the magnifier, /// from inside [showMagnifier] and [hideMagnifier]. If it is desired to show or hide the magnifier,
...@@ -836,13 +836,13 @@ class SelectionOverlay { ...@@ -836,13 +836,13 @@ class SelectionOverlay {
/// since magnifiers may hide themselves. If this info is needed, check /// since magnifiers may hide themselves. If this info is needed, check
/// [MagnifierController.shown]. /// [MagnifierController.shown].
/// {@endtemplate} /// {@endtemplate}
void showMagnifier(MagnifierOverlayInfoBearer initialInfoBearer) { void showMagnifier(MagnifierInfo initalMagnifierInfo) {
if (_toolbar != null) { if (_toolbar != null) {
hideToolbar(); hideToolbar();
} }
// Start from empty, so we don't utilize any remnant values. // Start from empty, so we don't utilize any rememnant values.
_magnifierOverlayInfoBearer.value = initialInfoBearer; _magnifierInfo.value = initalMagnifierInfo;
// Pre-build the magnifiers so we can tell if we've built something // Pre-build the magnifiers so we can tell if we've built something
// or not. If we don't build a magnifiers, then we should not // or not. If we don't build a magnifiers, then we should not
...@@ -850,7 +850,7 @@ class SelectionOverlay { ...@@ -850,7 +850,7 @@ class SelectionOverlay {
final Widget? builtMagnifier = magnifierConfiguration.magnifierBuilder( final Widget? builtMagnifier = magnifierConfiguration.magnifierBuilder(
context, context,
_magnifierController, _magnifierController,
_magnifierOverlayInfoBearer, _magnifierInfo,
); );
if (builtMagnifier == null) { if (builtMagnifier == null) {
...@@ -1300,14 +1300,14 @@ class SelectionOverlay { ...@@ -1300,14 +1300,14 @@ class SelectionOverlay {
/// because the magnifier may have hidden itself and is looking for a cue to reshow /// because the magnifier may have hidden itself and is looking for a cue to reshow
/// itself. /// itself.
/// ///
/// If there is no magnifier in the overlay, this does nothing, /// If there is no magnifier in the overlay, this does nothing.
/// {@endtemplate} /// {@endtemplate}
void updateMagnifier(MagnifierOverlayInfoBearer magnifierOverlayInfoBearer) { void updateMagnifier(MagnifierInfo magnifierInfo) {
if (_magnifierController.overlayEntry == null) { if (_magnifierController.overlayEntry == null) {
return; return;
} }
_magnifierOverlayInfoBearer.value = magnifierOverlayInfoBearer; _magnifierInfo.value = magnifierInfo;
} }
} }
......
...@@ -14,18 +14,18 @@ void main() { ...@@ -14,18 +14,18 @@ void main() {
const Rect reasonableTextField = Rect.fromLTRB(0, 100, 200, 200); const Rect reasonableTextField = Rect.fromLTRB(0, 100, 200, 200);
final MagnifierController magnifierController = MagnifierController(); final MagnifierController magnifierController = MagnifierController();
// Make sure that your gesture in infoBearer is within the line in infoBearer, // Make sure that your gesture in magnifierInfo is within the line in magnifierInfo,
// or else the magnifier status will stay hidden and this will not complete. // or else the magnifier status will stay hidden and this will not complete.
Future<void> showCupertinoMagnifier( Future<void> showCupertinoMagnifier(
BuildContext context, BuildContext context,
WidgetTester tester, WidgetTester tester,
ValueNotifier<MagnifierOverlayInfoBearer> infoBearer, ValueNotifier<MagnifierInfo> magnifierInfo,
) async { ) async {
final Future<void> magnifierShown = magnifierController.show( final Future<void> magnifierShown = magnifierController.show(
context: context, context: context,
builder: (_) => CupertinoTextMagnifier( builder: (_) => CupertinoTextMagnifier(
controller: magnifierController, controller: magnifierController,
magnifierOverlayInfoBearer: infoBearer, magnifierInfo: magnifierInfo,
)); ));
WidgetsBinding.instance.scheduleFrame(); WidgetsBinding.instance.scheduleFrame();
...@@ -76,9 +76,9 @@ void main() { ...@@ -76,9 +76,9 @@ void main() {
final Rect fakeTextFieldRect = final Rect fakeTextFieldRect =
tapPointRenderBox.localToGlobal(Offset.zero) & tapPointRenderBox.size; tapPointRenderBox.localToGlobal(Offset.zero) & tapPointRenderBox.size;
final ValueNotifier<MagnifierOverlayInfoBearer> magnifier = final ValueNotifier<MagnifierInfo> magnifier =
ValueNotifier<MagnifierOverlayInfoBearer>( ValueNotifier<MagnifierInfo>(
MagnifierOverlayInfoBearer( MagnifierInfo(
currentLineBoundaries: fakeTextFieldRect, currentLineBoundaries: fakeTextFieldRect,
fieldBounds: fakeTextFieldRect, fieldBounds: fakeTextFieldRect,
caretRect: fakeTextFieldRect, caretRect: fakeTextFieldRect,
...@@ -110,8 +110,8 @@ void main() { ...@@ -110,8 +110,8 @@ void main() {
await showCupertinoMagnifier( await showCupertinoMagnifier(
context, context,
tester, tester,
ValueNotifier<MagnifierOverlayInfoBearer>( ValueNotifier<MagnifierInfo>(
MagnifierOverlayInfoBearer( MagnifierInfo(
currentLineBoundaries: reasonableTextField, currentLineBoundaries: reasonableTextField,
fieldBounds: reasonableTextField, fieldBounds: reasonableTextField,
caretRect: reasonableTextField, caretRect: reasonableTextField,
...@@ -143,8 +143,8 @@ void main() { ...@@ -143,8 +143,8 @@ void main() {
await showCupertinoMagnifier( await showCupertinoMagnifier(
context, context,
tester, tester,
ValueNotifier<MagnifierOverlayInfoBearer>( ValueNotifier<MagnifierInfo>(
MagnifierOverlayInfoBearer( MagnifierInfo(
currentLineBoundaries: reasonableTextField, currentLineBoundaries: reasonableTextField,
fieldBounds: reasonableTextField, fieldBounds: reasonableTextField,
caretRect: reasonableTextField, caretRect: reasonableTextField,
...@@ -176,9 +176,9 @@ void main() { ...@@ -176,9 +176,9 @@ void main() {
final BuildContext context = final BuildContext context =
tester.firstElement(find.byType(Placeholder)); tester.firstElement(find.byType(Placeholder));
final ValueNotifier<MagnifierOverlayInfoBearer> magnifierinfo = final ValueNotifier<MagnifierInfo> magnifierinfo =
ValueNotifier<MagnifierOverlayInfoBearer>( ValueNotifier<MagnifierInfo>(
MagnifierOverlayInfoBearer( MagnifierInfo(
currentLineBoundaries: reasonableTextField, currentLineBoundaries: reasonableTextField,
fieldBounds: reasonableTextField, fieldBounds: reasonableTextField,
caretRect: reasonableTextField, caretRect: reasonableTextField,
...@@ -192,7 +192,7 @@ void main() { ...@@ -192,7 +192,7 @@ void main() {
await showCupertinoMagnifier(context, tester, magnifierinfo); await showCupertinoMagnifier(context, tester, magnifierinfo);
// Move the gesture to one that should hide it. // Move the gesture to one that should hide it.
magnifierinfo.value = MagnifierOverlayInfoBearer( magnifierinfo.value = MagnifierInfo(
currentLineBoundaries: reasonableTextField, currentLineBoundaries: reasonableTextField,
fieldBounds: reasonableTextField, fieldBounds: reasonableTextField,
caretRect: reasonableTextField, caretRect: reasonableTextField,
...@@ -216,9 +216,9 @@ void main() { ...@@ -216,9 +216,9 @@ void main() {
final BuildContext context = final BuildContext context =
tester.firstElement(find.byType(Placeholder)); tester.firstElement(find.byType(Placeholder));
final ValueNotifier<MagnifierOverlayInfoBearer> magnifierInfo = final ValueNotifier<MagnifierInfo> magnifierInfo =
ValueNotifier<MagnifierOverlayInfoBearer>( ValueNotifier<MagnifierInfo>(
MagnifierOverlayInfoBearer( MagnifierInfo(
currentLineBoundaries: reasonableTextField, currentLineBoundaries: reasonableTextField,
fieldBounds: reasonableTextField, fieldBounds: reasonableTextField,
caretRect: reasonableTextField, caretRect: reasonableTextField,
...@@ -231,7 +231,7 @@ void main() { ...@@ -231,7 +231,7 @@ void main() {
await showCupertinoMagnifier(context, tester, magnifierInfo); await showCupertinoMagnifier(context, tester, magnifierInfo);
// Move the gesture to one that should hide it. // Move the gesture to one that should hide it.
magnifierInfo.value = MagnifierOverlayInfoBearer( magnifierInfo.value = MagnifierInfo(
currentLineBoundaries: reasonableTextField, currentLineBoundaries: reasonableTextField,
fieldBounds: reasonableTextField, fieldBounds: reasonableTextField,
caretRect: reasonableTextField, caretRect: reasonableTextField,
...@@ -243,7 +243,7 @@ void main() { ...@@ -243,7 +243,7 @@ void main() {
expect(magnifierController.overlayEntry, isNotNull); expect(magnifierController.overlayEntry, isNotNull);
// Return the gesture to one that shows it. // Return the gesture to one that shows it.
magnifierInfo.value = MagnifierOverlayInfoBearer( magnifierInfo.value = MagnifierInfo(
currentLineBoundaries: reasonableTextField, currentLineBoundaries: reasonableTextField,
fieldBounds: reasonableTextField, fieldBounds: reasonableTextField,
caretRect: reasonableTextField, caretRect: reasonableTextField,
......
...@@ -5964,7 +5964,7 @@ void main() { ...@@ -5964,7 +5964,7 @@ void main() {
}); });
group('magnifier', () { group('magnifier', () {
late ValueNotifier<MagnifierOverlayInfoBearer> infoBearer; late ValueNotifier<MagnifierInfo> magnifierInfo;
final Widget fakeMagnifier = Container(key: UniqueKey()); final Widget fakeMagnifier = Container(key: UniqueKey());
group('magnifier builder', () { group('magnifier builder', () {
...@@ -5987,7 +5987,7 @@ void main() { ...@@ -5987,7 +5987,7 @@ void main() {
defaultCupertinoTextField.magnifierConfiguration!.magnifierBuilder( defaultCupertinoTextField.magnifierConfiguration!.magnifierBuilder(
context, context,
MagnifierController(), MagnifierController(),
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty), ValueNotifier<MagnifierInfo>(MagnifierInfo.empty),
), ),
isA<Widget>().having((Widget widget) => widget.key, 'key', equals(customMagnifier.key))); isA<Widget>().having((Widget widget) => widget.key, 'key', equals(customMagnifier.key)));
}); });
...@@ -6005,7 +6005,7 @@ void main() { ...@@ -6005,7 +6005,7 @@ void main() {
editableText.magnifierConfiguration.magnifierBuilder( editableText.magnifierConfiguration.magnifierBuilder(
context, context,
MagnifierController(), MagnifierController(),
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty), ValueNotifier<MagnifierInfo>(MagnifierInfo.empty),
), ),
isA<CupertinoTextMagnifier>()); isA<CupertinoTextMagnifier>());
}, },
...@@ -6025,7 +6025,7 @@ void main() { ...@@ -6025,7 +6025,7 @@ void main() {
editableText.magnifierConfiguration.magnifierBuilder( editableText.magnifierConfiguration.magnifierBuilder(
context, context,
MagnifierController(), MagnifierController(),
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty), ValueNotifier<MagnifierInfo>(MagnifierInfo.empty),
), ),
isNull); isNull);
}, },
...@@ -6046,9 +6046,9 @@ void main() { ...@@ -6046,9 +6046,9 @@ void main() {
magnifierConfiguration: TextMagnifierConfiguration( magnifierConfiguration: TextMagnifierConfiguration(
magnifierBuilder: (_, magnifierBuilder: (_,
MagnifierController controller, MagnifierController controller,
ValueNotifier<MagnifierOverlayInfoBearer> ValueNotifier<MagnifierInfo>
localInfoBearer) { localMagnifierInfo) {
infoBearer = localInfoBearer; magnifierInfo = localMagnifierInfo;
return fakeMagnifier; return fakeMagnifier;
}), }),
), ),
...@@ -6086,14 +6086,14 @@ void main() { ...@@ -6086,14 +6086,14 @@ void main() {
await tester.pump(); await tester.pump();
expect(find.byKey(fakeMagnifier.key!), findsOneWidget); expect(find.byKey(fakeMagnifier.key!), findsOneWidget);
firstDragGesturePosition = infoBearer.value.globalGesturePosition; firstDragGesturePosition = magnifierInfo.value.globalGesturePosition;
await gesture.moveTo(textOffsetToPosition(tester, testValue.length)); await gesture.moveTo(textOffsetToPosition(tester, testValue.length));
await tester.pump(); await tester.pump();
// Expect the position the magnifier gets to have moved. // Expect the position the magnifier gets to have moved.
expect(firstDragGesturePosition, expect(firstDragGesturePosition,
isNot(infoBearer.value.globalGesturePosition)); isNot(magnifierInfo.value.globalGesturePosition));
await gesture.up(); await gesture.up();
await tester.pump(); await tester.pump();
...@@ -6114,9 +6114,9 @@ void main() { ...@@ -6114,9 +6114,9 @@ void main() {
magnifierBuilder: ( magnifierBuilder: (
_, _,
MagnifierController controller, MagnifierController controller,
ValueNotifier<MagnifierOverlayInfoBearer> localInfoBearer ValueNotifier<MagnifierInfo> localMagnifierInfo
) { ) {
infoBearer = localInfoBearer; magnifierInfo = localMagnifierInfo;
return fakeMagnifier; return fakeMagnifier;
}, },
), ),
...@@ -6144,7 +6144,7 @@ void main() { ...@@ -6144,7 +6144,7 @@ void main() {
expect(controller.selection.extentOffset, 5); expect(controller.selection.extentOffset, 5);
expect(find.byKey(fakeMagnifier.key!), findsOneWidget); expect(find.byKey(fakeMagnifier.key!), findsOneWidget);
final Offset firstLongPressGesturePosition = infoBearer.value.globalGesturePosition; final Offset firstLongPressGesturePosition = magnifierInfo.value.globalGesturePosition;
// Move the gesture to 'h' to update the magnifier and move the cursor to 'h'. // Move the gesture to 'h' to update the magnifier and move the cursor to 'h'.
await gesture.moveTo(textOffsetToPosition(tester, testValue.indexOf('h'))); await gesture.moveTo(textOffsetToPosition(tester, testValue.indexOf('h')));
...@@ -6153,7 +6153,7 @@ void main() { ...@@ -6153,7 +6153,7 @@ void main() {
expect(controller.selection.extentOffset, 9); expect(controller.selection.extentOffset, 9);
expect(find.byKey(fakeMagnifier.key!), findsOneWidget); expect(find.byKey(fakeMagnifier.key!), findsOneWidget);
// Expect the position the magnifier gets to have moved. // Expect the position the magnifier gets to have moved.
expect(firstLongPressGesturePosition, isNot(infoBearer.value.globalGesturePosition)); expect(firstLongPressGesturePosition, isNot(magnifierInfo.value.globalGesturePosition));
// End the long press to hide the magnifier. // End the long press to hide the magnifier.
await gesture.up(); await gesture.up();
......
...@@ -28,12 +28,12 @@ void main() { ...@@ -28,12 +28,12 @@ void main() {
Future<void> showMagnifier( Future<void> showMagnifier(
BuildContext context, BuildContext context,
WidgetTester tester, WidgetTester tester,
ValueNotifier<MagnifierOverlayInfoBearer> infoBearer, ValueNotifier<MagnifierInfo> magnifierInfo,
) async { ) async {
final Future<void> magnifierShown = magnifierController.show( final Future<void> magnifierShown = magnifierController.show(
context: context, context: context,
builder: (_) => TextMagnifier( builder: (_) => TextMagnifier(
magnifierInfo: infoBearer, magnifierInfo: magnifierInfo,
)); ));
WidgetsBinding.instance.scheduleFrame(); WidgetsBinding.instance.scheduleFrame();
...@@ -60,7 +60,7 @@ void main() { ...@@ -60,7 +60,7 @@ void main() {
final Widget? builtWidget = TextMagnifier.adaptiveMagnifierConfiguration.magnifierBuilder( final Widget? builtWidget = TextMagnifier.adaptiveMagnifierConfiguration.magnifierBuilder(
context, context,
MagnifierController(), MagnifierController(),
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty), ValueNotifier<MagnifierInfo>(MagnifierInfo.empty),
); );
expect(builtWidget, isA<TextMagnifier>()); expect(builtWidget, isA<TextMagnifier>());
...@@ -77,7 +77,7 @@ void main() { ...@@ -77,7 +77,7 @@ void main() {
final Widget? builtWidget = TextMagnifier.adaptiveMagnifierConfiguration.magnifierBuilder( final Widget? builtWidget = TextMagnifier.adaptiveMagnifierConfiguration.magnifierBuilder(
context, context,
MagnifierController(), MagnifierController(),
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty), ValueNotifier<MagnifierInfo>(MagnifierInfo.empty),
); );
expect(builtWidget, isA<CupertinoTextMagnifier>()); expect(builtWidget, isA<CupertinoTextMagnifier>());
...@@ -94,7 +94,7 @@ void main() { ...@@ -94,7 +94,7 @@ void main() {
final Widget? builtWidget = TextMagnifier.adaptiveMagnifierConfiguration.magnifierBuilder( final Widget? builtWidget = TextMagnifier.adaptiveMagnifierConfiguration.magnifierBuilder(
context, context,
MagnifierController(), MagnifierController(),
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty), ValueNotifier<MagnifierInfo>(MagnifierInfo.empty),
); );
expect(builtWidget, isNull); expect(builtWidget, isNull);
...@@ -144,9 +144,9 @@ void main() { ...@@ -144,9 +144,9 @@ void main() {
tapPointRenderBox.localToGlobal(Offset.zero) & tapPointRenderBox.localToGlobal(Offset.zero) &
tapPointRenderBox.size; tapPointRenderBox.size;
final ValueNotifier<MagnifierOverlayInfoBearer> magnifierInfo = final ValueNotifier<MagnifierInfo> magnifierInfo =
ValueNotifier<MagnifierOverlayInfoBearer>( ValueNotifier<MagnifierInfo>(
MagnifierOverlayInfoBearer( MagnifierInfo(
currentLineBoundaries: fakeTextFieldRect, currentLineBoundaries: fakeTextFieldRect,
fieldBounds: fakeTextFieldRect, fieldBounds: fakeTextFieldRect,
caretRect: fakeTextFieldRect, caretRect: fakeTextFieldRect,
...@@ -179,8 +179,8 @@ void main() { ...@@ -179,8 +179,8 @@ void main() {
await showMagnifier( await showMagnifier(
context, context,
tester, tester,
ValueNotifier<MagnifierOverlayInfoBearer>( ValueNotifier<MagnifierInfo>(
MagnifierOverlayInfoBearer( MagnifierInfo(
currentLineBoundaries: reasonableTextField, currentLineBoundaries: reasonableTextField,
// Inflate these two to make sure we're bounding on the // Inflate these two to make sure we're bounding on the
// current line boundaries, not anything else. // current line boundaries, not anything else.
...@@ -212,8 +212,8 @@ void main() { ...@@ -212,8 +212,8 @@ void main() {
await showMagnifier( await showMagnifier(
context, context,
tester, tester,
ValueNotifier<MagnifierOverlayInfoBearer>( ValueNotifier<MagnifierInfo>(
MagnifierOverlayInfoBearer( MagnifierInfo(
currentLineBoundaries: reasonableTextField, currentLineBoundaries: reasonableTextField,
// Inflate these two to make sure we're bounding on the // Inflate these two to make sure we're bounding on the
// current line boundaries, not anything else. // current line boundaries, not anything else.
...@@ -240,8 +240,8 @@ void main() { ...@@ -240,8 +240,8 @@ void main() {
await showMagnifier( await showMagnifier(
context, context,
tester, tester,
ValueNotifier<MagnifierOverlayInfoBearer>( ValueNotifier<MagnifierInfo>(
MagnifierOverlayInfoBearer( MagnifierInfo(
currentLineBoundaries: reasonableTextField, currentLineBoundaries: reasonableTextField,
fieldBounds: reasonableTextField, fieldBounds: reasonableTextField,
caretRect: reasonableTextField, caretRect: reasonableTextField,
...@@ -267,8 +267,8 @@ void main() { ...@@ -267,8 +267,8 @@ void main() {
await showMagnifier( await showMagnifier(
context, context,
tester, tester,
ValueNotifier<MagnifierOverlayInfoBearer>( ValueNotifier<MagnifierInfo>(
MagnifierOverlayInfoBearer( MagnifierInfo(
currentLineBoundaries: topOfScreenTextFieldRect, currentLineBoundaries: topOfScreenTextFieldRect,
fieldBounds: topOfScreenTextFieldRect, fieldBounds: topOfScreenTextFieldRect,
caretRect: topOfScreenTextFieldRect, caretRect: topOfScreenTextFieldRect,
...@@ -300,8 +300,8 @@ void main() { ...@@ -300,8 +300,8 @@ void main() {
await showMagnifier( await showMagnifier(
context, context,
tester, tester,
ValueNotifier<MagnifierOverlayInfoBearer>( ValueNotifier<MagnifierInfo>(
MagnifierOverlayInfoBearer( MagnifierInfo(
currentLineBoundaries: reasonableTextField, currentLineBoundaries: reasonableTextField,
fieldBounds: reasonableTextField, fieldBounds: reasonableTextField,
caretRect: reasonableTextField, caretRect: reasonableTextField,
...@@ -331,8 +331,8 @@ void main() { ...@@ -331,8 +331,8 @@ void main() {
await showMagnifier( await showMagnifier(
context, context,
tester, tester,
ValueNotifier<MagnifierOverlayInfoBearer>( ValueNotifier<MagnifierInfo>(
MagnifierOverlayInfoBearer( MagnifierInfo(
currentLineBoundaries: topOfScreenTextFieldRect, currentLineBoundaries: topOfScreenTextFieldRect,
fieldBounds: topOfScreenTextFieldRect, fieldBounds: topOfScreenTextFieldRect,
caretRect: topOfScreenTextFieldRect, caretRect: topOfScreenTextFieldRect,
...@@ -364,8 +364,8 @@ void main() { ...@@ -364,8 +364,8 @@ void main() {
await showMagnifier( await showMagnifier(
context, context,
tester, tester,
ValueNotifier<MagnifierOverlayInfoBearer>( ValueNotifier<MagnifierInfo>(
MagnifierOverlayInfoBearer( MagnifierInfo(
currentLineBoundaries: reasonableTextField, currentLineBoundaries: reasonableTextField,
fieldBounds: reasonableTextField, fieldBounds: reasonableTextField,
caretRect: reasonableTextField, caretRect: reasonableTextField,
...@@ -386,9 +386,9 @@ void main() { ...@@ -386,9 +386,9 @@ void main() {
final BuildContext context = final BuildContext context =
tester.firstElement(find.byType(Placeholder)); tester.firstElement(find.byType(Placeholder));
final ValueNotifier<MagnifierOverlayInfoBearer> magnifierPositioner = final ValueNotifier<MagnifierInfo> magnifierPositioner =
ValueNotifier<MagnifierOverlayInfoBearer>( ValueNotifier<MagnifierInfo>(
MagnifierOverlayInfoBearer( MagnifierInfo(
currentLineBoundaries: reasonableTextField, currentLineBoundaries: reasonableTextField,
fieldBounds: reasonableTextField, fieldBounds: reasonableTextField,
caretRect: reasonableTextField, caretRect: reasonableTextField,
...@@ -399,7 +399,7 @@ void main() { ...@@ -399,7 +399,7 @@ void main() {
await showMagnifier(context, tester, magnifierPositioner); await showMagnifier(context, tester, magnifierPositioner);
// New position has a horizontal shift. // New position has a horizontal shift.
magnifierPositioner.value = MagnifierOverlayInfoBearer( magnifierPositioner.value = MagnifierInfo(
currentLineBoundaries: reasonableTextField, currentLineBoundaries: reasonableTextField,
fieldBounds: reasonableTextField, fieldBounds: reasonableTextField,
caretRect: reasonableTextField, caretRect: reasonableTextField,
...@@ -422,9 +422,9 @@ void main() { ...@@ -422,9 +422,9 @@ void main() {
final BuildContext context = final BuildContext context =
tester.firstElement(find.byType(Placeholder)); tester.firstElement(find.byType(Placeholder));
final ValueNotifier<MagnifierOverlayInfoBearer> magnifierPositioner = final ValueNotifier<MagnifierInfo> magnifierPositioner =
ValueNotifier<MagnifierOverlayInfoBearer>( ValueNotifier<MagnifierInfo>(
MagnifierOverlayInfoBearer( MagnifierInfo(
currentLineBoundaries: reasonableTextField, currentLineBoundaries: reasonableTextField,
fieldBounds: reasonableTextField, fieldBounds: reasonableTextField,
caretRect: reasonableTextField, caretRect: reasonableTextField,
...@@ -435,7 +435,7 @@ void main() { ...@@ -435,7 +435,7 @@ void main() {
await showMagnifier(context, tester, magnifierPositioner); await showMagnifier(context, tester, magnifierPositioner);
// New position has a vertical shift. // New position has a vertical shift.
magnifierPositioner.value = MagnifierOverlayInfoBearer( magnifierPositioner.value = MagnifierInfo(
currentLineBoundaries: reasonableTextField.shift(verticalShift), currentLineBoundaries: reasonableTextField.shift(verticalShift),
fieldBounds: Rect.fromPoints(reasonableTextField.topLeft, fieldBounds: Rect.fromPoints(reasonableTextField.topLeft,
reasonableTextField.bottomRight + verticalShift), reasonableTextField.bottomRight + verticalShift),
...@@ -458,9 +458,9 @@ void main() { ...@@ -458,9 +458,9 @@ void main() {
final BuildContext context = final BuildContext context =
tester.firstElement(find.byType(Placeholder)); tester.firstElement(find.byType(Placeholder));
final ValueNotifier<MagnifierOverlayInfoBearer> magnifierPositioner = final ValueNotifier<MagnifierInfo> magnifierPositioner =
ValueNotifier<MagnifierOverlayInfoBearer>( ValueNotifier<MagnifierInfo>(
MagnifierOverlayInfoBearer( MagnifierInfo(
currentLineBoundaries: reasonableTextField, currentLineBoundaries: reasonableTextField,
fieldBounds: reasonableTextField, fieldBounds: reasonableTextField,
caretRect: reasonableTextField, caretRect: reasonableTextField,
...@@ -471,7 +471,7 @@ void main() { ...@@ -471,7 +471,7 @@ void main() {
await showMagnifier(context, tester, magnifierPositioner); await showMagnifier(context, tester, magnifierPositioner);
// New position has a vertical shift. // New position has a vertical shift.
magnifierPositioner.value = MagnifierOverlayInfoBearer( magnifierPositioner.value = MagnifierInfo(
currentLineBoundaries: reasonableTextField.shift(verticalShift), currentLineBoundaries: reasonableTextField.shift(verticalShift),
fieldBounds: Rect.fromPoints(reasonableTextField.topLeft, fieldBounds: Rect.fromPoints(reasonableTextField.topLeft,
reasonableTextField.bottomRight + verticalShift), reasonableTextField.bottomRight + verticalShift),
......
...@@ -12032,7 +12032,7 @@ void main() { ...@@ -12032,7 +12032,7 @@ void main() {
textField.magnifierConfiguration!.magnifierBuilder( textField.magnifierConfiguration!.magnifierBuilder(
context, context,
MagnifierController(), MagnifierController(),
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty), ValueNotifier<MagnifierInfo>(MagnifierInfo.empty),
), ),
isA<Widget>().having( isA<Widget>().having(
(Widget widget) => widget.key, (Widget widget) => widget.key,
...@@ -12053,7 +12053,7 @@ void main() { ...@@ -12053,7 +12053,7 @@ void main() {
editableText.magnifierConfiguration.magnifierBuilder( editableText.magnifierConfiguration.magnifierBuilder(
context, context,
MagnifierController(), MagnifierController(),
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty), ValueNotifier<MagnifierInfo>(MagnifierInfo.empty),
), ),
isA<TextMagnifier>()); isA<TextMagnifier>());
}, variant: TargetPlatformVariant.only(TargetPlatform.android)); }, variant: TargetPlatformVariant.only(TargetPlatform.android));
...@@ -12071,7 +12071,7 @@ void main() { ...@@ -12071,7 +12071,7 @@ void main() {
editableText.magnifierConfiguration.magnifierBuilder( editableText.magnifierConfiguration.magnifierBuilder(
context, context,
MagnifierController(), MagnifierController(),
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty), ValueNotifier<MagnifierInfo>(MagnifierInfo.empty),
), ),
isA<CupertinoTextMagnifier>()); isA<CupertinoTextMagnifier>());
}, variant: TargetPlatformVariant.only(TargetPlatform.iOS)); }, variant: TargetPlatformVariant.only(TargetPlatform.iOS));
...@@ -12089,7 +12089,7 @@ void main() { ...@@ -12089,7 +12089,7 @@ void main() {
editableText.magnifierConfiguration.magnifierBuilder( editableText.magnifierConfiguration.magnifierBuilder(
context, context,
MagnifierController(), MagnifierController(),
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty), ValueNotifier<MagnifierInfo>(MagnifierInfo.empty),
), ),
isNull); isNull);
}, },
...@@ -12101,7 +12101,7 @@ void main() { ...@@ -12101,7 +12101,7 @@ void main() {
}); });
group('magnifier', () { group('magnifier', () {
late ValueNotifier<MagnifierOverlayInfoBearer> infoBearer; late ValueNotifier<MagnifierInfo> magnifierInfo;
final Widget fakeMagnifier = Container(key: UniqueKey()); final Widget fakeMagnifier = Container(key: UniqueKey());
testWidgets( testWidgets(
...@@ -12117,9 +12117,9 @@ void main() { ...@@ -12117,9 +12117,9 @@ void main() {
magnifierBuilder: ( magnifierBuilder: (
_, _,
MagnifierController controller, MagnifierController controller,
ValueNotifier<MagnifierOverlayInfoBearer> localInfoBearer ValueNotifier<MagnifierInfo> localMagnifierInfo
) { ) {
infoBearer = localInfoBearer; magnifierInfo = localMagnifierInfo;
return fakeMagnifier; return fakeMagnifier;
}, },
), ),
...@@ -12153,14 +12153,14 @@ void main() { ...@@ -12153,14 +12153,14 @@ void main() {
await tester.pump(); await tester.pump();
expect(find.byKey(fakeMagnifier.key!), findsOneWidget); expect(find.byKey(fakeMagnifier.key!), findsOneWidget);
final Offset firstDragGesturePosition = infoBearer.value.globalGesturePosition; final Offset firstDragGesturePosition = magnifierInfo.value.globalGesturePosition;
await gesture.moveTo(textOffsetToPosition(tester, testValue.length)); await gesture.moveTo(textOffsetToPosition(tester, testValue.length));
await tester.pump(); await tester.pump();
// Expect the position the magnifier gets to have moved. // Expect the position the magnifier gets to have moved.
expect(firstDragGesturePosition, expect(firstDragGesturePosition,
isNot(infoBearer.value.globalGesturePosition)); isNot(magnifierInfo.value.globalGesturePosition));
await gesture.up(); await gesture.up();
await tester.pump(); await tester.pump();
...@@ -12182,9 +12182,9 @@ void main() { ...@@ -12182,9 +12182,9 @@ void main() {
magnifierBuilder: ( magnifierBuilder: (
_, _,
MagnifierController controller, MagnifierController controller,
ValueNotifier<MagnifierOverlayInfoBearer> localInfoBearer ValueNotifier<MagnifierInfo> localMagnifierInfo
) { ) {
infoBearer = localInfoBearer; magnifierInfo = localMagnifierInfo;
return fakeMagnifier; return fakeMagnifier;
}, },
), ),
...@@ -12214,7 +12214,7 @@ void main() { ...@@ -12214,7 +12214,7 @@ void main() {
expect(controller.selection.extentOffset, isTargetPlatformAndroid ? 7 : 5); expect(controller.selection.extentOffset, isTargetPlatformAndroid ? 7 : 5);
expect(find.byKey(fakeMagnifier.key!), findsOneWidget); expect(find.byKey(fakeMagnifier.key!), findsOneWidget);
final Offset firstLongPressGesturePosition = infoBearer.value.globalGesturePosition; final Offset firstLongPressGesturePosition = magnifierInfo.value.globalGesturePosition;
// Move the gesture to 'h' on Android to update the magnifier and select 'ghi'. // Move the gesture to 'h' on Android to update the magnifier and select 'ghi'.
// Move the gesture to 'h' on iOS to update the magnifier and move the cursor to 'h'. // Move the gesture to 'h' on iOS to update the magnifier and move the cursor to 'h'.
...@@ -12224,7 +12224,7 @@ void main() { ...@@ -12224,7 +12224,7 @@ void main() {
expect(controller.selection.extentOffset, isTargetPlatformAndroid ? 11 : 9); expect(controller.selection.extentOffset, isTargetPlatformAndroid ? 11 : 9);
expect(find.byKey(fakeMagnifier.key!), findsOneWidget); expect(find.byKey(fakeMagnifier.key!), findsOneWidget);
// Expect the position the magnifier gets to have moved. // Expect the position the magnifier gets to have moved.
expect(firstLongPressGesturePosition, isNot(infoBearer.value.globalGesturePosition)); expect(firstLongPressGesturePosition, isNot(magnifierInfo.value.globalGesturePosition));
// End the long press to hide the magnifier. // End the long press to hide the magnifier.
await gesture.up(); await gesture.up();
......
...@@ -12919,7 +12919,7 @@ void main() { ...@@ -12919,7 +12919,7 @@ void main() {
editableText.magnifierConfiguration.magnifierBuilder( editableText.magnifierConfiguration.magnifierBuilder(
context, context,
MagnifierController(), MagnifierController(),
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty) ValueNotifier<MagnifierInfo>(MagnifierInfo.empty)
), ),
isNull, isNull,
); );
......
...@@ -1101,7 +1101,7 @@ void main() { ...@@ -1101,7 +1101,7 @@ void main() {
}, skip: kIsWeb); // [intended] Web uses its native context menu. }, skip: kIsWeb); // [intended] Web uses its native context menu.
group('magnifier', () { group('magnifier', () {
late ValueNotifier<MagnifierOverlayInfoBearer> infoBearer; late ValueNotifier<MagnifierInfo> magnifierInfo;
final Widget fakeMagnifier = Container(key: UniqueKey()); final Widget fakeMagnifier = Container(key: UniqueKey());
testWidgets('Can drag handles to show, unshow, and update magnifier', testWidgets('Can drag handles to show, unshow, and update magnifier',
...@@ -1114,9 +1114,9 @@ void main() { ...@@ -1114,9 +1114,9 @@ void main() {
magnifierConfiguration: TextMagnifierConfiguration( magnifierConfiguration: TextMagnifierConfiguration(
magnifierBuilder: (_, magnifierBuilder: (_,
MagnifierController controller, MagnifierController controller,
ValueNotifier<MagnifierOverlayInfoBearer> ValueNotifier<MagnifierInfo>
localInfoBearer) { localMagnifierInfo) {
infoBearer = localInfoBearer; magnifierInfo = localMagnifierInfo;
return fakeMagnifier; return fakeMagnifier;
}, },
), ),
...@@ -1151,14 +1151,14 @@ void main() { ...@@ -1151,14 +1151,14 @@ void main() {
// Expect the magnifier to show and then store it's position. // Expect the magnifier to show and then store it's position.
expect(find.byKey(fakeMagnifier.key!), findsOneWidget); expect(find.byKey(fakeMagnifier.key!), findsOneWidget);
final Offset firstDragGesturePosition = final Offset firstDragGesturePosition =
infoBearer.value.globalGesturePosition; magnifierInfo.value.globalGesturePosition;
await gesture.moveTo(textOffsetToPosition(paragraph, text.length)); await gesture.moveTo(textOffsetToPosition(paragraph, text.length));
await tester.pump(); await tester.pump();
// Expect the position the magnifier gets to have moved. // Expect the position the magnifier gets to have moved.
expect(firstDragGesturePosition, expect(firstDragGesturePosition,
isNot(infoBearer.value.globalGesturePosition)); isNot(magnifierInfo.value.globalGesturePosition));
// Lift the pointer and expect the magnifier to disappear. // Lift the pointer and expect the magnifier to disappear.
await gesture.up(); await gesture.up();
......
...@@ -5153,7 +5153,7 @@ void main() { ...@@ -5153,7 +5153,7 @@ void main() {
}); });
group('magnifier', () { group('magnifier', () {
late ValueNotifier<MagnifierOverlayInfoBearer> infoBearer; late ValueNotifier<MagnifierInfo> magnifierInfo;
final Widget fakeMagnifier = Container(key: UniqueKey()); final Widget fakeMagnifier = Container(key: UniqueKey());
testWidgets( testWidgets(
...@@ -5166,9 +5166,9 @@ void main() { ...@@ -5166,9 +5166,9 @@ void main() {
magnifierBuilder: ( magnifierBuilder: (
_, _,
MagnifierController controller, MagnifierController controller,
ValueNotifier<MagnifierOverlayInfoBearer> localInfoBearer ValueNotifier<MagnifierInfo> localMagnifierInfo
) { ) {
infoBearer = localInfoBearer; magnifierInfo = localMagnifierInfo;
return fakeMagnifier; return fakeMagnifier;
}, },
) )
...@@ -5209,14 +5209,14 @@ void main() { ...@@ -5209,14 +5209,14 @@ void main() {
await tester.pump(); await tester.pump();
expect(find.byKey(fakeMagnifier.key!), findsOneWidget); expect(find.byKey(fakeMagnifier.key!), findsOneWidget);
firstDragGesturePosition = infoBearer.value.globalGesturePosition; firstDragGesturePosition = magnifierInfo.value.globalGesturePosition;
await gesture.moveTo(textOffsetToPosition(tester, testValue.length)); await gesture.moveTo(textOffsetToPosition(tester, testValue.length));
await tester.pump(); await tester.pump();
// Expect the position the magnifier gets to have moved. // Expect the position the magnifier gets to have moved.
expect(firstDragGesturePosition, expect(firstDragGesturePosition,
isNot(infoBearer.value.globalGesturePosition)); isNot(magnifierInfo.value.globalGesturePosition));
await gesture.up(); await gesture.up();
await tester.pump(); await tester.pump();
......
...@@ -1256,7 +1256,7 @@ void main() { ...@@ -1256,7 +1256,7 @@ void main() {
tester, tester,
magnifierConfiguration: TextMagnifierConfiguration( magnifierConfiguration: TextMagnifierConfiguration(
shouldDisplayHandlesInMagnifier: false, shouldDisplayHandlesInMagnifier: false,
magnifierBuilder: (BuildContext context, MagnifierController controller, ValueNotifier<MagnifierOverlayInfoBearer>? notifier) { magnifierBuilder: (BuildContext context, MagnifierController controller, ValueNotifier<MagnifierInfo>? notifier) {
return SizedBox.shrink( return SizedBox.shrink(
key: magnifierKey, key: magnifierKey,
); );
...@@ -1266,7 +1266,7 @@ void main() { ...@@ -1266,7 +1266,7 @@ void main() {
expect(find.byKey(magnifierKey), findsNothing); expect(find.byKey(magnifierKey), findsNothing);
final MagnifierOverlayInfoBearer info = MagnifierOverlayInfoBearer( final MagnifierInfo info = MagnifierInfo(
globalGesturePosition: Offset.zero, globalGesturePosition: Offset.zero,
caretRect: Offset.zero & const Size(5.0, 20.0), caretRect: Offset.zero & const Size(5.0, 20.0),
fieldBounds: Offset.zero & const Size(200.0, 50.0), fieldBounds: Offset.zero & const Size(200.0, 50.0),
......
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