Unverified Commit 2a2e0cf1 authored by RomanJos's avatar RomanJos Committed by GitHub

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.
parent 2e3d3e65
......@@ -19,14 +19,16 @@ import 'ticker_provider.dart';
///
/// 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
/// screen, such as when a child is zoomed in and increases in size. However, it
/// will not receive gestures outside of its original area. To prevent
/// InteractiveViewer from drawing outside of its original size, wrap it in a
/// [ClipRect]. Or, to prevent dead areas where InteractiveViewer does not
/// receive gestures, be sure that the InteractiveViewer widget is the size of
/// the area that should be interactive. See
/// [flutter-go](https://github.com/justinmc/flutter-go) for an example of
/// By default, InteractiveViewer clips its child using [Clip.hardEdge].
/// To prevent this behavior, consider setting [clipBehavior] to [Clip.none].
/// When [clipBehavior] is [Clip.none], InteractiveViewer may draw outside of
/// its original area of the screen, such as when a child is zoomed in and
/// increases in size. However, it will not receive gestures outside of its original area.
/// To prevent dead areas where InteractiveViewer does not receive gestures,
/// don't set [clipBehavior] or be sure that the InteractiveViewer widget is the
/// 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
/// sizes and child sizes.
///
......@@ -68,6 +70,7 @@ class InteractiveViewer extends StatefulWidget {
/// The [child] parameter must not be null.
InteractiveViewer({
Key? key,
this.clipBehavior = Clip.hardEdge,
this.alignPanAxis = false,
this.boundaryMargin = EdgeInsets.zero,
this.constrained = true,
......@@ -102,6 +105,13 @@ class InteractiveViewer extends StatefulWidget {
&& boundaryMargin.left.isFinite)),
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
/// or the vertical axis.
///
......@@ -1061,15 +1071,20 @@ class _InteractiveViewerState extends State<InteractiveViewer> with TickerProvid
);
if (!widget.constrained) {
child = ClipRect(
child: OverflowBox(
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(
clipBehavior: widget.clipBehavior,
child: child,
);
}
......
......@@ -985,6 +985,49 @@ void main() {
expect(scale, 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', () {
......
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