blob: a85d51670cd4510154e34eb62c0d50602838db7f [file] [log] [blame]
// 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.
// 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());
class IVBuilderExampleApp extends StatelessWidget {
const IVBuilderExampleApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('IV Builder Example'),
),
body: _IVBuilderExample(),
),
);
}
}
class _IVBuilderExample extends StatefulWidget {
@override
State<_IVBuilderExample> createState() => _IVBuilderExampleState();
}
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;
for (final Vector3 point in <Vector3>[
quad.point0,
quad.point1,
quad.point2,
quad.point3
]) {
if (xMin == null || point.x < xMin) {
xMin = point.x;
}
if (xMax == null || point.x > xMax) {
xMax = point.x;
}
if (yMin == null || point.y < yMin) {
yMin = point.y;
}
if (yMax == null || 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();
}
@override
Widget build(BuildContext context) {
return Center(
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return InteractiveViewer.builder(
alignPanAxis: true,
scaleEnabled: false,
transformationController: _transformationController,
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'),
),
);
});
},
);
},
),
);
}
}
typedef _CellBuilder = Widget Function(
BuildContext context, int row, int column);
class _TableBuilder extends StatelessWidget {
const _TableBuilder({
required this.rowCount,
required this.columnCount,
required this.cellWidth,
required this.builder,
}) : assert(rowCount > 0),
assert(columnCount > 0);
final int rowCount;
final int columnCount;
final double cellWidth;
final _CellBuilder builder;
@override
Widget build(BuildContext context) {
return Table(
// ignore: prefer_const_literals_to_create_immutables
columnWidths: <int, TableColumnWidth>{
for (int column = 0; column < columnCount; column++)
column: FixedColumnWidth(cellWidth),
},
// ignore: prefer_const_literals_to_create_immutables
children: <TableRow>[
for (int row = 0; row < rowCount; row++)
// ignore: prefer_const_constructors
TableRow(
// ignore: prefer_const_literals_to_create_immutables
children: <Widget>[
for (int column = 0; column < columnCount; column++)
builder(context, row, column),
],
),
],
);
}
}