import 'dart:async'; import 'dart:math'; import 'package:flutter/material.dart'; import 'package:grpc/grpc_web.dart'; import 'package:cyclop/cyclop.dart'; import 'src/color_ref.dart'; import 'src/connection/ws.dart'; import 'src/widget/square.dart'; class TransformationsDemo extends StatefulWidget { const TransformationsDemo({Key? key}) : super(key: key); @override State createState() => _TransformationsDemoState(); } class _TransformationsDemoState extends State with TickerProviderStateMixin { final GlobalKey _targetKey = GlobalKey(); final Map, Widget> elements = , Widget>{}; Set swatches = Colors.primaries.map((e) => Color(e.value)).toSet(); final double minScale = 2; final ColorRef cc = ColorRef(Colors.blueGrey); final TransformationController _transformationController = TransformationController(); Animation? _animationReset; late AnimationController _controllerReset; Matrix4? _homeMatrix; final channel = GrpcWebClientChannel.xhr(Uri.parse('http://localhost:8080')); @override void initState() { super.initState(); // WebRTCChannels().connect((x) {}); } @override Widget build(BuildContext context) { // The scene is drawn by a CustomPaint, but user interaction is handled by // the InteractiveViewer parent widget. return Scaffold( backgroundColor: Theme.of(context).colorScheme.primary, // appBar: AppBar( // automaticallyImplyLeading: false, // title: const Text('title'), // ), body: Container( color: Colors.white, child: LayoutBuilder( builder: (context, constraints) { // Start the first render, start the scene centered in the viewport. if (_homeMatrix == null) { _homeMatrix = Matrix4.identity() * minScale ..translate(0, 0); _transformationController.value = _homeMatrix!; createComponentMap(); } return ClipRect( child: MouseRegion( cursor: SystemMouseCursors.click, child: GestureDetector( behavior: HitTestBehavior.opaque, // onTapUp: _onTapUp, child: InteractiveViewer( constrained: false, key: _targetKey, transformationController: _transformationController, boundaryMargin: const EdgeInsets.all(0), minScale: minScale, maxScale: 30, // onInteractionStart: _onScaleStart, onInteractionUpdate: _onInteractionUpdate, onInteractionEnd: _onInteractionEnd, child: SizedBox( width: 256 * 90, height: 256 * 53, child: Stack( children: [ getGrid(), ...elements.values.toList(), ], ), ), ), ), ), ); }, ), ), // persistentFooterButtons: [], floatingActionButton: ColorButton( darkMode: true, key: const Key('c2'), color: cc.getColor(), boxShape: BoxShape.circle, swatches: swatches, size: 64, config: const ColorPickerConfig( enableOpacity: false, enableLibrary: true, ), onColorChanged: (value) => setState(() { cc.setColor(value); }), onSwatchesChanged: (newSwatches) => setState(() => swatches = newSwatches), ), ); } Widget getGrid() { Widget grid = Container(); double scale = _transformationController.value.clone().getMaxScaleOnAxis(); if (scale > 10) { double opacity = 1.0; if (scale <= 15) { opacity = 1 - (15 - scale) / 5; } const oneGrid = GridPaper( color: Colors.black12, interval: 256, divisions: 16, subdivisions: 16, child: SizedBox( width: 256, height: 256, )); grid = Opacity( opacity: opacity, child: Stack( children: elements.keys .toList() .map((point) => Container( margin: EdgeInsets.only( left: 256.0 * point.x, top: 256.0 * point.y, ), child: oneGrid, )) .toList())); } return grid; } @override void dispose() { // _controllerReset.dispose(); super.dispose(); } void _onInteractionUpdate(ScaleUpdateDetails details) => _onUpdate(); void _onInteractionEnd(ScaleEndDetails details) { _onUpdate(); Timer(const Duration(milliseconds: 250), () => _onUpdate()); Timer(const Duration(milliseconds: 500), () => _onUpdate()); } void _onUpdate() { setState(() { createComponentMap(); }); } void createComponentMap() { var bounds = MediaQuery.of(context).size; var topLeft = _transformationController.toScene(bounds.topLeft(Offset.zero)); var bottomRight = _transformationController.toScene(bounds.bottomRight(Offset.zero)); var newSet = >{}; var rec = Rectangle.fromPoints( _offsetToIndex(topLeft), _offsetToIndex(bottomRight)); for (var i = rec.left; i <= rec.right; i++) { for (var j = rec.top; j <= rec.bottom; j++) { newSet.add(Point(i, j)); } } final oldKeys = Set.from(elements.keys); final newPoints = newSet.difference(oldKeys); final disposePoints = oldKeys.difference(newSet); for (var point in newPoints) { elements[point] = Container( key: Key(point.toString()), margin: EdgeInsets.only(left: 256.0 * point.x, top: 256.0 * point.y), child: Stack(children: [ // CustomPaint( // painter: OpenPainter(point), // ), Text(point.toString()), SquareComponent(color: cc, tile: point, key: Key(point.toString())), ]) // child: SquareComponent(point, cc), ); } for (var point in disposePoints) { elements.remove(point); } } } Point _offsetToIndex(Offset offset) { offset = offset / 256.0; var x = offset.dx.floor(); var y = offset.dy.floor(); if (x < 0) x = 0; if (y < 0) y = 0; return Point(x, y); } class OpenPainter extends CustomPainter { Point point; OpenPainter(Point this.point); @override void paint(Canvas canvas, Size size) { var paint1 = Paint() ..color = Color.fromARGB( 100, ((point.x * point.y * 7) % 64) * 4, ((point.x * 10039) % 64) * 4, ((point.y * 10061) % 64) * 4, ) ..style = PaintingStyle.fill; canvas.drawRect(const Offset(0, 0) & const Size(256, 256), paint1); } @override bool shouldRepaint(CustomPainter oldDelegate) => true; }