mirror of
https://github.com/flutter/flutter
synced 2024-10-03 23:14:12 +00:00
Added example for Magnifier and TextMagnifier (#110218)
This commit is contained in:
parent
dc2618bb90
commit
4ceeca0833
62
examples/api/lib/widgets/magnifier/magnifier.0.dart
Normal file
62
examples/api/lib/widgets/magnifier/magnifier.0.dart
Normal file
|
@ -0,0 +1,62 @@
|
|||
// 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,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
112
examples/api/lib/widgets/text_magnifier/text_magnifier.0.dart
Normal file
112
examples/api/lib/widgets/text_magnifier/text_magnifier.0.dart
Normal file
|
@ -0,0 +1,112 @@
|
|||
// 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,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
56
examples/api/test/widgets/magnifier/magnifier.0_test.dart
Normal file
56
examples/api/test/widgets/magnifier/magnifier.0_test.dart
Normal file
|
@ -0,0 +1,56 @@
|
|||
// 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'),
|
||||
);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
// 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';
|
|||
/// finger may be blocking the point of interest, like a selection handle.
|
||||
///
|
||||
/// Delegates styling to [CupertinoMagnifier] with its position depending on
|
||||
/// [magnifierOverlayInfoBearer].
|
||||
/// [magnifierInfo].
|
||||
///
|
||||
/// Specifically, the [CupertinoTextMagnifier] follows the following rules.
|
||||
/// [CupertinoTextMagnifier]:
|
||||
|
@ -21,7 +21,7 @@ import 'package:flutter/widgets.dart';
|
|||
/// then has vertical offset [dragResistance] * k.
|
||||
class CupertinoTextMagnifier extends StatefulWidget {
|
||||
/// Constructs a [RawMagnifier] in the Cupertino style, positioning with respect to
|
||||
/// [magnifierOverlayInfoBearer].
|
||||
/// [magnifierInfo].
|
||||
///
|
||||
/// The default constructor parameters and constants were eyeballed on
|
||||
/// an iPhone XR iOS v15.5.
|
||||
|
@ -32,7 +32,7 @@ class CupertinoTextMagnifier extends StatefulWidget {
|
|||
this.dragResistance = 10.0,
|
||||
this.hideBelowThreshold = 48.0,
|
||||
this.horizontalScreenEdgePadding = 10.0,
|
||||
required this.magnifierOverlayInfoBearer,
|
||||
required this.magnifierInfo,
|
||||
});
|
||||
|
||||
/// The curve used for the in / out animations.
|
||||
|
@ -63,9 +63,9 @@ class CupertinoTextMagnifier extends StatefulWidget {
|
|||
final double horizontalScreenEdgePadding;
|
||||
|
||||
/// [CupertinoTextMagnifier] will determine its own positioning
|
||||
/// based on the [MagnifierOverlayInfoBearer] of this notifier.
|
||||
final ValueNotifier<MagnifierOverlayInfoBearer>
|
||||
magnifierOverlayInfoBearer;
|
||||
/// based on the [MagnifierInfo] of this notifier.
|
||||
final ValueNotifier<MagnifierInfo>
|
||||
magnifierInfo;
|
||||
|
||||
/// The duration that the magnifier drags behind its final position.
|
||||
static const Duration _kDragAnimationDuration = Duration(milliseconds: 45);
|
||||
|
@ -95,7 +95,7 @@ class _CupertinoTextMagnifierState extends State<CupertinoTextMagnifier>
|
|||
)..addListener(() => setState(() {}));
|
||||
|
||||
widget.controller.animationController = _ioAnimationController;
|
||||
widget.magnifierOverlayInfoBearer
|
||||
widget.magnifierInfo
|
||||
.addListener(_determineMagnifierPositionAndFocalPoint);
|
||||
|
||||
_ioAnimation = Tween<double>(
|
||||
|
@ -111,16 +111,16 @@ class _CupertinoTextMagnifierState extends State<CupertinoTextMagnifier>
|
|||
void dispose() {
|
||||
widget.controller.animationController = null;
|
||||
_ioAnimationController.dispose();
|
||||
widget.magnifierOverlayInfoBearer
|
||||
widget.magnifierInfo
|
||||
.removeListener(_determineMagnifierPositionAndFocalPoint);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(CupertinoTextMagnifier oldWidget) {
|
||||
if (oldWidget.magnifierOverlayInfoBearer != widget.magnifierOverlayInfoBearer) {
|
||||
oldWidget.magnifierOverlayInfoBearer.removeListener(_determineMagnifierPositionAndFocalPoint);
|
||||
widget.magnifierOverlayInfoBearer.addListener(_determineMagnifierPositionAndFocalPoint);
|
||||
if (oldWidget.magnifierInfo != widget.magnifierInfo) {
|
||||
oldWidget.magnifierInfo.removeListener(_determineMagnifierPositionAndFocalPoint);
|
||||
widget.magnifierInfo.addListener(_determineMagnifierPositionAndFocalPoint);
|
||||
}
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
@ -132,8 +132,8 @@ class _CupertinoTextMagnifierState extends State<CupertinoTextMagnifier>
|
|||
}
|
||||
|
||||
void _determineMagnifierPositionAndFocalPoint() {
|
||||
final MagnifierOverlayInfoBearer textEditingContext =
|
||||
widget.magnifierOverlayInfoBearer.value;
|
||||
final MagnifierInfo textEditingContext =
|
||||
widget.magnifierInfo.value;
|
||||
|
||||
// The exact Y of the center of the current line.
|
||||
final double verticalCenterOfCurrentLine =
|
||||
|
@ -228,7 +228,7 @@ class _CupertinoTextMagnifierState extends State<CupertinoTextMagnifier>
|
|||
///
|
||||
/// * [RawMagnifier], the backing implementation.
|
||||
/// * [CupertinoTextMagnifier], a widget that positions [CupertinoMagnifier] based on
|
||||
/// [MagnifierOverlayInfoBearer].
|
||||
/// [MagnifierInfo].
|
||||
/// * [MagnifierController], the controller for this magnifier.
|
||||
class CupertinoMagnifier extends StatelessWidget {
|
||||
/// Creates a [RawMagnifier] in the Cupertino style.
|
||||
|
|
|
@ -798,9 +798,11 @@ class CupertinoTextField extends StatefulWidget {
|
|||
/// platforms. If it is desired to suppress the magnifier, consider passing
|
||||
/// [TextMagnifierConfiguration.disabled].
|
||||
///
|
||||
// TODO(antholeole): https://github.com/flutter/flutter/issues/108041
|
||||
// once the magnifier PR lands, I should enrich this area of the
|
||||
// docs with images of what a magnifier is.
|
||||
/// {@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;
|
||||
|
||||
/// {@macro flutter.widgets.EditableText.spellCheckConfiguration}
|
||||
|
@ -873,14 +875,14 @@ class CupertinoTextField extends StatefulWidget {
|
|||
magnifierBuilder: (
|
||||
BuildContext context,
|
||||
MagnifierController controller,
|
||||
ValueNotifier<MagnifierOverlayInfoBearer> magnifierOverlayInfoBearer
|
||||
ValueNotifier<MagnifierInfo> magnifierInfo
|
||||
) {
|
||||
switch (defaultTargetPlatform) {
|
||||
case TargetPlatform.android:
|
||||
case TargetPlatform.iOS:
|
||||
return CupertinoTextMagnifier(
|
||||
controller: controller,
|
||||
magnifierOverlayInfoBearer: magnifierOverlayInfoBearer,
|
||||
magnifierInfo: magnifierInfo,
|
||||
);
|
||||
case TargetPlatform.fuchsia:
|
||||
case TargetPlatform.linux:
|
||||
|
|
|
@ -45,17 +45,17 @@ class TextMagnifier extends StatefulWidget {
|
|||
magnifierBuilder: (
|
||||
BuildContext context,
|
||||
MagnifierController controller,
|
||||
ValueNotifier<MagnifierOverlayInfoBearer> magnifierOverlayInfoBearer,
|
||||
ValueNotifier<MagnifierInfo> magnifierInfo,
|
||||
) {
|
||||
switch (defaultTargetPlatform) {
|
||||
case TargetPlatform.iOS:
|
||||
return CupertinoTextMagnifier(
|
||||
controller: controller,
|
||||
magnifierOverlayInfoBearer: magnifierOverlayInfoBearer,
|
||||
magnifierInfo: magnifierInfo,
|
||||
);
|
||||
case TargetPlatform.android:
|
||||
return TextMagnifier(
|
||||
magnifierInfo: magnifierOverlayInfoBearer,
|
||||
magnifierInfo: magnifierInfo,
|
||||
);
|
||||
case TargetPlatform.fuchsia:
|
||||
case TargetPlatform.linux:
|
||||
|
@ -75,7 +75,7 @@ class TextMagnifier extends StatefulWidget {
|
|||
/// [TextMagnifier] positions itself based on [magnifierInfo].
|
||||
///
|
||||
/// {@macro widgets.material.magnifier.positionRules}
|
||||
final ValueNotifier<MagnifierOverlayInfoBearer>
|
||||
final ValueNotifier<MagnifierInfo>
|
||||
magnifierInfo;
|
||||
|
||||
@override
|
||||
|
@ -132,7 +132,7 @@ class _TextMagnifierState extends State<TextMagnifier> {
|
|||
|
||||
/// {@macro widgets.material.magnifier.positionRules}
|
||||
void _determineMagnifierPositionAndFocalPoint() {
|
||||
final MagnifierOverlayInfoBearer selectionInfo =
|
||||
final MagnifierInfo selectionInfo =
|
||||
widget.magnifierInfo.value;
|
||||
final Rect screenRect = Offset.zero & MediaQuery.of(context).size;
|
||||
|
||||
|
|
|
@ -436,6 +436,12 @@ class TextField extends StatefulWidget {
|
|||
/// 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].
|
||||
///
|
||||
/// {@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;
|
||||
|
||||
/// Controls the text being edited.
|
||||
|
|
|
@ -18,7 +18,7 @@ import 'overlay.dart';
|
|||
/// {@template flutter.widgets.magnifier.MagnifierBuilder}
|
||||
/// 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
|
||||
/// [MagnifierController] and provide it an [AnimationController], so that [MagnifierController]
|
||||
/// can wait before removing it from the overlay.
|
||||
|
@ -26,28 +26,28 @@ import 'overlay.dart';
|
|||
///
|
||||
/// See also:
|
||||
///
|
||||
/// - [MagnifierOverlayInfoBearer], the data class that updates the
|
||||
/// - [MagnifierInfo], the data class that updates the
|
||||
/// magnifier.
|
||||
typedef MagnifierBuilder = Widget? Function(
|
||||
BuildContext context,
|
||||
MagnifierController controller,
|
||||
ValueNotifier<MagnifierOverlayInfoBearer> magnifierOverlayInfoBearer,
|
||||
ValueNotifier<MagnifierInfo> magnifierInfo,
|
||||
);
|
||||
|
||||
/// A data class that contains the geometry information of text layouts
|
||||
/// and selection gestures, used to position magnifiers.
|
||||
@immutable
|
||||
class MagnifierOverlayInfoBearer {
|
||||
/// Constructs a [MagnifierOverlayInfoBearer] from provided geometry values.
|
||||
const MagnifierOverlayInfoBearer({
|
||||
class MagnifierInfo {
|
||||
/// Constructs a [MagnifierInfo] from provided geometry values.
|
||||
const MagnifierInfo({
|
||||
required this.globalGesturePosition,
|
||||
required this.caretRect,
|
||||
required this.fieldBounds,
|
||||
required this.currentLineBoundaries,
|
||||
});
|
||||
|
||||
/// Const [MagnifierOverlayInfoBearer] with all values set to 0.
|
||||
static const MagnifierOverlayInfoBearer empty = MagnifierOverlayInfoBearer(
|
||||
/// Const [MagnifierInfo] with all values set to 0.
|
||||
static const MagnifierInfo empty = MagnifierInfo(
|
||||
globalGesturePosition: Offset.zero,
|
||||
caretRect: Rect.zero,
|
||||
currentLineBoundaries: Rect.zero,
|
||||
|
@ -73,7 +73,7 @@ class MagnifierOverlayInfoBearer {
|
|||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
return other is MagnifierOverlayInfoBearer
|
||||
return other is MagnifierInfo
|
||||
&& other.globalGesturePosition == globalGesturePosition
|
||||
&& other.caretRect == caretRect
|
||||
&& other.currentLineBoundaries == currentLineBoundaries
|
||||
|
@ -388,6 +388,12 @@ class MagnifierDecoration extends ShapeDecoration {
|
|||
|
||||
/// 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}
|
||||
/// 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
|
||||
|
|
|
@ -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 Offset globalTransformAsOffset = Offset(globalTransform.x, globalTransform.y);
|
||||
final Offset globalSelectionPointPosition = selectionPoint.localPosition + globalTransformAsOffset;
|
||||
|
@ -580,7 +580,7 @@ class _SelectableRegionState extends State<SelectableRegion> with TextSelectionD
|
|||
selectionPoint.lineHeight
|
||||
);
|
||||
|
||||
return MagnifierOverlayInfoBearer(
|
||||
return MagnifierInfo(
|
||||
globalGesturePosition: globalGesturePosition,
|
||||
caretRect: caretRect,
|
||||
fieldBounds: globalTransformAsOffset & _selectable!.size,
|
||||
|
|
|
@ -543,7 +543,7 @@ class TextSelectionOverlay {
|
|||
return endHandleRect?.height ?? renderObject.preferredLineHeight;
|
||||
}
|
||||
|
||||
MagnifierOverlayInfoBearer _buildMagnifier({
|
||||
MagnifierInfo _buildMagnifier({
|
||||
required RenderEditable renderEditable,
|
||||
required Offset globalGesturePosition,
|
||||
required TextPosition currentTextPosition,
|
||||
|
@ -567,7 +567,7 @@ class TextSelectionOverlay {
|
|||
renderEditable.getLocalRectForCaret(positionAtEndOfLine).bottomCenter,
|
||||
);
|
||||
|
||||
return MagnifierOverlayInfoBearer(
|
||||
return MagnifierInfo(
|
||||
fieldBounds: globalRenderEditableTopLeft & renderEditable.size,
|
||||
globalGesturePosition: globalGesturePosition,
|
||||
caretRect: localCaretRect.shift(globalRenderEditableTopLeft),
|
||||
|
@ -808,8 +808,8 @@ class SelectionOverlay {
|
|||
final BuildContext context;
|
||||
|
||||
|
||||
final ValueNotifier<MagnifierOverlayInfoBearer> _magnifierOverlayInfoBearer =
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty);
|
||||
final ValueNotifier<MagnifierInfo> _magnifierInfo =
|
||||
ValueNotifier<MagnifierInfo>(MagnifierInfo.empty);
|
||||
|
||||
/// [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,
|
||||
|
@ -836,13 +836,13 @@ class SelectionOverlay {
|
|||
/// since magnifiers may hide themselves. If this info is needed, check
|
||||
/// [MagnifierController.shown].
|
||||
/// {@endtemplate}
|
||||
void showMagnifier(MagnifierOverlayInfoBearer initialInfoBearer) {
|
||||
void showMagnifier(MagnifierInfo initalMagnifierInfo) {
|
||||
if (_toolbar != null) {
|
||||
hideToolbar();
|
||||
}
|
||||
|
||||
// Start from empty, so we don't utilize any remnant values.
|
||||
_magnifierOverlayInfoBearer.value = initialInfoBearer;
|
||||
// Start from empty, so we don't utilize any rememnant values.
|
||||
_magnifierInfo.value = initalMagnifierInfo;
|
||||
|
||||
// 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
|
||||
|
@ -850,7 +850,7 @@ class SelectionOverlay {
|
|||
final Widget? builtMagnifier = magnifierConfiguration.magnifierBuilder(
|
||||
context,
|
||||
_magnifierController,
|
||||
_magnifierOverlayInfoBearer,
|
||||
_magnifierInfo,
|
||||
);
|
||||
|
||||
if (builtMagnifier == null) {
|
||||
|
@ -1300,14 +1300,14 @@ class SelectionOverlay {
|
|||
/// because the magnifier may have hidden itself and is looking for a cue to reshow
|
||||
/// itself.
|
||||
///
|
||||
/// If there is no magnifier in the overlay, this does nothing,
|
||||
/// If there is no magnifier in the overlay, this does nothing.
|
||||
/// {@endtemplate}
|
||||
void updateMagnifier(MagnifierOverlayInfoBearer magnifierOverlayInfoBearer) {
|
||||
void updateMagnifier(MagnifierInfo magnifierInfo) {
|
||||
if (_magnifierController.overlayEntry == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
_magnifierOverlayInfoBearer.value = magnifierOverlayInfoBearer;
|
||||
_magnifierInfo.value = magnifierInfo;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,18 +14,18 @@ void main() {
|
|||
const Rect reasonableTextField = Rect.fromLTRB(0, 100, 200, 200);
|
||||
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.
|
||||
Future<void> showCupertinoMagnifier(
|
||||
BuildContext context,
|
||||
WidgetTester tester,
|
||||
ValueNotifier<MagnifierOverlayInfoBearer> infoBearer,
|
||||
ValueNotifier<MagnifierInfo> magnifierInfo,
|
||||
) async {
|
||||
final Future<void> magnifierShown = magnifierController.show(
|
||||
context: context,
|
||||
builder: (_) => CupertinoTextMagnifier(
|
||||
controller: magnifierController,
|
||||
magnifierOverlayInfoBearer: infoBearer,
|
||||
magnifierInfo: magnifierInfo,
|
||||
));
|
||||
|
||||
WidgetsBinding.instance.scheduleFrame();
|
||||
|
@ -76,9 +76,9 @@ void main() {
|
|||
final Rect fakeTextFieldRect =
|
||||
tapPointRenderBox.localToGlobal(Offset.zero) & tapPointRenderBox.size;
|
||||
|
||||
final ValueNotifier<MagnifierOverlayInfoBearer> magnifier =
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(
|
||||
MagnifierOverlayInfoBearer(
|
||||
final ValueNotifier<MagnifierInfo> magnifier =
|
||||
ValueNotifier<MagnifierInfo>(
|
||||
MagnifierInfo(
|
||||
currentLineBoundaries: fakeTextFieldRect,
|
||||
fieldBounds: fakeTextFieldRect,
|
||||
caretRect: fakeTextFieldRect,
|
||||
|
@ -110,8 +110,8 @@ void main() {
|
|||
await showCupertinoMagnifier(
|
||||
context,
|
||||
tester,
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(
|
||||
MagnifierOverlayInfoBearer(
|
||||
ValueNotifier<MagnifierInfo>(
|
||||
MagnifierInfo(
|
||||
currentLineBoundaries: reasonableTextField,
|
||||
fieldBounds: reasonableTextField,
|
||||
caretRect: reasonableTextField,
|
||||
|
@ -143,8 +143,8 @@ void main() {
|
|||
await showCupertinoMagnifier(
|
||||
context,
|
||||
tester,
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(
|
||||
MagnifierOverlayInfoBearer(
|
||||
ValueNotifier<MagnifierInfo>(
|
||||
MagnifierInfo(
|
||||
currentLineBoundaries: reasonableTextField,
|
||||
fieldBounds: reasonableTextField,
|
||||
caretRect: reasonableTextField,
|
||||
|
@ -176,9 +176,9 @@ void main() {
|
|||
final BuildContext context =
|
||||
tester.firstElement(find.byType(Placeholder));
|
||||
|
||||
final ValueNotifier<MagnifierOverlayInfoBearer> magnifierinfo =
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(
|
||||
MagnifierOverlayInfoBearer(
|
||||
final ValueNotifier<MagnifierInfo> magnifierinfo =
|
||||
ValueNotifier<MagnifierInfo>(
|
||||
MagnifierInfo(
|
||||
currentLineBoundaries: reasonableTextField,
|
||||
fieldBounds: reasonableTextField,
|
||||
caretRect: reasonableTextField,
|
||||
|
@ -192,7 +192,7 @@ void main() {
|
|||
await showCupertinoMagnifier(context, tester, magnifierinfo);
|
||||
|
||||
// Move the gesture to one that should hide it.
|
||||
magnifierinfo.value = MagnifierOverlayInfoBearer(
|
||||
magnifierinfo.value = MagnifierInfo(
|
||||
currentLineBoundaries: reasonableTextField,
|
||||
fieldBounds: reasonableTextField,
|
||||
caretRect: reasonableTextField,
|
||||
|
@ -216,9 +216,9 @@ void main() {
|
|||
final BuildContext context =
|
||||
tester.firstElement(find.byType(Placeholder));
|
||||
|
||||
final ValueNotifier<MagnifierOverlayInfoBearer> magnifierInfo =
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(
|
||||
MagnifierOverlayInfoBearer(
|
||||
final ValueNotifier<MagnifierInfo> magnifierInfo =
|
||||
ValueNotifier<MagnifierInfo>(
|
||||
MagnifierInfo(
|
||||
currentLineBoundaries: reasonableTextField,
|
||||
fieldBounds: reasonableTextField,
|
||||
caretRect: reasonableTextField,
|
||||
|
@ -231,7 +231,7 @@ void main() {
|
|||
await showCupertinoMagnifier(context, tester, magnifierInfo);
|
||||
|
||||
// Move the gesture to one that should hide it.
|
||||
magnifierInfo.value = MagnifierOverlayInfoBearer(
|
||||
magnifierInfo.value = MagnifierInfo(
|
||||
currentLineBoundaries: reasonableTextField,
|
||||
fieldBounds: reasonableTextField,
|
||||
caretRect: reasonableTextField,
|
||||
|
@ -243,7 +243,7 @@ void main() {
|
|||
expect(magnifierController.overlayEntry, isNotNull);
|
||||
|
||||
// Return the gesture to one that shows it.
|
||||
magnifierInfo.value = MagnifierOverlayInfoBearer(
|
||||
magnifierInfo.value = MagnifierInfo(
|
||||
currentLineBoundaries: reasonableTextField,
|
||||
fieldBounds: reasonableTextField,
|
||||
caretRect: reasonableTextField,
|
||||
|
|
|
@ -5964,7 +5964,7 @@ void main() {
|
|||
});
|
||||
|
||||
group('magnifier', () {
|
||||
late ValueNotifier<MagnifierOverlayInfoBearer> infoBearer;
|
||||
late ValueNotifier<MagnifierInfo> magnifierInfo;
|
||||
final Widget fakeMagnifier = Container(key: UniqueKey());
|
||||
|
||||
group('magnifier builder', () {
|
||||
|
@ -5987,7 +5987,7 @@ void main() {
|
|||
defaultCupertinoTextField.magnifierConfiguration!.magnifierBuilder(
|
||||
context,
|
||||
MagnifierController(),
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty),
|
||||
ValueNotifier<MagnifierInfo>(MagnifierInfo.empty),
|
||||
),
|
||||
isA<Widget>().having((Widget widget) => widget.key, 'key', equals(customMagnifier.key)));
|
||||
});
|
||||
|
@ -6005,7 +6005,7 @@ void main() {
|
|||
editableText.magnifierConfiguration.magnifierBuilder(
|
||||
context,
|
||||
MagnifierController(),
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty),
|
||||
ValueNotifier<MagnifierInfo>(MagnifierInfo.empty),
|
||||
),
|
||||
isA<CupertinoTextMagnifier>());
|
||||
},
|
||||
|
@ -6025,7 +6025,7 @@ void main() {
|
|||
editableText.magnifierConfiguration.magnifierBuilder(
|
||||
context,
|
||||
MagnifierController(),
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty),
|
||||
ValueNotifier<MagnifierInfo>(MagnifierInfo.empty),
|
||||
),
|
||||
isNull);
|
||||
},
|
||||
|
@ -6046,9 +6046,9 @@ void main() {
|
|||
magnifierConfiguration: TextMagnifierConfiguration(
|
||||
magnifierBuilder: (_,
|
||||
MagnifierController controller,
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>
|
||||
localInfoBearer) {
|
||||
infoBearer = localInfoBearer;
|
||||
ValueNotifier<MagnifierInfo>
|
||||
localMagnifierInfo) {
|
||||
magnifierInfo = localMagnifierInfo;
|
||||
return fakeMagnifier;
|
||||
}),
|
||||
),
|
||||
|
@ -6086,14 +6086,14 @@ void main() {
|
|||
await tester.pump();
|
||||
|
||||
expect(find.byKey(fakeMagnifier.key!), findsOneWidget);
|
||||
firstDragGesturePosition = infoBearer.value.globalGesturePosition;
|
||||
firstDragGesturePosition = magnifierInfo.value.globalGesturePosition;
|
||||
|
||||
await gesture.moveTo(textOffsetToPosition(tester, testValue.length));
|
||||
await tester.pump();
|
||||
|
||||
// Expect the position the magnifier gets to have moved.
|
||||
expect(firstDragGesturePosition,
|
||||
isNot(infoBearer.value.globalGesturePosition));
|
||||
isNot(magnifierInfo.value.globalGesturePosition));
|
||||
|
||||
await gesture.up();
|
||||
await tester.pump();
|
||||
|
@ -6114,9 +6114,9 @@ void main() {
|
|||
magnifierBuilder: (
|
||||
_,
|
||||
MagnifierController controller,
|
||||
ValueNotifier<MagnifierOverlayInfoBearer> localInfoBearer
|
||||
ValueNotifier<MagnifierInfo> localMagnifierInfo
|
||||
) {
|
||||
infoBearer = localInfoBearer;
|
||||
magnifierInfo = localMagnifierInfo;
|
||||
return fakeMagnifier;
|
||||
},
|
||||
),
|
||||
|
@ -6144,7 +6144,7 @@ void main() {
|
|||
expect(controller.selection.extentOffset, 5);
|
||||
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'.
|
||||
await gesture.moveTo(textOffsetToPosition(tester, testValue.indexOf('h')));
|
||||
|
@ -6153,7 +6153,7 @@ void main() {
|
|||
expect(controller.selection.extentOffset, 9);
|
||||
expect(find.byKey(fakeMagnifier.key!), findsOneWidget);
|
||||
// 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.
|
||||
await gesture.up();
|
||||
|
|
|
@ -28,12 +28,12 @@ void main() {
|
|||
Future<void> showMagnifier(
|
||||
BuildContext context,
|
||||
WidgetTester tester,
|
||||
ValueNotifier<MagnifierOverlayInfoBearer> infoBearer,
|
||||
ValueNotifier<MagnifierInfo> magnifierInfo,
|
||||
) async {
|
||||
final Future<void> magnifierShown = magnifierController.show(
|
||||
context: context,
|
||||
builder: (_) => TextMagnifier(
|
||||
magnifierInfo: infoBearer,
|
||||
magnifierInfo: magnifierInfo,
|
||||
));
|
||||
|
||||
WidgetsBinding.instance.scheduleFrame();
|
||||
|
@ -60,7 +60,7 @@ void main() {
|
|||
final Widget? builtWidget = TextMagnifier.adaptiveMagnifierConfiguration.magnifierBuilder(
|
||||
context,
|
||||
MagnifierController(),
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty),
|
||||
ValueNotifier<MagnifierInfo>(MagnifierInfo.empty),
|
||||
);
|
||||
|
||||
expect(builtWidget, isA<TextMagnifier>());
|
||||
|
@ -77,7 +77,7 @@ void main() {
|
|||
final Widget? builtWidget = TextMagnifier.adaptiveMagnifierConfiguration.magnifierBuilder(
|
||||
context,
|
||||
MagnifierController(),
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty),
|
||||
ValueNotifier<MagnifierInfo>(MagnifierInfo.empty),
|
||||
);
|
||||
|
||||
expect(builtWidget, isA<CupertinoTextMagnifier>());
|
||||
|
@ -94,7 +94,7 @@ void main() {
|
|||
final Widget? builtWidget = TextMagnifier.adaptiveMagnifierConfiguration.magnifierBuilder(
|
||||
context,
|
||||
MagnifierController(),
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty),
|
||||
ValueNotifier<MagnifierInfo>(MagnifierInfo.empty),
|
||||
);
|
||||
|
||||
expect(builtWidget, isNull);
|
||||
|
@ -144,9 +144,9 @@ void main() {
|
|||
tapPointRenderBox.localToGlobal(Offset.zero) &
|
||||
tapPointRenderBox.size;
|
||||
|
||||
final ValueNotifier<MagnifierOverlayInfoBearer> magnifierInfo =
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(
|
||||
MagnifierOverlayInfoBearer(
|
||||
final ValueNotifier<MagnifierInfo> magnifierInfo =
|
||||
ValueNotifier<MagnifierInfo>(
|
||||
MagnifierInfo(
|
||||
currentLineBoundaries: fakeTextFieldRect,
|
||||
fieldBounds: fakeTextFieldRect,
|
||||
caretRect: fakeTextFieldRect,
|
||||
|
@ -179,8 +179,8 @@ void main() {
|
|||
await showMagnifier(
|
||||
context,
|
||||
tester,
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(
|
||||
MagnifierOverlayInfoBearer(
|
||||
ValueNotifier<MagnifierInfo>(
|
||||
MagnifierInfo(
|
||||
currentLineBoundaries: reasonableTextField,
|
||||
// Inflate these two to make sure we're bounding on the
|
||||
// current line boundaries, not anything else.
|
||||
|
@ -212,8 +212,8 @@ void main() {
|
|||
await showMagnifier(
|
||||
context,
|
||||
tester,
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(
|
||||
MagnifierOverlayInfoBearer(
|
||||
ValueNotifier<MagnifierInfo>(
|
||||
MagnifierInfo(
|
||||
currentLineBoundaries: reasonableTextField,
|
||||
// Inflate these two to make sure we're bounding on the
|
||||
// current line boundaries, not anything else.
|
||||
|
@ -240,8 +240,8 @@ void main() {
|
|||
await showMagnifier(
|
||||
context,
|
||||
tester,
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(
|
||||
MagnifierOverlayInfoBearer(
|
||||
ValueNotifier<MagnifierInfo>(
|
||||
MagnifierInfo(
|
||||
currentLineBoundaries: reasonableTextField,
|
||||
fieldBounds: reasonableTextField,
|
||||
caretRect: reasonableTextField,
|
||||
|
@ -267,8 +267,8 @@ void main() {
|
|||
await showMagnifier(
|
||||
context,
|
||||
tester,
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(
|
||||
MagnifierOverlayInfoBearer(
|
||||
ValueNotifier<MagnifierInfo>(
|
||||
MagnifierInfo(
|
||||
currentLineBoundaries: topOfScreenTextFieldRect,
|
||||
fieldBounds: topOfScreenTextFieldRect,
|
||||
caretRect: topOfScreenTextFieldRect,
|
||||
|
@ -300,8 +300,8 @@ void main() {
|
|||
await showMagnifier(
|
||||
context,
|
||||
tester,
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(
|
||||
MagnifierOverlayInfoBearer(
|
||||
ValueNotifier<MagnifierInfo>(
|
||||
MagnifierInfo(
|
||||
currentLineBoundaries: reasonableTextField,
|
||||
fieldBounds: reasonableTextField,
|
||||
caretRect: reasonableTextField,
|
||||
|
@ -331,8 +331,8 @@ void main() {
|
|||
await showMagnifier(
|
||||
context,
|
||||
tester,
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(
|
||||
MagnifierOverlayInfoBearer(
|
||||
ValueNotifier<MagnifierInfo>(
|
||||
MagnifierInfo(
|
||||
currentLineBoundaries: topOfScreenTextFieldRect,
|
||||
fieldBounds: topOfScreenTextFieldRect,
|
||||
caretRect: topOfScreenTextFieldRect,
|
||||
|
@ -364,8 +364,8 @@ void main() {
|
|||
await showMagnifier(
|
||||
context,
|
||||
tester,
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(
|
||||
MagnifierOverlayInfoBearer(
|
||||
ValueNotifier<MagnifierInfo>(
|
||||
MagnifierInfo(
|
||||
currentLineBoundaries: reasonableTextField,
|
||||
fieldBounds: reasonableTextField,
|
||||
caretRect: reasonableTextField,
|
||||
|
@ -386,9 +386,9 @@ void main() {
|
|||
final BuildContext context =
|
||||
tester.firstElement(find.byType(Placeholder));
|
||||
|
||||
final ValueNotifier<MagnifierOverlayInfoBearer> magnifierPositioner =
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(
|
||||
MagnifierOverlayInfoBearer(
|
||||
final ValueNotifier<MagnifierInfo> magnifierPositioner =
|
||||
ValueNotifier<MagnifierInfo>(
|
||||
MagnifierInfo(
|
||||
currentLineBoundaries: reasonableTextField,
|
||||
fieldBounds: reasonableTextField,
|
||||
caretRect: reasonableTextField,
|
||||
|
@ -399,7 +399,7 @@ void main() {
|
|||
await showMagnifier(context, tester, magnifierPositioner);
|
||||
|
||||
// New position has a horizontal shift.
|
||||
magnifierPositioner.value = MagnifierOverlayInfoBearer(
|
||||
magnifierPositioner.value = MagnifierInfo(
|
||||
currentLineBoundaries: reasonableTextField,
|
||||
fieldBounds: reasonableTextField,
|
||||
caretRect: reasonableTextField,
|
||||
|
@ -422,9 +422,9 @@ void main() {
|
|||
final BuildContext context =
|
||||
tester.firstElement(find.byType(Placeholder));
|
||||
|
||||
final ValueNotifier<MagnifierOverlayInfoBearer> magnifierPositioner =
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(
|
||||
MagnifierOverlayInfoBearer(
|
||||
final ValueNotifier<MagnifierInfo> magnifierPositioner =
|
||||
ValueNotifier<MagnifierInfo>(
|
||||
MagnifierInfo(
|
||||
currentLineBoundaries: reasonableTextField,
|
||||
fieldBounds: reasonableTextField,
|
||||
caretRect: reasonableTextField,
|
||||
|
@ -435,7 +435,7 @@ void main() {
|
|||
await showMagnifier(context, tester, magnifierPositioner);
|
||||
|
||||
// New position has a vertical shift.
|
||||
magnifierPositioner.value = MagnifierOverlayInfoBearer(
|
||||
magnifierPositioner.value = MagnifierInfo(
|
||||
currentLineBoundaries: reasonableTextField.shift(verticalShift),
|
||||
fieldBounds: Rect.fromPoints(reasonableTextField.topLeft,
|
||||
reasonableTextField.bottomRight + verticalShift),
|
||||
|
@ -458,9 +458,9 @@ void main() {
|
|||
final BuildContext context =
|
||||
tester.firstElement(find.byType(Placeholder));
|
||||
|
||||
final ValueNotifier<MagnifierOverlayInfoBearer> magnifierPositioner =
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(
|
||||
MagnifierOverlayInfoBearer(
|
||||
final ValueNotifier<MagnifierInfo> magnifierPositioner =
|
||||
ValueNotifier<MagnifierInfo>(
|
||||
MagnifierInfo(
|
||||
currentLineBoundaries: reasonableTextField,
|
||||
fieldBounds: reasonableTextField,
|
||||
caretRect: reasonableTextField,
|
||||
|
@ -471,7 +471,7 @@ void main() {
|
|||
await showMagnifier(context, tester, magnifierPositioner);
|
||||
|
||||
// New position has a vertical shift.
|
||||
magnifierPositioner.value = MagnifierOverlayInfoBearer(
|
||||
magnifierPositioner.value = MagnifierInfo(
|
||||
currentLineBoundaries: reasonableTextField.shift(verticalShift),
|
||||
fieldBounds: Rect.fromPoints(reasonableTextField.topLeft,
|
||||
reasonableTextField.bottomRight + verticalShift),
|
||||
|
|
|
@ -12032,7 +12032,7 @@ void main() {
|
|||
textField.magnifierConfiguration!.magnifierBuilder(
|
||||
context,
|
||||
MagnifierController(),
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty),
|
||||
ValueNotifier<MagnifierInfo>(MagnifierInfo.empty),
|
||||
),
|
||||
isA<Widget>().having(
|
||||
(Widget widget) => widget.key,
|
||||
|
@ -12053,7 +12053,7 @@ void main() {
|
|||
editableText.magnifierConfiguration.magnifierBuilder(
|
||||
context,
|
||||
MagnifierController(),
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty),
|
||||
ValueNotifier<MagnifierInfo>(MagnifierInfo.empty),
|
||||
),
|
||||
isA<TextMagnifier>());
|
||||
}, variant: TargetPlatformVariant.only(TargetPlatform.android));
|
||||
|
@ -12071,7 +12071,7 @@ void main() {
|
|||
editableText.magnifierConfiguration.magnifierBuilder(
|
||||
context,
|
||||
MagnifierController(),
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty),
|
||||
ValueNotifier<MagnifierInfo>(MagnifierInfo.empty),
|
||||
),
|
||||
isA<CupertinoTextMagnifier>());
|
||||
}, variant: TargetPlatformVariant.only(TargetPlatform.iOS));
|
||||
|
@ -12089,7 +12089,7 @@ void main() {
|
|||
editableText.magnifierConfiguration.magnifierBuilder(
|
||||
context,
|
||||
MagnifierController(),
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty),
|
||||
ValueNotifier<MagnifierInfo>(MagnifierInfo.empty),
|
||||
),
|
||||
isNull);
|
||||
},
|
||||
|
@ -12101,7 +12101,7 @@ void main() {
|
|||
});
|
||||
|
||||
group('magnifier', () {
|
||||
late ValueNotifier<MagnifierOverlayInfoBearer> infoBearer;
|
||||
late ValueNotifier<MagnifierInfo> magnifierInfo;
|
||||
final Widget fakeMagnifier = Container(key: UniqueKey());
|
||||
|
||||
testWidgets(
|
||||
|
@ -12117,9 +12117,9 @@ void main() {
|
|||
magnifierBuilder: (
|
||||
_,
|
||||
MagnifierController controller,
|
||||
ValueNotifier<MagnifierOverlayInfoBearer> localInfoBearer
|
||||
ValueNotifier<MagnifierInfo> localMagnifierInfo
|
||||
) {
|
||||
infoBearer = localInfoBearer;
|
||||
magnifierInfo = localMagnifierInfo;
|
||||
return fakeMagnifier;
|
||||
},
|
||||
),
|
||||
|
@ -12153,14 +12153,14 @@ void main() {
|
|||
await tester.pump();
|
||||
|
||||
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 tester.pump();
|
||||
|
||||
// Expect the position the magnifier gets to have moved.
|
||||
expect(firstDragGesturePosition,
|
||||
isNot(infoBearer.value.globalGesturePosition));
|
||||
isNot(magnifierInfo.value.globalGesturePosition));
|
||||
|
||||
await gesture.up();
|
||||
await tester.pump();
|
||||
|
@ -12182,9 +12182,9 @@ void main() {
|
|||
magnifierBuilder: (
|
||||
_,
|
||||
MagnifierController controller,
|
||||
ValueNotifier<MagnifierOverlayInfoBearer> localInfoBearer
|
||||
ValueNotifier<MagnifierInfo> localMagnifierInfo
|
||||
) {
|
||||
infoBearer = localInfoBearer;
|
||||
magnifierInfo = localMagnifierInfo;
|
||||
return fakeMagnifier;
|
||||
},
|
||||
),
|
||||
|
@ -12214,7 +12214,7 @@ void main() {
|
|||
expect(controller.selection.extentOffset, isTargetPlatformAndroid ? 7 : 5);
|
||||
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 iOS to update the magnifier and move the cursor to 'h'.
|
||||
|
@ -12224,7 +12224,7 @@ void main() {
|
|||
expect(controller.selection.extentOffset, isTargetPlatformAndroid ? 11 : 9);
|
||||
expect(find.byKey(fakeMagnifier.key!), findsOneWidget);
|
||||
// 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.
|
||||
await gesture.up();
|
||||
|
|
|
@ -12919,7 +12919,7 @@ void main() {
|
|||
editableText.magnifierConfiguration.magnifierBuilder(
|
||||
context,
|
||||
MagnifierController(),
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>(MagnifierOverlayInfoBearer.empty)
|
||||
ValueNotifier<MagnifierInfo>(MagnifierInfo.empty)
|
||||
),
|
||||
isNull,
|
||||
);
|
||||
|
|
|
@ -1101,7 +1101,7 @@ void main() {
|
|||
}, skip: kIsWeb); // [intended] Web uses its native context menu.
|
||||
|
||||
group('magnifier', () {
|
||||
late ValueNotifier<MagnifierOverlayInfoBearer> infoBearer;
|
||||
late ValueNotifier<MagnifierInfo> magnifierInfo;
|
||||
final Widget fakeMagnifier = Container(key: UniqueKey());
|
||||
|
||||
testWidgets('Can drag handles to show, unshow, and update magnifier',
|
||||
|
@ -1114,9 +1114,9 @@ void main() {
|
|||
magnifierConfiguration: TextMagnifierConfiguration(
|
||||
magnifierBuilder: (_,
|
||||
MagnifierController controller,
|
||||
ValueNotifier<MagnifierOverlayInfoBearer>
|
||||
localInfoBearer) {
|
||||
infoBearer = localInfoBearer;
|
||||
ValueNotifier<MagnifierInfo>
|
||||
localMagnifierInfo) {
|
||||
magnifierInfo = localMagnifierInfo;
|
||||
return fakeMagnifier;
|
||||
},
|
||||
),
|
||||
|
@ -1151,14 +1151,14 @@ void main() {
|
|||
// Expect the magnifier to show and then store it's position.
|
||||
expect(find.byKey(fakeMagnifier.key!), findsOneWidget);
|
||||
final Offset firstDragGesturePosition =
|
||||
infoBearer.value.globalGesturePosition;
|
||||
magnifierInfo.value.globalGesturePosition;
|
||||
|
||||
await gesture.moveTo(textOffsetToPosition(paragraph, text.length));
|
||||
await tester.pump();
|
||||
|
||||
// Expect the position the magnifier gets to have moved.
|
||||
expect(firstDragGesturePosition,
|
||||
isNot(infoBearer.value.globalGesturePosition));
|
||||
isNot(magnifierInfo.value.globalGesturePosition));
|
||||
|
||||
// Lift the pointer and expect the magnifier to disappear.
|
||||
await gesture.up();
|
||||
|
|
|
@ -5153,7 +5153,7 @@ void main() {
|
|||
});
|
||||
|
||||
group('magnifier', () {
|
||||
late ValueNotifier<MagnifierOverlayInfoBearer> infoBearer;
|
||||
late ValueNotifier<MagnifierInfo> magnifierInfo;
|
||||
final Widget fakeMagnifier = Container(key: UniqueKey());
|
||||
|
||||
testWidgets(
|
||||
|
@ -5166,9 +5166,9 @@ void main() {
|
|||
magnifierBuilder: (
|
||||
_,
|
||||
MagnifierController controller,
|
||||
ValueNotifier<MagnifierOverlayInfoBearer> localInfoBearer
|
||||
ValueNotifier<MagnifierInfo> localMagnifierInfo
|
||||
) {
|
||||
infoBearer = localInfoBearer;
|
||||
magnifierInfo = localMagnifierInfo;
|
||||
return fakeMagnifier;
|
||||
},
|
||||
)
|
||||
|
@ -5209,14 +5209,14 @@ void main() {
|
|||
await tester.pump();
|
||||
|
||||
expect(find.byKey(fakeMagnifier.key!), findsOneWidget);
|
||||
firstDragGesturePosition = infoBearer.value.globalGesturePosition;
|
||||
firstDragGesturePosition = magnifierInfo.value.globalGesturePosition;
|
||||
|
||||
await gesture.moveTo(textOffsetToPosition(tester, testValue.length));
|
||||
await tester.pump();
|
||||
|
||||
// Expect the position the magnifier gets to have moved.
|
||||
expect(firstDragGesturePosition,
|
||||
isNot(infoBearer.value.globalGesturePosition));
|
||||
isNot(magnifierInfo.value.globalGesturePosition));
|
||||
|
||||
await gesture.up();
|
||||
await tester.pump();
|
||||
|
|
|
@ -1256,7 +1256,7 @@ void main() {
|
|||
tester,
|
||||
magnifierConfiguration: TextMagnifierConfiguration(
|
||||
shouldDisplayHandlesInMagnifier: false,
|
||||
magnifierBuilder: (BuildContext context, MagnifierController controller, ValueNotifier<MagnifierOverlayInfoBearer>? notifier) {
|
||||
magnifierBuilder: (BuildContext context, MagnifierController controller, ValueNotifier<MagnifierInfo>? notifier) {
|
||||
return SizedBox.shrink(
|
||||
key: magnifierKey,
|
||||
);
|
||||
|
@ -1266,7 +1266,7 @@ void main() {
|
|||
|
||||
expect(find.byKey(magnifierKey), findsNothing);
|
||||
|
||||
final MagnifierOverlayInfoBearer info = MagnifierOverlayInfoBearer(
|
||||
final MagnifierInfo info = MagnifierInfo(
|
||||
globalGesturePosition: Offset.zero,
|
||||
caretRect: Offset.zero & const Size(5.0, 20.0),
|
||||
fieldBounds: Offset.zero & const Size(200.0, 50.0),
|
||||
|
|
Loading…
Reference in a new issue