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(); Map, Widget> elements = , Widget>{}; Color appbarColor = Colors.blueGrey; Set swatches = Colors.primaries.map((e) => Color(e.value)).toSet(); double minScale = 2; ColorRef cc = ColorRef(); 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) {}); // _controllerReset = AnimationController( // vsync: this, // ); } @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!; elements = _start( context: context, channel: channel, transformationController: _transformationController); // _onUpdate(context: context); } return ClipRect( child: MouseRegion( cursor: SystemMouseCursors.click, child: GestureDetector( behavior: HitTestBehavior.opaque, // onTapUp: _onTapUp, child: InteractiveViewer( constrained: false, key: _targetKey, transformationController: _transformationController, boundaryMargin: EdgeInsets.all(0), minScale: minScale, maxScale: 30, // onInteractionStart: _onScaleStart, onInteractionUpdate: _onInteractionUpdate, onInteractionEnd: _onInteractionEnd, child: SizedBox( width: 256 * 90, height: 256 * 53, child: Stack(children: elements.values.toList()), ), ), ), ), ); }, ), ), // persistentFooterButtons: [], floatingActionButton: ColorButton( darkMode: true, key: const Key('c2'), color: appbarColor, boxShape: BoxShape.circle, swatches: swatches, size: 64, config: const ColorPickerConfig( enableOpacity: false, enableLibrary: true, ), onColorChanged: (value) => setState(() { appbarColor = value; cc.setColor(appbarColor); }), onSwatchesChanged: (newSwatches) => setState(() => swatches = newSwatches), ), ); } @override void dispose() { // _controllerReset.dispose(); super.dispose(); } void _onInteractionUpdate(ScaleUpdateDetails details) { _onUpdate(); } void _onUpdate({BuildContext? context}) { var context2 = context ?? _targetKey.currentContext!; setState(() { _start( context: context2, channel: channel, transformationController: _transformationController, elements: elements); }); } Map, Widget> _start( {required BuildContext context, required GrpcWebClientChannel channel, required TransformationController transformationController, Map, Widget>? elements}) { 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)); } } var elements2 = elements ?? , Widget>{}; newSet .difference(Set.from(elements2.keys)) .forEach((point) => elements2[point] ??= Container( margin: EdgeInsets.only(left: 256.0 * point.x, top: 256.0 * point.y), child: SquareComponent(point, cc), )); Set.from(elements2.keys).difference(newSet).forEach( (point) => elements2.remove(point), ); // print(elements2.length); // WebRTCChannels().connect(); return elements2; } void _onInteractionEnd(ScaleEndDetails details) => _onUpdate(); } 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); }