| // 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:vector_math/vector_math_64.dart' show Quad, Vector3; | |
| /// Flutter code sample for [InteractiveViewer.builder]. | |
| void main() => runApp(const IVBuilderExampleApp()); | |
| class IVBuilderExampleApp extends StatelessWidget { | |
| const IVBuilderExampleApp({super.key}); | |
| @override | |
| Widget build(BuildContext context) { | |
| return MaterialApp( | |
| home: Scaffold( | |
| appBar: AppBar(title: const Text('IV Builder Example')), | |
| body: const _IVBuilderExample(), | |
| ), | |
| ); | |
| } | |
| } | |
| class _IVBuilderExample extends StatefulWidget { | |
| const _IVBuilderExample(); | |
| @override | |
| State<_IVBuilderExample> createState() => _IVBuilderExampleState(); | |
| } | |
| class _IVBuilderExampleState extends State<_IVBuilderExample> { | |
| 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 <Vector3>[ | |
| quad.point1, | |
| quad.point2, | |
| quad.point3, | |
| ]) { | |
| if (point.x < xMin) { | |
| xMin = point.x; | |
| } else if (point.x > xMax) { | |
| xMax = point.x; | |
| } | |
| if (point.y < yMin) { | |
| yMin = point.y; | |
| } else if (point.y > yMax) { | |
| yMax = point.y; | |
| } | |
| } | |
| return Rect.fromLTRB(xMin, yMin, xMax, yMax); | |
| } | |
| @override | |
| Widget build(BuildContext context) { | |
| return Center( | |
| child: LayoutBuilder( | |
| builder: (BuildContext context, BoxConstraints constraints) { | |
| return InteractiveViewer.builder( | |
| boundaryMargin: const EdgeInsets.all(double.infinity), | |
| builder: (BuildContext context, Quad viewport) { | |
| return _TableBuilder( | |
| 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.withValues(alpha: 0.1), | |
| child: Align(child: Text('$row x $column')), | |
| ); | |
| }, | |
| ); | |
| }, | |
| ); | |
| }, | |
| ), | |
| ); | |
| } | |
| } | |
| typedef _CellBuilder = | |
| Widget Function(BuildContext context, int row, int column); | |
| class _TableBuilder extends StatelessWidget { | |
| const _TableBuilder({ | |
| required this.cellWidth, | |
| required this.cellHeight, | |
| required this.viewport, | |
| required this.builder, | |
| }); | |
| final double cellWidth; | |
| final double cellHeight; | |
| final Rect viewport; | |
| final _CellBuilder builder; | |
| @override | |
| Widget build(BuildContext context) { | |
| 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://api.flutter.dev/flutter/widgets/InteractiveViewer/constrained.html | |
| width: 1, | |
| height: 1, | |
| child: Stack( | |
| clipBehavior: .none, | |
| children: <Widget>[ | |
| 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), | |
| ), | |
| ], | |
| ), | |
| ); | |
| } | |
| } |