import 'dart:ui'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; class Demo2 extends StatefulWidget { const Demo2({Key? key}) : super(key: key); @override State createState() => _Demo2State(); } class _Demo2State extends State { late ScrollController _scrollController; @override void initState() { super.initState(); _scrollController = ScrollController(); } @override Widget build(BuildContext context) { return Row( children: [ ColoredBox( color: const Color(0xFFC8DDFF), child: SizedBox( width: 55, child: ListView.builder( itemBuilder: (context, index) { return CustomPaint( painter: index == 2 ? _Painter() : null, child: SizedBox( width: double.infinity, height: 100, child: Center(child: Text("Hello $index")), ), ); }, ), ), ), Expanded( child: Column( children: [ Expanded( child: Container( color: Colors.yellow, ), ), const Expanded( child: TestWidget( child: TestWidget(), ), ), Expanded( child: Container( color: Colors.lightGreen, ), ), Expanded( child: Container( color: Colors.redAccent, ), ), ], ), ), ], ); } } const double _width = 18.74; const double _height = 158.41; class _Painter extends CustomPainter { @override void paint(Canvas canvas, Size size) { canvas.drawCircle( Offset(size.width - 5 - 2, size.height / 2), 2, Paint()..color = Colors.green, ); var path = Path(); path.reset(); path.moveTo(size.width, size.height / 2 - 20); path.lineTo(size.width + 20, size.height / 2); path.lineTo(size.width, size.height / 2 + 20); path.close(); canvas.drawPath( path, Paint() ..color = Colors.blue ..style = PaintingStyle.fill, ); // canvas.drawRect(Offset.zero & size, Paint()..color = Colors.green); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => true; } class TestWidget extends SingleChildRenderObjectWidget { const TestWidget({ Key? key, Widget? child, }) : super(key: key, child: child); @override RenderObject createRenderObject(BuildContext context) { return _RenderTest(); } } class _RenderTest extends RenderBox with RenderObjectWithChildMixin { @override void performLayout() { child?.layout(constraints.deflate(const EdgeInsets.all(20))); size = constraints.constrain(Size.infinite); } @override Size computeDryLayout(BoxConstraints constraints) { return constraints.constrain(Size.infinite); Listener listener; } @override void paint(PaintingContext context, Offset offset) { super.paint(context, offset); Color color; if (parent is _RenderTest) { if (_touchOffset != null) { color = Colors.black; } else { color = Colors.red; } } else { if (_touchOffset != null) { color = Colors.tealAccent; } else { color = Colors.deepPurple; } } context.canvas.drawRect(offset & size, Paint() ..color = color); if (child != null) { context.paintChild(child!, offset); } } @override bool hitTestChildren(BoxHitTestResult result, {required Offset position}) { if(child != null){ return child!.hitTest(result, position: position); } return super.hitTestChildren(result, position: position); } @override bool hitTestSelf(Offset position) => size.contains(position); Offset? _touchOffset; GestureRecognizer gestureDetector = TapGestureRecognizer() ..onTap = () { debugPrint("TapGestureRecognizer onTap"); } ..onTapDown = (details) { debugPrint("TapGestureRecognizer onTapDown"); } ..onTapUp = (details) { debugPrint("TapGestureRecognizer onTapUp"); } ..onTapCancel = () { debugPrint("TapGestureRecognizer onTapCancel"); }; @override void handleEvent(PointerEvent event, covariant HitTestEntry entry) { debugPrint(event.runtimeType.toString()); debugPrint(event.position.toString()); if (event is PointerDownEvent) { _touchOffset = event.localPosition; gestureDetector.addPointer(event); } else if (event is PointerMoveEvent) { _touchOffset = event.localPosition; } else { _touchOffset = null; } markNeedsPaint(); } }