zhaoyadi vor 3 Jahren
Ursprung
Commit
e976dd9065

+ 128 - 0
example/lib/demo/demo3.dart

@@ -0,0 +1,128 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
+import 'package:luojigou_thinking_core/luojigou_thinking_core.dart';
+
+class Demo3 extends StatefulWidget {
+  const Demo3({Key? key}) : super(key: key);
+
+  @override
+  State<Demo3> createState() => _Demo3State();
+}
+
+class _Demo3State extends State<Demo3> {
+  @override
+  Widget build(BuildContext context) {
+    return ColoredBox(
+      color: Colors.black,
+      child: SingleChildScrollView(
+        child: Padding(
+          padding: const EdgeInsets.only(top: 20,left: 20,right: 20),
+          child: TrapezoidBox(
+            mainAxisSpacing: 16,
+            padding: const EdgeInsets.symmetric(
+              horizontal: 40,
+              vertical: 60,
+            ),
+            background: Colors.white,
+            children: const [
+              _HitTestBox(
+                size: Size(double.infinity, 300),
+              ),
+              _HitTestBox(
+                size: Size(double.infinity, 100),
+              ),
+              _HitTestBox(
+                size: Size(double.infinity, 200),
+              ),
+              _HitTestBox(
+                size: Size(double.infinity, 400),
+              ),
+              _HitTestBox(
+                size: Size(double.infinity, 200),
+              ),
+              _HitTestBox(
+                size: Size(double.infinity, 200),
+              ),
+              // Container(
+              //   width: double.infinity,
+              //   height: 200,
+              //   color: Colors.green,
+              // ),
+              // Container(
+              //   width: double.infinity,
+              //   height: 200,
+              //   color: Colors.red,
+              // ),
+              // Container(
+              //   width: double.infinity,
+              //   height: 200,
+              //   color: Colors.orange,
+              // ),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+}
+
+class _HitTestBox extends LeafRenderObjectWidget {
+  final Size size;
+
+  const _HitTestBox({
+    this.size = Size.zero,
+  });
+
+  @override
+  RenderObject createRenderObject(BuildContext context) {
+    return _RenderHitTestBox(size);
+  }
+
+  @override
+  void updateRenderObject(
+      BuildContext context, covariant _RenderHitTestBox renderObject) {
+    renderObject.setSize(size);
+  }
+}
+
+class _RenderHitTestBox extends RenderBox {
+  _RenderHitTestBox(this._size);
+
+  Color _color = Colors.deepPurple;
+
+  Size _size;
+
+  void setSize(Size value) {
+    _size = value;
+    markNeedsLayout();
+  }
+
+  @override
+  void performResize() {
+    assert(false);
+  }
+
+  @override
+  void performLayout() {
+    size = constraints.constrain(_size);
+  }
+
+  @override
+  bool hitTestSelf(Offset position) => size.contains(position);
+
+  @override
+  void handleEvent(PointerEvent event, BoxHitTestEntry entry) {
+    super.handleEvent(event, entry);
+    if (event is PointerDownEvent) {
+      _color = Colors.yellow;
+    } else if (event is PointerUpEvent) {
+      _color = Colors.deepPurple;
+    }
+    markNeedsPaint();
+  }
+
+  @override
+  void paint(PaintingContext context, Offset offset) {
+    context.canvas.drawRect(offset & size, Paint()..color = _color);
+  }
+}

+ 2 - 1
example/lib/main.dart

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
 
 import 'demo/demo1.dart';
 import 'demo/demo2.dart';
+import 'demo/demo3.dart';
 
 void main() {
   runApp(const MyApp());
@@ -34,7 +35,7 @@ class _TabDemoState extends State<_TabDemo> {
   Widget build(BuildContext context) {
     return const Scaffold(
       appBar: null,
-      body: Demo2(),
+      body: Demo3(),
     );
   }
 }

+ 15 - 0
lib/src/utils/plane_geometry_utils.dart

@@ -0,0 +1,15 @@
+import 'package:flutter/painting.dart';
+
+class PlaneGeometryUtils {
+  /// 求解参数直线方程的参数K
+  static double lineK(Offset x1, Offset x2) {
+    return (x1.dy - x2.dy) / (x1.dx - x2.dx);
+  }
+
+  /// 求解参数直线方程的参数C
+  static double lineC(Offset x1, Offset x2) {
+    return x1.dy - lineK(x1, x2) * x1.dx;
+  }
+
+
+}

+ 199 - 5
lib/src/widget/trapezoid.dart

@@ -1,15 +1,67 @@
 import 'package:flutter/material.dart';
 import 'package:flutter/rendering.dart';
+import 'dart:math' show max;
+
+class Quadrilateral {
+  final Offset a;
+  final Offset b;
+  final Offset c;
+  final Offset d;
+
+  Quadrilateral({
+    required this.a,
+    required this.b,
+    required this.c,
+    required this.d,
+  });
+
+  bool contains(Offset point) {
+    final double px = point.dx;
+    final double py = point.dy;
+
+    final double cp1 =
+        (b.dx - a.dx) * (py - a.dy) - (b.dy - a.dy) * (px - a.dx);
+    final double cp2 =
+        (c.dx - b.dx) * (py - b.dy) - (c.dy - b.dy) * (px - b.dx);
+    final double cp3 =
+        (d.dx - c.dx) * (py - c.dy) - (d.dy - c.dy) * (px - c.dx);
+    final double cp4 =
+        (a.dx - d.dx) * (py - d.dy) - (a.dy - d.dy) * (px - d.dx);
+    if ((cp1 > 0 && cp2 > 0 && cp3 > 0 && cp4 > 0) ||
+        (cp1 < 0 && cp2 < 0 && cp3 < 0 && cp4 < 0)) {
+      return true;
+    }
+    return false;
+  }
+}
 
 class TrapezoidBox extends MultiChildRenderObjectWidget {
+  final EdgeInsets padding;
+
+  final double mainAxisSpacing;
+
+  final Color? background;
+
   TrapezoidBox({
     Key? key,
     required List<Widget> children,
+    this.padding = EdgeInsets.zero,
+    this.mainAxisSpacing = 0,
+    this.background,
   }) : super(key: key, children: children);
 
   @override
-  RenderObject createRenderObject(BuildContext context) {
-    return _RenderTrapezoidBox();
+  _RenderTrapezoidBox createRenderObject(BuildContext context) {
+    return _RenderTrapezoidBox(padding, mainAxisSpacing, background);
+  }
+
+  @override
+  void updateRenderObject(
+      BuildContext context, covariant _RenderTrapezoidBox renderObject) {
+    renderObject
+      ..padding = padding
+      ..mainAxisSpacing = mainAxisSpacing
+      ..background = background;
   }
 }
 
@@ -24,6 +76,39 @@ class _RenderTrapezoidBox extends RenderBox
     with
         ContainerRenderObjectMixin<RenderBox, TrapezoidParentData>,
         RenderBoxContainerDefaultsMixin<RenderBox, TrapezoidParentData> {
+  _RenderTrapezoidBox(
+    this._padding,
+    this._mainAxisSpacing,
+    this._background,
+  );
+
+  EdgeInsets _padding;
+
+  set padding(EdgeInsets value) {
+    if (value != _padding) {
+      _padding = value;
+      markNeedsLayout();
+    }
+  }
+
+  double _mainAxisSpacing;
+
+  set mainAxisSpacing(double value) {
+    if (value != _mainAxisSpacing) {
+      _mainAxisSpacing = value;
+      markNeedsLayout();
+    }
+  }
+
+  Color? _background;
+
+  set background(Color? value) {
+    if (value != _background) {
+      _background = value;
+      markNeedsPaint();
+    }
+  }
+
   @override
   void setupParentData(covariant RenderObject child) {
     if (child.parentData is! TrapezoidParentData) {
@@ -33,27 +118,136 @@ class _RenderTrapezoidBox extends RenderBox
 
   @override
   void performLayout() {
+    var child = firstChild;
 
+    final childConstraints = constraints.deflate(_padding);
+    while (child != null) {
+      assert(child is RenderBox);
+      child.layout(childConstraints, parentUsesSize: true);
+      child = childAfter(child);
+    }
+
+    child = firstChild;
+
+    final double left = _padding.left;
+    final double right = constraints.maxWidth - _padding.right;
+
+    double start = _padding.top;
+    int count = 0;
+    while (child != null) {
+      assert(child is RenderBox);
+      count++;
+      final parentData = child.parentData as TrapezoidParentData;
+      final childSize = child.size;
+
+      var end = start + childSize.height;
+      if (count % 2 == 0) {
+        parentData.a = Offset(left, start);
+        parentData.b = Offset(right, start + 30);
+        parentData.c = Offset(left, end);
+        parentData.d = Offset(right, end - 30);
+
+        parentData.offset = Offset(left, start);
+      } else {
+        parentData.a = Offset(left, start + 30);
+        parentData.b = Offset(right, start);
+        parentData.c = Offset(left, end - 30);
+        parentData.d = Offset(right, end);
+
+        parentData.offset = Offset(left, start);
+      }
+
+      start = end - 30 + _mainAxisSpacing;
+
+      child = childAfter(child);
+    }
+
+    double height = _padding.top;
+    child = firstChild;
+
+    while (child != null) {
+      height += child.size.height + _mainAxisSpacing;
+      child = childAfter(child);
+    }
+
+    height += _padding.bottom - _mainAxisSpacing;
+    height -= (childCount - 1) * 30;
+    size = Size(
+      constraints.biggest.width,
+      height,
+    );
+  }
+
+  @override
+  bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
+    RenderBox? child = firstChild;
+    while (child != null) {
+      final TrapezoidParentData childParentData =
+          child.parentData! as TrapezoidParentData;
+      final Quadrilateral quadrilateral = Quadrilateral(
+        a: childParentData.a,
+        b: childParentData.b,
+        c: childParentData.d,
+        d: childParentData.c,
+      );
+
+      bool isIn = quadrilateral.contains(position);
+      if (isIn) {
+        final bool isHit = result.addWithPaintOffset(
+          offset: childParentData.offset,
+          position: position,
+          hitTest: (BoxHitTestResult result, Offset? transformed) {
+            assert(transformed == position - childParentData.offset);
+            return child!.hitTest(result, position: transformed!);
+          },
+        );
+        if (isHit) {
+          return true;
+        }
+      }
+      child = childParentData.nextSibling;
+    }
+    return false;
   }
 
   @override
   void paint(PaintingContext context, Offset offset) {
+    context.canvas.save();
+    context.canvas.translate(offset.dx, offset.dy);
+    _paintBackground(context.canvas);
     for (var child in getChildrenAsList()) {
-      _paintChild(context, offset, child);
+      _paintChild(context, child);
+    }
+    context.canvas.restore();
+  }
+
+  void _paintBackground(Canvas canvas) {
+    if(_background != null) {
+      Path path = Path();
+      path.moveTo(0, 30);
+      path.lineTo(size.width, 0);
+      path.lineTo(size.width, size.height);
+      path.lineTo(0, size.height);
+      path.close();
+      canvas.save();
+      canvas.clipPath(path);
+      canvas.drawRect(Offset.zero&size, Paint()..color = _background!);
+      canvas.restore();
     }
   }
 
-  void _paintChild(PaintingContext context, Offset offset, RenderBox child) {
+  void _paintChild(PaintingContext context, RenderBox child) {
     TrapezoidParentData parentData = child.parentData as TrapezoidParentData;
 
     context.canvas.save();
     Path path = Path();
     path.moveTo(parentData.a.dx, parentData.a.dy);
     path.lineTo(parentData.b.dx, parentData.b.dy);
-    path.lineTo(parentData.c.dx, parentData.c.dy);
     path.lineTo(parentData.d.dx, parentData.d.dy);
+    path.lineTo(parentData.c.dx, parentData.c.dy);
     path.close();
     context.canvas.clipPath(path);
+    context.paintChild(child, parentData.offset);
     context.canvas.restore();
   }
 }

+ 1 - 0
lib/widget.dart

@@ -2,3 +2,4 @@ export 'src/widget/put_away.dart';
 export 'src/widget/radar_chart.dart';
 export 'src/widget/reverse_flex.dart';
 export 'src/widget/vertical_tab_bar.dart';
+export 'src/widget/trapezoid.dart';