Flutter ClipPath

ClipPath

ClipPath按照自定义路径(Path)裁剪子组件,只保留路径内部的区域,外部直接裁掉(透明)。

同类裁剪组件对比:

  • ClipRect:矩形裁剪
  • ClipRRect:圆角矩形
  • ClipOval:椭圆 / 圆形
  • ClipPath:任意自定义 Path,最灵活

基础结构

swift 复制代码
ClipPath({
  Key? key,
  required CustomClipper<Path> clipper, // ✅核心:裁剪器,返回Path
  Clip clipBehavior = Clip.antiAlias, // 抗锯齿,antiAlias抗锯齿,hardEdge无抗锯齿
  Widget? child, // 要被裁剪的子组件
})

重点:必须写一个继承 CustomClipper<Path> 的类,重写 2 个方法

  1. getClip(Size size):根据子组件尺寸,返回裁剪 Path
  2. shouldReclip(CustomClipper<Path> oldClipper):判断是否需要重新裁剪(性能优化)

moveTo(100,200)。默认是(0,0)开始,这里表示切换开始点。

arduino 复制代码
Widget buildClipPathWidget() {
  return ClipPath(
    clipper: MyClipper1(),
    child: Container(color: Colors.red, width: 200, height: 200),
  );
}

class MyClipper1 extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    Path path = Path();
    path.moveTo(size.width / 2, 0);
    path.lineTo(0, size.height);
    path.lineTo(size.width, size.height);
    path.close();
    return path;
  }

  // 是否需要重新裁剪,如果路径没有修改,可以不重新裁剪
  @override
  bool shouldReclip(covariant CustomClipper<Path> oldClipper) {
    return false;
  }
}

也可以用这个做动画

scala 复制代码
import 'dart:async';

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(home: HomePage()));
}

class HomePage extends StatefulWidget {
  HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class ItemState {
  bool isExpanded = false;
}

class _HomePageState extends State<HomePage> {
  List<ItemState> itemStates = [];
  _HomePageState() {
    for (int i = 0; i < 2; i++) {
      itemStates.add(ItemState());
    }
  }

  double height = 0;
  Timer? _timer;
  double step = 0.5;
  @override
  initState() {
    super.initState();
    height = 0;
    _timer = Timer.periodic(Duration(milliseconds: 10), (timer) {
      setState(() {
        height += step;
        if (height > 100) {
          step = -0.5;
        } else if (height < 0) {
          step = 0.5;
        }
        debugPrint("height: $height");
      });
    });
  }

  var res = "no result";
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("贝塞尔曲线")),
      body: Container(
        // 如果不设置alignment,ExpansionTile就是全屏的
        alignment: Alignment.topLeft,
        color: Colors.blueGrey,
        width: MediaQuery.of(context).size.width,
        height: MediaQuery.of(context).size.height,
        child: buildClipPathWidget(context, height),
      ),
    );
  }

  @override
  dispose() {
    _timer?.cancel();
    super.dispose();
  }
}

Widget buildClipPathWidget(BuildContext context, double height) {
  return ClipPath(
    clipper: MyClipper3(height),
    child: Container(
      color: Colors.red,
      width: MediaQuery.of(context).size.width,
      height: 200,
    ),
  );
}

class MyClipper1 extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    Path path = Path();
    path.moveTo(size.width / 2, 0);
    path.lineTo(0, size.height);
    path.lineTo(size.width, size.height);
    path.close();
    return path;
  }

  // 是否需要重新裁剪,如果路径没有修改,可以不重新裁剪
  // 开发阶段,这里改成true。不然修改了路径,视图不会刷新
  @override
  bool shouldReclip(covariant CustomClipper<Path> oldClipper) {
    return true;
  }
}

class MyClipper2 extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    Path path = Path();
    path.lineTo(0, size.height - 30);
    // path.lineTo(size.width, size.height);
    // 添加贝塞尔曲线
    path.quadraticBezierTo(
      size.width / 2,
      size.height,
      size.width,
      size.height - 30,
    );
    path.lineTo(size.width, size.height - 30);
    path.lineTo(size.width, 0);
    path.close();
    return path;
  }

  // 是否需要重新裁剪,如果路径没有修改,可以不重新裁剪
  @override
  bool shouldReclip(covariant CustomClipper<Path> oldClipper) {
    return true;
  }
}

class MyClipper3 extends CustomClipper<Path> {
  double height;
  MyClipper3(this.height);
  @override
  Path getClip(Size size) {
    Path path = Path();
    path.lineTo(0, size.height - 30);
    // path.lineTo(size.width, size.height);
    // 添加贝塞尔曲线
    path.quadraticBezierTo(
      size.width / 2,
      size.height - 30 + height,
      size.width,
      size.height - 30,
    );
    path.lineTo(size.width, size.height + 100);
    path.lineTo(size.width, 0);
    path.close();
    return path;
  }

  // 是否需要重新裁剪,如果路径没有修改,可以不重新裁剪
  @override
  bool shouldReclip(MyClipper3 oldClipper) {
    return oldClipper.height != height;
  }
}
相关推荐
用户2181697049304 小时前
Flutter 折叠组件 ExpansionTile ExpansionPanelList
flutter
不羁的木木1 天前
给鸿蒙 App 增加用系统应用打开文件的能力 —— open_app_file 的鸿蒙使用指南
flutter·harmonyos
HouWan1 天前
Flutter: MediaQuery.of(context) 为什么可能拖慢页面?
android·flutter·ios
西西学代码1 天前
flutter---井字游戏
flutter
tushanxing1 天前
Day 2: 容器与单子布局基础
flutter
程序员老刘2 天前
跨平台开发地图 | 2026年9月
flutter·客户端
HouWan2 天前
Flutter iOS UISceneDelegate 迁移指南:理清新的 Scene 生命周期
flutter·ios·app
技术任我行XTing2 天前
【DFX系列】Flutter 鸿蒙应用外接纹理介绍及问题定位
flutter·harmonyos