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
5import 'package:flutter/material.dart';
6import 'package:vector_math/vector_math_64.dart' show Quad, Vector3;
7
8/// Flutter code sample for [InteractiveViewer.builder].
9
10void main() => runApp(const IVBuilderExampleApp());
11
12class 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
26class _IVBuilderExample extends StatefulWidget {
27 const _IVBuilderExample();
28
29 @override
30 State<_IVBuilderExample> createState() => _IVBuilderExampleState();
31}
32
33class _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
90typedef _CellBuilder = Widget Function(BuildContext context, int row, int column);
91
92class _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

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com