1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | import 'package:flutter/material.dart'; |
6 | import 'package:vector_math/vector_math_64.dart' show Quad, Vector3; |
7 | |
8 | /// Flutter code sample for [InteractiveViewer.builder]. |
9 | |
10 | void main() => runApp(const IVBuilderExampleApp()); |
11 | |
12 | class IVBuilderExampleApp extends StatelessWidget { |
13 | const IVBuilderExampleApp({super.key}); |
14 | |
15 | @override |
16 | Widget build(BuildContext context) { |
17 | return MaterialApp( |
18 | home: Scaffold( |
19 | appBar: AppBar(title: const Text('IV Builder Example' )), |
20 | body: const _IVBuilderExample(), |
21 | ), |
22 | ); |
23 | } |
24 | } |
25 | |
26 | class _IVBuilderExample extends StatefulWidget { |
27 | const _IVBuilderExample(); |
28 | |
29 | @override |
30 | State<_IVBuilderExample> createState() => _IVBuilderExampleState(); |
31 | } |
32 | |
33 | class _IVBuilderExampleState extends State<_IVBuilderExample> { |
34 | static const double _cellWidth = 160.0; |
35 | static const double _cellHeight = 80.0; |
36 | |
37 | // Returns the axis aligned bounding box for the given Quad, which might not |
38 | // be axis aligned. |
39 | Rect axisAlignedBoundingBox(Quad quad) { |
40 | double xMin = quad.point0.x; |
41 | double xMax = quad.point0.x; |
42 | double yMin = quad.point0.y; |
43 | double yMax = quad.point0.y; |
44 | for (final Vector3 point in <Vector3>[quad.point1, quad.point2, quad.point3]) { |
45 | if (point.x < xMin) { |
46 | xMin = point.x; |
47 | } else if (point.x > xMax) { |
48 | xMax = point.x; |
49 | } |
50 | |
51 | if (point.y < yMin) { |
52 | yMin = point.y; |
53 | } else if (point.y > yMax) { |
54 | yMax = point.y; |
55 | } |
56 | } |
57 | |
58 | return Rect.fromLTRB(xMin, yMin, xMax, yMax); |
59 | } |
60 | |
61 | @override |
62 | Widget build(BuildContext context) { |
63 | return Center( |
64 | child: LayoutBuilder( |
65 | builder: (BuildContext context, BoxConstraints constraints) { |
66 | return InteractiveViewer.builder( |
67 | boundaryMargin: const EdgeInsets.all(double.infinity), |
68 | builder: (BuildContext context, Quad viewport) { |
69 | return _TableBuilder( |
70 | cellWidth: _cellWidth, |
71 | cellHeight: _cellHeight, |
72 | viewport: axisAlignedBoundingBox(viewport), |
73 | builder: (BuildContext context, int row, int column) { |
74 | return Container( |
75 | height: _cellHeight, |
76 | width: _cellWidth, |
77 | color: row % 2 + column % 2 == 1 ? Colors.white : Colors.grey.withOpacity(0.1), |
78 | child: Align(child: Text(' $row x $column' )), |
79 | ); |
80 | }, |
81 | ); |
82 | }, |
83 | ); |
84 | }, |
85 | ), |
86 | ); |
87 | } |
88 | } |
89 | |
90 | typedef _CellBuilder = Widget Function(BuildContext context, int row, int column); |
91 | |
92 | class _TableBuilder extends StatelessWidget { |
93 | const _TableBuilder({ |
94 | required this.cellWidth, |
95 | required this.cellHeight, |
96 | required this.viewport, |
97 | required this.builder, |
98 | }); |
99 | |
100 | final double cellWidth; |
101 | final double cellHeight; |
102 | final Rect viewport; |
103 | final _CellBuilder builder; |
104 | |
105 | @override |
106 | Widget build(BuildContext context) { |
107 | final int firstRow = (viewport.top / cellHeight).floor(); |
108 | final int lastRow = (viewport.bottom / cellHeight).ceil(); |
109 | final int firstCol = (viewport.left / cellWidth).floor(); |
110 | final int lastCol = (viewport.right / cellWidth).ceil(); |
111 | |
112 | // This will create and render exactly (lastRow - firstRow) * (lastCol - firstCol) cells |
113 | |
114 | return SizedBox( |
115 | // Stack needs constraints, even though we then Clip.none outside of them. |
116 | // InteractiveViewer.builder always sets constrained to false, giving infinite constraints to the child. |
117 | // See: https://api.flutter.dev/flutter/widgets/InteractiveViewer/constrained.html |
118 | width: 1, |
119 | height: 1, |
120 | child: Stack( |
121 | clipBehavior: Clip.none, |
122 | children: <Widget>[ |
123 | for (int row = firstRow; row < lastRow; row++) |
124 | for (int col = firstCol; col < lastCol; col++) |
125 | Positioned( |
126 | left: col * cellWidth, |
127 | top: row * cellHeight, |
128 | child: builder(context, row, col), |
129 | ), |
130 | ], |
131 | ), |
132 | ); |
133 | } |
134 | } |
135 | |