import 'package:flutter/material.dart';

import 'dart:math' as math;

class UITest extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _UITest();
  }
}

class _UITest extends State<UITest> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Color(0xFFF7F7F7),
        elevation: 0,
        title: Text(
          "测试",
          style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold),
        ),
        leading: GestureDetector(
          onTap: () {
            Navigator.of(context).pop();
          },
          child: Container(
            alignment: Alignment.centerRight,
            margin: EdgeInsets.only(left: 10),
            padding: EdgeInsets.all(6),
            child: Icon(
              Icons.arrow_back_ios,
              color: Colors.black,
              size: 24,
            ),
          ),
        ),
        titleSpacing: 2,
        leadingWidth: 56,
      ),
      body: AspectRatio(
        aspectRatio: 1,
        child: PhysicalShape(
          color: ElevationOverlay.applyOverlay(context, Colors.white, 2),
          elevation: 2,
          clipper: BottomAppBarClipper(
            shape: CircularHorizontalNotchedRectangle(),
          ),
          child: Container(
            margin: EdgeInsets.all(50),
            color: Colors.blue.withAlpha(123),
            alignment: Alignment.center,
            child: Text("主体内容"),
          ),
        ),
      ),
      endDrawer: Drawer(),
      bottomNavigationBar: BottomAppBar(
        color: Colors.deepPurpleAccent,
        shape: CircularNotchedRectangle(),
        child: Container(
          height: 50.0,
        ),
      ),
      extendBody: true,
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          print("点击");
        },
        child: Icon(Icons.add),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
    );
  }

}

class BottomAppBarClipper extends CustomClipper<Path> {

  final NotchedShape shape;

  const BottomAppBarClipper({this.shape});

  @override
  Path getClip(Size size) {
    final Rect button = Rect.fromCircle(center: Offset(size.width / 2, size.height / 2), radius: 20);
    return shape.getOuterPath(Offset.zero & size, button?.inflate(2));
  }

  @override
  bool shouldReclip(BottomAppBarClipper oldClipper) {
    return true;
  }
}

class CircularHorizontalNotchedRectangle extends NotchedShape {
  @override
  Path getOuterPath(Rect host, Rect guest) {
    if (guest == null || !host.overlaps(guest))
      return Path()..addRect(host);

    // 客人的形状是一个以客人矩形为边界的圆形。
    // 所以客人的半径是客人宽度的一半。
    final double notchRadius = guest.width / 2.0;

    // 我们从 3 段为缺口构建路径:
    // A 段 - 从主机顶部边缘到 B 段的贝塞尔曲线。
    // B 段 - 半径为 notchRadius 的圆弧。段
    // C - 从段 B 返回到主机顶部边缘的贝塞尔曲线。
    // 以下公式的详细解释和推导可在以下网址获得:https:goo.glUfzrqn

    const double s1 = 15.0;
    const double s2 = 1.0;

    final double r = notchRadius;
    final double a = -1.0 * r - s2;               //  半径+s2
    final double b = host.top - guest.center.dy; // 圆心到矩形的y轴距离

    final double n2 = math.sqrt(b * b * r * r * (a * a + b * b - r * r));
    final double p2xA = ((a * r * r) - n2) / (a * a + b * b);
    final double p2xB = ((a * r * r) + n2) / (a * a + b * b);
    final double p2yA = math.sqrt(r * r - p2xA * p2xA);
    final double p2yB = math.sqrt(r * r - p2xB * p2xB);

    final List<Offset> p = List<Offset>.filled(6, null, growable: false);

    // p0、p1 和 p2 是线段 A 的控制点。
    p[0] = Offset(a - s1, b);
    p[1] = Offset(a, b);
    final double cmp = b < 0 ? -1.0 : 1.0;
    p[2] = cmp * p2yA > cmp * p2yB ? Offset(p2xA, p2yA) : Offset(p2xB, p2yB);

    // p3、p4 和 p5 是线段 B 的控制点,它是线段 A 绕 y 轴的镜像。
    p[3] = Offset(-1.0 * p[2].dx, p[2].dy);
    p[4] = Offset(-1.0 * p[1].dx, p[1].dy);
    p[5] = Offset(-1.0 * p[0].dx, p[0].dy);

    // 将所有点转换回绝对坐标系。
    for (int i = 0; i < p.length; i += 1)
      p[i] = p[i] + guest.center;

    return Path()
      ..moveTo(host.left, host.top)
      ..lineTo(p[0].dx, p[0].dy)
      ..quadraticBezierTo(p[1].dx, p[1].dy, p[2].dx, p[2].dy)
      ..arcToPoint(
        p[3],
        radius: Radius.circular(notchRadius),
        clockwise: false,
      )
      ..quadraticBezierTo(p[4].dx, p[4].dy, p[5].dx, p[5].dy)
      ..lineTo(host.right, host.top)
      ..lineTo(host.right, host.bottom)
      ..lineTo(host.left, host.bottom)
      ..close();
  }

}