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;
  }
}
相关推荐
恋猫de小郭7 小时前
最近 Android 有什么更新:Android Dev Summit 2026 技术解读
android·前端·flutter
好的佩奇18 小时前
Dart 之类修饰符
android·flutter·dart
好的佩奇011 天前
Dart 之类修饰符
android·flutter·dart
Jewel1051 天前
文字为什么总是不居中!记录一个bug
android·flutter·ios
2601_949847752 天前
Form-data 跨端三方库仓颉适配全流程
前端·javascript·flutter
大龄秃头程序员2 天前
Flutter 中我为什么把 GetX 定位为高效率方案,而不是默认架构方案
flutter
传奇开心果编程2 天前
组合优先开发范式:从汉字造字原理到声明式UI组件组合思想
android·flutter·ios·swiftui·移动开发·harmonyos·android jetpack
2501_944521593 天前
Ramda 跨端三方库仓颉适配全流程
开发语言·javascript·flutter·php
大龄秃头程序员3 天前
Flutter 架构里总被混淆的 IoC、DI、Service Locator,到底是什么关系?
flutter
事圆则缓3 天前
Flutter 与原生交互的最后一层:RTC 视频帧怎么走
flutter·音视频·实时音视频