Last active
May 5, 2024 15:25
-
-
Save PlugFox/218f5884cf61130433cd684783fd58b7 to your computer and use it in GitHub Desktop.
Seat Viewer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Seat Viewer | |
* https://gist.github.com/PlugFox/218f5884cf61130433cd684783fd58b7 | |
* https://dartpad.dev?id=218f5884cf61130433cd684783fd58b7 | |
* Mike Matiunin <plugfox@gmail.com>, 05 May 2024 | |
*/ | |
import 'dart:math' as math; | |
import 'package:flutter/foundation.dart' show listEquals; | |
import 'package:flutter/material.dart'; | |
void main() => runApp(const MainApp()); | |
typedef Seat = ({int number, bool reserved}); | |
final List<Seat> seats = () { | |
final rnd = math.Random(); | |
return <Seat>[ | |
for (int i = 1; i <= 225; i++) | |
( | |
number: i, | |
reserved: rnd.nextInt(5) == 0, | |
), | |
]; | |
}(); | |
class MainApp extends StatelessWidget { | |
const MainApp({super.key}); | |
@override | |
Widget build(BuildContext context) => MaterialApp( | |
title: 'Seats Viewer', | |
darkTheme: ThemeData.light(), | |
home: Scaffold( | |
body: SeatsViewer( | |
seats: seats, | |
), | |
), | |
); | |
} | |
class SeatsViewer extends StatefulWidget { | |
const SeatsViewer({ | |
required this.seats, | |
this.size = const Size.square(64), | |
this.margin = const EdgeInsets.all(2), | |
this.seatsPerRow, | |
super.key, | |
}); | |
final List<Seat> seats; | |
final int? seatsPerRow; | |
final Size size; | |
final EdgeInsets margin; | |
@override | |
State<SeatsViewer> createState() => _SeatsViewerState(); | |
} | |
class _SeatsViewerState extends State<SeatsViewer> { | |
final controller = TransformationController(); | |
late List<Widget> positions; | |
@override | |
void initState() { | |
super.initState(); | |
_rebuild(); | |
} | |
@override | |
void didUpdateWidget(covariant SeatsViewer oldWidget) { | |
super.didUpdateWidget(oldWidget); | |
if (oldWidget.size == widget.size && | |
oldWidget.margin == widget.margin && | |
listEquals(oldWidget.seats, widget.seats)) return; | |
_rebuild(); | |
} | |
@override | |
void dispose() { | |
super.dispose(); | |
controller.dispose(); | |
} | |
void _rebuild() { | |
positions = widget.seats | |
.map( | |
(seat) => Padding( | |
padding: widget.margin, | |
child: SizedBox.fromSize( | |
size: widget.size, | |
child: Opacity( | |
opacity: seat.reserved ? 0.5 : 1.0, | |
child: Material( | |
color: seat.reserved ? Colors.red : Colors.green, | |
shape: const RoundedRectangleBorder( | |
borderRadius: BorderRadius.all(Radius.circular(16)), | |
), | |
elevation: 4, | |
child: InkWell( | |
onTap: seat.reserved ? null : () {}, | |
borderRadius: BorderRadius.circular(16), | |
child: Center( | |
child: Text( | |
seat.number.toString(), | |
style: const TextStyle( | |
color: Colors.black, | |
fontSize: 16, | |
fontWeight: FontWeight.bold, | |
), | |
), | |
), | |
), | |
), | |
), | |
), | |
), | |
) | |
.toList(growable: false); | |
} | |
@override | |
Widget build(BuildContext context) => InteractiveViewer( | |
constrained: true, | |
transformationController: controller, | |
minScale: 0.8, | |
maxScale: 1.6, | |
boundaryMargin: const EdgeInsets.all(64), | |
child: Flow( | |
clipBehavior: Clip.none, | |
delegate: _SeatsFlowDelegate( | |
seatsPerRow: widget.seatsPerRow, | |
), | |
children: positions, | |
), | |
); | |
} | |
class _SeatsFlowDelegate extends FlowDelegate { | |
const _SeatsFlowDelegate({ | |
this.seatsPerRow, | |
}); | |
final int? seatsPerRow; | |
@override | |
void paintChildren(FlowPaintingContext context) { | |
final count = context.childCount; | |
if (count == 0) return; | |
final layoutSize = context.size; | |
final seatSize = context.getChildSize(0)!; | |
final seatsPerRow = this.seatsPerRow ?? math.sqrt(seats.length).ceil(); | |
final scaleX = layoutSize.width / (seatSize.width * seatsPerRow); | |
final scaleY = | |
layoutSize.height / (seatSize.height * (count / seatsPerRow).ceil()); | |
final scale = math.min(scaleX, scaleY); | |
for (var i = 0; i < count; i++) { | |
final x = (i % seatsPerRow) * seatSize.width; | |
final y = (i ~/ seatsPerRow) * seatSize.height; | |
context.paintChild( | |
i, | |
transform: Matrix4.identity() | |
..scale(scale, scale, 1.0) | |
..translate(x, y, 0), | |
); | |
} | |
} | |
@override | |
bool shouldRepaint(covariant _SeatsFlowDelegate oldDelegate) => | |
oldDelegate.seatsPerRow != seatsPerRow; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment