mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Add clipBehavior to InteractiveViewer (#73281)
More intuitive control over whether the child can overflow out of InteractiveViewer, deterring a lot of confusion we were seeing.
This commit is contained in:
parent
2e3d3e6522
commit
2a2e0cf100
@ -19,14 +19,16 @@ import 'ticker_provider.dart';
|
|||||||
///
|
///
|
||||||
/// The user can transform the child by dragging to pan or pinching to zoom.
|
/// The user can transform the child by dragging to pan or pinching to zoom.
|
||||||
///
|
///
|
||||||
/// By default, InteractiveViewer may draw outside of its original area of the
|
/// By default, InteractiveViewer clips its child using [Clip.hardEdge].
|
||||||
/// screen, such as when a child is zoomed in and increases in size. However, it
|
/// To prevent this behavior, consider setting [clipBehavior] to [Clip.none].
|
||||||
/// will not receive gestures outside of its original area. To prevent
|
/// When [clipBehavior] is [Clip.none], InteractiveViewer may draw outside of
|
||||||
/// InteractiveViewer from drawing outside of its original size, wrap it in a
|
/// its original area of the screen, such as when a child is zoomed in and
|
||||||
/// [ClipRect]. Or, to prevent dead areas where InteractiveViewer does not
|
/// increases in size. However, it will not receive gestures outside of its original area.
|
||||||
/// receive gestures, be sure that the InteractiveViewer widget is the size of
|
/// To prevent dead areas where InteractiveViewer does not receive gestures,
|
||||||
/// the area that should be interactive. See
|
/// don't set [clipBehavior] or be sure that the InteractiveViewer widget is the
|
||||||
/// [flutter-go](https://github.com/justinmc/flutter-go) for an example of
|
/// size of the area that should be interactive.
|
||||||
|
///
|
||||||
|
/// See [flutter-go](https://github.com/justinmc/flutter-go) for an example of
|
||||||
/// robust positioning of an InteractiveViewer child that works for all screen
|
/// robust positioning of an InteractiveViewer child that works for all screen
|
||||||
/// sizes and child sizes.
|
/// sizes and child sizes.
|
||||||
///
|
///
|
||||||
@ -68,6 +70,7 @@ class InteractiveViewer extends StatefulWidget {
|
|||||||
/// The [child] parameter must not be null.
|
/// The [child] parameter must not be null.
|
||||||
InteractiveViewer({
|
InteractiveViewer({
|
||||||
Key? key,
|
Key? key,
|
||||||
|
this.clipBehavior = Clip.hardEdge,
|
||||||
this.alignPanAxis = false,
|
this.alignPanAxis = false,
|
||||||
this.boundaryMargin = EdgeInsets.zero,
|
this.boundaryMargin = EdgeInsets.zero,
|
||||||
this.constrained = true,
|
this.constrained = true,
|
||||||
@ -102,6 +105,13 @@ class InteractiveViewer extends StatefulWidget {
|
|||||||
&& boundaryMargin.left.isFinite)),
|
&& boundaryMargin.left.isFinite)),
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
|
/// If set to [Clip.none], the child may extend beyond the size of the InteractiveViewer,
|
||||||
|
/// but it will not receive gestures in these areas.
|
||||||
|
/// Be sure that the InteractiveViewer is the desired size when using [Clip.none].
|
||||||
|
///
|
||||||
|
/// Defaults to [Clip.hardEdge].
|
||||||
|
final Clip clipBehavior;
|
||||||
|
|
||||||
/// If true, panning is only allowed in the direction of the horizontal axis
|
/// If true, panning is only allowed in the direction of the horizontal axis
|
||||||
/// or the vertical axis.
|
/// or the vertical axis.
|
||||||
///
|
///
|
||||||
@ -1061,15 +1071,20 @@ class _InteractiveViewerState extends State<InteractiveViewer> with TickerProvid
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!widget.constrained) {
|
if (!widget.constrained) {
|
||||||
|
child = OverflowBox(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
minWidth: 0.0,
|
||||||
|
minHeight: 0.0,
|
||||||
|
maxWidth: double.infinity,
|
||||||
|
maxHeight: double.infinity,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget.clipBehavior != Clip.none) {
|
||||||
child = ClipRect(
|
child = ClipRect(
|
||||||
child: OverflowBox(
|
clipBehavior: widget.clipBehavior,
|
||||||
alignment: Alignment.topLeft,
|
child: child,
|
||||||
minWidth: 0.0,
|
|
||||||
minHeight: 0.0,
|
|
||||||
maxWidth: double.infinity,
|
|
||||||
maxHeight: double.infinity,
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -985,6 +985,49 @@ void main() {
|
|||||||
expect(scale, greaterThan(1.0));
|
expect(scale, greaterThan(1.0));
|
||||||
expect(transformationController.value.getMaxScaleOnAxis(), greaterThan(1.0));
|
expect(transformationController.value.getMaxScaleOnAxis(), greaterThan(1.0));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Check if ClipRect is present in the tree', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: InteractiveViewer(
|
||||||
|
constrained: false,
|
||||||
|
clipBehavior: Clip.none,
|
||||||
|
minScale: 1.0,
|
||||||
|
maxScale: 1.0,
|
||||||
|
child: const SizedBox(width: 200.0, height: 200.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
find.byType(ClipRect),
|
||||||
|
findsNothing,
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: InteractiveViewer(
|
||||||
|
constrained: false,
|
||||||
|
minScale: 1.0,
|
||||||
|
maxScale: 1.0,
|
||||||
|
child: const SizedBox(width: 200.0, height: 200.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
find.byType(ClipRect),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('getNearestPointOnLine', () {
|
group('getNearestPointOnLine', () {
|
||||||
|
Loading…
Reference in New Issue
Block a user