import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'dart:ui'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Stiletto', theme: ThemeData( primarySwatch: Colors.red, ), home: Scaffold( appBar: AppBar( // Here we take the value from the StilettoCanvas object that was created by // the App.build method, and use it to set our appbar title. title: Text("Stiletto"), ), body: Center( child: SizedBox.expand( child: StilettoCanvas() ) ) ) // home: Center( // child: SizedBox.expand( // child: StilettoCanvas() // ) // ) ); } } class StilettoCanvas extends StatefulWidget { StilettoCanvas({Key? key}) : super(key: key); // This widget is the home page of your application. It is stateful, meaning // that it has a State object (defined below) that contains fields that affect // how it looks. // This class is the configuration for the state. It holds the values (in this // case the title) provided by the parent (in this case the App widget) and // used by the build method of the State. Fields in a Widget subclass are // always marked "final". @override _StilettoCanvasState createState() => _StilettoCanvasState(); } class _StilettoCanvasState extends State { Offset _lastMousePos = Offset.zero; RenderBox? _currentRenderBox = null; GlobalKey _globalKey = GlobalKey(); _afterLayout(Duration) { RenderObject? renderObject = _globalKey.currentContext?.findRenderObject(); if (renderObject is RenderBox) { _currentRenderBox = renderObject; } } void _onPointerDown(PointerDownEvent event) { print(event.position); setState(() { Offset? localMousePos = _currentRenderBox?.globalToLocal(event.position); if (localMousePos is Offset) { _lastMousePos = localMousePos; } }); } @override Widget build(BuildContext context) { // This method is rerun every time setState is called, for instance as done // by the _incrementCounter method above. // // The Flutter framework has been optimized to make rerunning build methods // fast, so that you can just rebuild anything that needs updating rather // than having to individually change instances of widgets. WidgetsBinding.instance?.addPostFrameCallback(_afterLayout); return Listener( onPointerDown: _onPointerDown, child: ClipRect( child: CustomPaint( painter: StilettoPainter(_lastMousePos), ), key: _globalKey ) ); } } class StilettoPainter extends CustomPainter { StilettoPainter(this.sunPos) : super(); final Offset sunPos; Alignment _offsetToAlignment(Offset offset, Rect rect) { return Alignment( 2 * offset.dx / rect.width - 1, 2 * offset.dy / rect.height - 1 ); } @override void paint(Canvas canvas, Size size) { final Rect rect = Offset.zero & size; final RadialGradient gradient = RadialGradient( center: _offsetToAlignment(sunPos, rect), radius: 0.2, colors: [Color(0xFFFFFF00), Color(0xFF0099FF)], stops: [0.4, 1.0], ); canvas.drawRect( rect, Paint()..shader = gradient.createShader(rect), ); } @override bool shouldRepaint(StilettoPainter oldDelegate) { return sunPos != oldDelegate.sunPos; } }