diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 9e86344fc59e0..1fdeba8ae2c46 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -b09cf532697be32cf0c5009c8517a969e4708cca +a62228fc4cde953c46fdcf9787340fde72c62420 diff --git a/bin/internal/fuchsia-linux.version b/bin/internal/fuchsia-linux.version index 148f5c770c3df..5816fa42fdace 100644 --- a/bin/internal/fuchsia-linux.version +++ b/bin/internal/fuchsia-linux.version @@ -1 +1 @@ -5fHjoMTz4s7Ni_lYivRcAWMYXCQmru1TQHTJhiL72B4C +zAMU0LoSMxRGKjJ1KaJ9TZS3YSMvtFFRftl3ziEuwX4C diff --git a/bin/internal/fuchsia-mac.version b/bin/internal/fuchsia-mac.version index be42c849d20d0..fd2799068a49e 100644 --- a/bin/internal/fuchsia-mac.version +++ b/bin/internal/fuchsia-mac.version @@ -1 +1 @@ -fbvo-hhGcTFQTIu_k0HSw2d1hUzPdMQTzvvOqcX1YfgC +ciPTp_Hy7XM6gpJSTXkh2ZxMNd7x_TdRASKR5TdMCm0C diff --git a/examples/api/lib/widgets/interactive_viewer/interactive_viewer.builder.0.dart b/examples/api/lib/widgets/interactive_viewer/interactive_viewer.builder.0.dart index a85d51670cd45..3b99f87434d43 100644 --- a/examples/api/lib/widgets/interactive_viewer/interactive_viewer.builder.0.dart +++ b/examples/api/lib/widgets/interactive_viewer/interactive_viewer.builder.0.dart @@ -5,7 +5,6 @@ // Flutter code sample for InteractiveViewer.builder import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:vector_math/vector_math_64.dart' show Quad, Vector3; void main() => runApp(const IVBuilderExampleApp()); @@ -32,77 +31,34 @@ class _IVBuilderExample extends StatefulWidget { } class _IVBuilderExampleState extends State<_IVBuilderExample> { - final TransformationController _transformationController = - TransformationController(); - - static const double _cellWidth = 200.0; - static const double _cellHeight = 26.0; - - // Returns true iff the given cell is currently visible. Caches viewport - // calculations. - Quad? _cachedViewport; - late int _firstVisibleRow; - late int _firstVisibleColumn; - late int _lastVisibleRow; - late int _lastVisibleColumn; - bool _isCellVisible(int row, int column, Quad viewport) { - if (viewport != _cachedViewport) { - final Rect aabb = _axisAlignedBoundingBox(viewport); - _cachedViewport = viewport; - _firstVisibleRow = (aabb.top / _cellHeight).floor(); - _firstVisibleColumn = (aabb.left / _cellWidth).floor(); - _lastVisibleRow = (aabb.bottom / _cellHeight).floor(); - _lastVisibleColumn = (aabb.right / _cellWidth).floor(); - } - return row >= _firstVisibleRow && - row <= _lastVisibleRow && - column >= _firstVisibleColumn && - column <= _lastVisibleColumn; - } - - // Returns the axis aligned bounding box for the given Quad, which might not - // be axis aligned. - Rect _axisAlignedBoundingBox(Quad quad) { - double? xMin; - double? xMax; - double? yMin; - double? yMax; + static const double _cellWidth = 160.0; + static const double _cellHeight = 80.0; + + // Returns the axis aligned bounding box for the given Quad, which might not be axis aligned. + Rect axisAlignedBoundingBox(Quad quad) { + double xMin = quad.point0.x; + double xMax = quad.point0.x; + double yMin = quad.point0.y; + double yMax = quad.point0.y; for (final Vector3 point in [ - quad.point0, quad.point1, quad.point2, quad.point3 ]) { - if (xMin == null || point.x < xMin) { + if (point.x < xMin) { xMin = point.x; - } - if (xMax == null || point.x > xMax) { + } else if (point.x > xMax) { xMax = point.x; } - if (yMin == null || point.y < yMin) { + + if (point.y < yMin) { yMin = point.y; - } - if (yMax == null || point.y > yMax) { + } else if (point.y > yMax) { yMax = point.y; } } - return Rect.fromLTRB(xMin!, yMin!, xMax!, yMax!); - } - void _onChangeTransformation() { - setState(() {}); - } - - @override - void initState() { - super.initState(); - _transformationController.addListener(_onChangeTransformation); - } - - @override - void dispose() { - _transformationController.removeListener(_onChangeTransformation); - super.dispose(); + return Rect.fromLTRB(xMin, yMin, xMax, yMax); } @override @@ -111,32 +67,25 @@ class _IVBuilderExampleState extends State<_IVBuilderExample> { child: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { return InteractiveViewer.builder( - alignPanAxis: true, - scaleEnabled: false, - transformationController: _transformationController, + boundaryMargin: const EdgeInsets.all(double.infinity), builder: (BuildContext context, Quad viewport) { - // A simple extension of Table that builds cells. return _TableBuilder( - rowCount: 60, - columnCount: 6, - cellWidth: _cellWidth, - builder: (BuildContext context, int row, int column) { - if (!_isCellVisible(row, column, viewport)) { - debugPrint('removing cell ($row, $column)'); - return Container(height: _cellHeight); - } - debugPrint('building cell ($row, $column)'); - return Container( - height: _cellHeight, - color: row % 2 + column % 2 == 1 - ? Colors.white - : Colors.grey.withOpacity(0.1), - child: Align( - alignment: Alignment.centerLeft, - child: Text('$row x $column'), - ), - ); - }); + cellWidth: _cellWidth, + cellHeight: _cellHeight, + viewport: axisAlignedBoundingBox(viewport), + builder: (BuildContext context, int row, int column) { + return Container( + height: _cellHeight, + width: _cellWidth, + color: row % 2 + column % 2 == 1 + ? Colors.white + : Colors.grey.withOpacity(0.1), + child: Align( + child: Text('$row x $column'), + ), + ); + }, + ); }, ); }, @@ -150,38 +99,44 @@ typedef _CellBuilder = Widget Function( class _TableBuilder extends StatelessWidget { const _TableBuilder({ - required this.rowCount, - required this.columnCount, required this.cellWidth, + required this.cellHeight, + required this.viewport, required this.builder, - }) : assert(rowCount > 0), - assert(columnCount > 0); + }); - final int rowCount; - final int columnCount; final double cellWidth; + final double cellHeight; + final Rect viewport; final _CellBuilder builder; @override Widget build(BuildContext context) { - return Table( - // ignore: prefer_const_literals_to_create_immutables - columnWidths: { - for (int column = 0; column < columnCount; column++) - column: FixedColumnWidth(cellWidth), - }, - // ignore: prefer_const_literals_to_create_immutables - children: [ - for (int row = 0; row < rowCount; row++) - // ignore: prefer_const_constructors - TableRow( - // ignore: prefer_const_literals_to_create_immutables - children: [ - for (int column = 0; column < columnCount; column++) - builder(context, row, column), - ], - ), - ], + final int firstRow = (viewport.top / cellHeight).floor(); + final int lastRow = (viewport.bottom / cellHeight).ceil(); + final int firstCol = (viewport.left / cellWidth).floor(); + final int lastCol = (viewport.right / cellWidth).ceil(); + + // This will create and render exactly (lastRow - firstRow) * (lastCol - firstCol) cells + + return SizedBox( + // Stack needs constraints, even though we then Clip.none outside of them. + // InteractiveViewer.builder always sets constrained to false, giving infinite constraints to the child. + // See: https://master-api.flutter.dev/flutter/widgets/InteractiveViewer/constrained.html + width: 1, + height: 1, + child: Stack( + clipBehavior: Clip.none, + children: [ + for (int row = firstRow; row < lastRow; row++) + for (int col = firstCol; col < lastCol; col++) + Positioned( + left: col * cellWidth, + top: row * cellHeight, + child: builder(context, row, col), + ), + ], + ), ); } } diff --git a/examples/api/test/widgets/interactive_viewer/interactive_viewer.builder.0_test.dart b/examples/api/test/widgets/interactive_viewer/interactive_viewer.builder.0_test.dart new file mode 100644 index 0000000000000..4cfaa619894f6 --- /dev/null +++ b/examples/api/test/widgets/interactive_viewer/interactive_viewer.builder.0_test.dart @@ -0,0 +1,35 @@ +// 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/widgets.dart'; +import 'package:flutter_api_samples/widgets/interactive_viewer/interactive_viewer.builder.0.dart' + as example; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('has correct items on screen', (WidgetTester tester) async { + await tester.pumpWidget( + const example.IVBuilderExampleApp(), + ); + + final Finder positionedFinder = find.byType(Positioned); + final Finder zeroFinder = find.text('0 x 0'); + final Finder nineFinder = find.text('0 x 9'); + + expect(positionedFinder, findsNWidgets(35)); + expect(zeroFinder, findsOneWidget); + expect(nineFinder, findsNothing); + + const Offset firstLocation = Offset(750.0, 100.0); + final TestGesture gesture = await tester.startGesture(firstLocation); + + const Offset secondLocation = Offset(50.0, 100.0); + await gesture.moveTo(secondLocation); + await tester.pump(); + + expect(positionedFinder, findsNWidgets(42)); + expect(nineFinder, findsOneWidget); + expect(zeroFinder, findsNothing); + }); +} diff --git a/packages/flutter/lib/src/material/slider_theme.dart b/packages/flutter/lib/src/material/slider_theme.dart index 9b42d095327de..cd4121b6c91e7 100644 --- a/packages/flutter/lib/src/material/slider_theme.dart +++ b/packages/flutter/lib/src/material/slider_theme.dart @@ -1524,25 +1524,7 @@ mixin BaseSliderTrackShape { /// * [RoundedRectSliderTrackShape], for a similar track with rounded edges. class RectangularSliderTrackShape extends SliderTrackShape with BaseSliderTrackShape { /// Creates a slider track that draws 2 rectangles. - const RectangularSliderTrackShape({ - @Deprecated( - 'It no longer has any effect because the thumb does not shrink when the slider is disabled now. ' - 'This feature was deprecated after v1.26.0-18.0.pre.', - ) - this.disabledThumbGapWidth = 2.0, - }); - - /// Horizontal spacing, or gap, between the disabled thumb and the track. - /// - /// This is only used when the slider is disabled. There is no gap around - /// the thumb and any part of the track when the slider is enabled. The - /// Material spec defaults this gap width 2, which is half of the disabled - /// thumb radius. - @Deprecated( - 'It no longer has any effect because the thumb does not shrink when the slider is disabled now. ' - 'This feature was deprecated after v1.26.0-18.0.pre.', - ) - final double disabledThumbGapWidth; + const RectangularSliderTrackShape(); @override void paint( diff --git a/packages/flutter/lib/src/widgets/interactive_viewer.dart b/packages/flutter/lib/src/widgets/interactive_viewer.dart index 4137b929f979f..5ab1498702029 100644 --- a/packages/flutter/lib/src/widgets/interactive_viewer.dart +++ b/packages/flutter/lib/src/widgets/interactive_viewer.dart @@ -37,15 +37,15 @@ typedef InteractiveViewerWidgetBuilder = Widget Function(BuildContext context, Q /// 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. -/// /// The [child] must not be null. /// /// See also: /// * The [Flutter Gallery's transformations demo](https://github.com/flutter/gallery/blob/master/lib/demos/reference/transformations_demo.dart), /// which includes the use of InteractiveViewer. +/// * The [flutter-go demo](https://github.com/justinmc/flutter-go), which includes robust positioning of an InteractiveViewer child +/// that works for all screen sizes and child sizes. +/// * The [Lazy Flutter Performance Session](https://www.youtube.com/watch?v=qax_nOpgz7E), which includes the use of an InteractiveViewer to +/// performantly view subsets of a large set of widgets using the builder constructor. /// /// {@tool dartpad} /// This example shows a simple Container that can be panned and zoomed.