阅读视图

发现新文章,点击刷新页面。

FuncAvatar: 你的头像氛围感神器 🤥🤥🤥

前言

相信大家在节假日中,总能发现,大家的头像的覆上了一层节日限定皮肤,那一下节日氛围就满满的了!🎊🎊🎊

自然而然,就衍生出了,下面那些头像制作网站,帮你赶时髦。

但那些远远不够丰富和完善,你为不同的效果到处奔波,甚至还要付费时,就些许狼狈了。
因此我要打造一款功能更强大更丰富开源免费的头像制作插件

设计

概述

logo128.png

FuncAvatar 是一款专为个性化头像制作而设计的浏览器插件。它集成了多种创意风格处理功能,让用户能够轻松地将普通头像转换为独特的艺术作品。


🎪 适用场景:

  • 社交媒体:为微信、QQ、微博等平台制作个性头像
  • 节日庆祝:快速制作节日主题头像
  • 游戏娱乐:创建复古像素风格的游戏头像
  • 爱国情感:添加国旗元素展示爱国情怀

流程图

graph TD
    A[上传图片] --> B[裁剪图片]
    B --> C[选择风格]
    
    C -->|覆盖风| D1[选择主题与透明度]
    C -->|像素风| D2[设置像素大小与模式]
    C -->|国旗风| D3[选择国旗与样式]
    C -->|后续更多风格| D4[......]
        
    D1 --> E[合成预览]
    D2 --> E
    D3 --> E
    D4 --> E
    
    E --> F[实时预览]
    F --> G{满意效果?}
    G -->|否| C
    G -->|是| H[下载图片]
    
 
    H --> I[浏览器下载完成]
    
    style A fill:#e3f2fd
    style C fill:#fff8e1
    style E fill:#f3e5f5
    style G fill:#fff3e0

项目结构

FuncAvatar/
├── manifest.json              # 插件配置文件
├── background.js              # 后台服务脚本
├── popup.html                 # 插件弹窗界面
├── popup.css                  # 插件专用样式
├── js/                        # JavaScript模块
│   ├── init.js                # 初始化脚本
│   ├── core/                  # 核心模块
│   │   └── AvatarMaker.js     # 主控制器类
│   ├── handlers/              # 功能处理模块
│   │   ├── ImageHandler.js    # 图片处理
│   │   ├── CropHandler.js     # 图片裁剪
│   │   ├── ThemeHandler.js    # 主题覆盖
│   │   ├── PixelHandler.js    # 像素风格
│   │   └── FlagHandler.js     # 国旗风格
│   └── ui/                    # UI控制模块
│       └── UIController.js    # 界面控制器
├── icons/                     # 插件图标
│   ├── ...
├── images/                    # 主题图片资源
│   ├── ...
├── flag/                      # 标准国旗图片
│   ├── ...
├── flag-fly/                  # 飘动效果国旗图片
│   ├── ...
└── README.md                  # 说明文档

实现

上传

上传 就是上传头像的过程。用户上传图片后弹出裁剪窗口,可通过拖拽或缩放调整裁剪区域,系统记录裁剪比例,最终生成裁剪后的图片并更新预览。

核心代码:

this.cropData = {
    x: 0,      // 相对于图片左上角的比例位置 (0~1)
    y: 0,
    width: 0,  // 裁剪区域宽度比例
    height: 0
};

// 更新裁剪数据(计算比例)
updateCropData() {
    const imageRect = this.cropImage.getBoundingClientRect();
    const boxRect = this.cropBox.getBoundingClientRect();

    this.cropData = {
        x: (boxRect.left - imageRect.left) / imageRect.width,
        y: (boxRect.top - imageRect.top) / imageRect.height,
        width: boxRect.width / imageRect.width,
        height: boxRect.height / imageRect.height
    };
}
// 拖拽裁剪框移动
dragCropBox(event) {
    let newLeft = event.clientX - this.dragStart.x;
    let newTop = event.clientY - this.dragStart.y;

    // 限制在图片范围内
    newLeft = Math.max(minLeft, Math.min(maxLeft, newLeft));
    newTop = Math.max(minTop, Math.min(maxTop, newTop));

    this.cropBox.style.left = newLeft + 'px';
    this.cropBox.style.top = newTop + 'px';
    this.updateCropData();
}
// 调整裁剪框大小(保持正方形)
resizeCropBox(event) {
    const mouseX = event.clientX;
    const mouseY = event.clientY;

    // 根据拖动方向调整宽高
    switch (this.resizeHandle) {
        case 'nw': ...; break;
        case 'ne': ...; break;
        case 'sw': ...; break;
        case 'se': ...; break;
    }

    // 保持正方形
    const size = Math.min(newWidth, newHeight);
    newWidth = newHeight = Math.max(50, size);

    this.cropBox.style.width = newWidth + 'px';
    this.cropBox.style.height = newHeight + 'px';
    this.updateCropData();
}
// 生成裁剪后图片
createCroppedImage() {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    const size = 400;
    canvas.width = size;
    canvas.height = size;

    const img = this.cropImage;
    const cropX = this.cropData.x * img.naturalWidth;
    const cropY = this.cropData.y * img.naturalHeight;
    const cropW = this.cropData.width * img.naturalWidth;
    const cropH = this.cropData.height * img.naturalHeight;

    ctx.drawImage(img, cropX, cropY, cropW, cropH, 0, 0, size, size);

    const croppedImageUrl = canvas.toDataURL();
    this.app.croppedImageUrl = croppedImageUrl;
    this.app.updatePreview();
}


覆盖风

覆盖风 是一种将喜爱的效果图叠加在头像上方的风格,通过调节渐变的浓度与方向,使头像与效果图自然融合,呈现出理想的视觉效果。
FuncAvatar 中,覆盖风功能:

  • 预设主题
    • 春节主题 🧧:春节元素装饰
    • 中秋主题 🌕:中秋节庆装饰效果
    • 生日主题 🎂:生日庆祝装饰
    • 国庆主题 🎊:国庆节庆装饰
  • 自定义主题:支持上传自定义装饰图片
  • 透明度调节:可调整装饰效果的透明度
  • 渐变方向:可调整透明度的渐变方向
    • 从左向右
    • 从右向左
    • 从上向下
    • 从下向上
    • 从中心向四周
    • 从四周向中心

核心代码:

 // 渐变效果绘制
 applyOverlayWithGradient(ctx, size) {
        if (!this.overlayImage) return;
        
        // 获取渐变方向和透明度
        const gradientDirection = document.getElementById('gradientDirection')?.value || 'left-to-right';
        const opacity = this.opacity / 100; // 透明度控制效果强烈程度
        
        // 创建临时画布用于绘制渐变效果
        const tempCanvas = document.createElement('canvas');
        tempCanvas.width = size;
        tempCanvas.height = size;
        const tempCtx = tempCanvas.getContext('2d');
        
        // 先在临时画布上绘制覆盖图片
        tempCtx.drawImage(this.overlayImage, 0, 0, size, size);
        
        // 创建透明度渐变
        let gradient;
        switch (gradientDirection) {
            case 'left-to-right':
                gradient = tempCtx.createLinearGradient(0, 0, size, 0);
                gradient.addColorStop(0, `rgba(255, 255, 255, ${opacity})`);  // 左侧不透明
                gradient.addColorStop(1, `rgba(255, 255, 255, 0)`);           // 右侧透明
                break;
            case 'right-to-left':
                gradient = tempCtx.createLinearGradient(size, 0, 0, 0);
                gradient.addColorStop(0, `rgba(255, 255, 255, ${opacity})`);  // 右侧不透明
                gradient.addColorStop(1, `rgba(255, 255, 255, 0)`);           // 左侧透明
                break;
            case 'top-to-bottom':
                gradient = tempCtx.createLinearGradient(0, 0, 0, size);
                gradient.addColorStop(0, `rgba(255, 255, 255, ${opacity})`);  // 顶部不透明
                gradient.addColorStop(1, `rgba(255, 255, 255, 0)`);           // 底部透明
                break;
            case 'bottom-to-top':
                gradient = tempCtx.createLinearGradient(0, size, 0, 0);
                gradient.addColorStop(0, `rgba(255, 255, 255, ${opacity})`);  // 底部不透明
                gradient.addColorStop(1, `rgba(255, 255, 255, 0)`);           // 顶部透明
                break;
            case 'center-to-edge':
                gradient = tempCtx.createRadialGradient(size/2, size/2, 0, size/2, size/2, size/2);
                gradient.addColorStop(0, `rgba(255, 255, 255, ${opacity})`);  // 中心不透明
                gradient.addColorStop(1, `rgba(255, 255, 255, 0)`);           // 边缘透明
                break;
            case 'edge-to-center':
                gradient = tempCtx.createRadialGradient(size/2, size/2, size/2, size/2, size/2, 0);
                gradient.addColorStop(0, `rgba(255, 255, 255, ${opacity})`);  // 边缘不透明
                gradient.addColorStop(1, `rgba(255, 255, 255, 0)`);           // 中心透明
                break;
            default:
                gradient = tempCtx.createLinearGradient(0, 0, size, 0);
                gradient.addColorStop(0, `rgba(255, 255, 255, ${opacity})`);
                gradient.addColorStop(1, `rgba(255, 255, 255, 0)`);
        }
        
        // 应用渐变透明度遮罩
        tempCtx.globalCompositeOperation = 'destination-in';
        tempCtx.fillStyle = gradient;
        tempCtx.fillRect(0, 0, size, size);
        
        // 将处理后的图片绘制到主画布上
        ctx.drawImage(tempCanvas, 0, 0, size, size);
    }

像素风

像素风 是一种将图片进行 低分辨率化处理 的视觉效果,它通过减少图片细节、放大像素单元,营造出复古的数字像素感或点阵风格。
FuncAvatar 中,像素风主要分为两种模式:

  • 马赛克模式(Block Pixel) :以方块为单位对图片取色、平均化,生成经典的像素块效果。
  • 点阵模式(Dot Pixel) :用规则排列的小圆点表示图像颜色,模拟显示屏点阵或印刷网点质感。

马赛克模式(Block Pixel)

原理:
将图像划分为固定大小的方块(pixelSize),对每个方块内所有像素的颜色求平均,然后用该平均色填充整个方块,从而形成像素化视觉。

核心代码:

applyBlockPixelEffect(canvas, ctx, image) {
    const pixelSize = this.app.pixelSize || 8;
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    const data = imageData.data;
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    for (let y = 0; y < canvas.height; y += pixelSize) {
        for (let x = 0; x < canvas.width; x += pixelSize) {
            let r = 0, g = 0, b = 0, a = 0, count = 0;
            for (let dy = 0; dy < pixelSize && y + dy < canvas.height; dy++) {
                for (let dx = 0; dx < pixelSize && x + dx < canvas.width; dx++) {
                    const i = ((y + dy) * canvas.width + (x + dx)) * 4;
                    r += data[i]; g += data[i + 1]; b += data[i + 2]; a += data[i + 3];
                    count++;
                }
            }
            if (count > 0) {
                r = Math.round(r / count);
                g = Math.round(g / count);
                b = Math.round(b / count);
                a = Math.round(a / count);
                ctx.fillStyle = `rgba(${r},${g},${b},${a / 255})`;
                ctx.fillRect(x, y, pixelSize, pixelSize);
            }
        }
    }
}

点阵模式(Dot Pixel)

原理:
通过在规则间距的网格点上取图像颜色,并以小圆点绘制出来。
用户可调整点间距(dotSpacing)与点半径(dotRadius),以获得更稀疏或更密集的点阵效果。

核心代码:

applyDotPixelEffect(canvas, ctx, image) {
    const dotSpacing = this.app.dotSpacing || 6;
    const dotRadius = this.app.dotRadius || 3;
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    const tempCanvas = document.createElement('canvas');
    const tempCtx = tempCanvas.getContext('2d');
    tempCanvas.width = canvas.width;
    tempCanvas.height = canvas.height;
    tempCtx.drawImage(image, 0, 0, canvas.width, canvas.height);

    const imageData = tempCtx.getImageData(0, 0, canvas.width, canvas.height);
    const data = imageData.data;

    for (let y = dotRadius; y < canvas.height; y += dotSpacing) {
        for (let x = dotRadius; x < canvas.width; x += dotSpacing) {
            const i = (y * canvas.width + x) * 4;
            const [r, g, b, a] = [data[i], data[i+1], data[i+2], data[i+3]];
            if (a > 0) {
                ctx.fillStyle = `rgba(${r},${g},${b},${a / 255})`;
                ctx.beginPath();
                ctx.arc(x, y, dotRadius, 0, Math.PI * 2);
                ctx.fill();
            }
        }
    }
}

国旗风

国旗风 是一种通过在头像上添加所选国家国旗元素的风格。用户可根据个人喜好选择不同国旗,并调节显示风格与位置,使头像既保留个性,又能表达对国家的热爱与归属感。
FuncAvatar 中,国旗风功能:

  • 国家国旗:插件中内含 254 个国家旗帜
  • 国旗样式
    • 原样:正常平面国旗样式
    • 飘动:类似波浪形状,飘动的国旗样式
    • 嵌入:选取一个圆形裁取国旗核心位置,放置边缘,原图片缩小10%,营造出嵌入效果
  • 国旗位置
    • 左上角
    • 右上角
    • 左下角
    • 右下角

核心代码:

// 国家配置文件
// code     国家代码
// name     国家名称
// cropMode 裁取位置
[
    // 亚洲国家
    { code: 'cn', name: '中国', cropMode: 'center' },
    { code: 'jp', name: '日本', cropMode: 'center' },
    { code: 'kr', name: '韩国', cropMode: 'center' },
    { code: 'kp', name: '朝鲜', cropMode: 'center' },
    ......
]
// 原样:将flag内图片在头像图片上展示,根据选择位置变化
applyOriginalStyle(canvas, ctx, flagImage) {
        const flagSize = Math.min(canvas.width, canvas.height) * 0.3; // 国旗大小为画布的30%
        const flagHeight = flagSize * (flagImage.height / flagImage.width); // 实际国旗高度
        const margin = 15; // 统一边距
        const position = this.calculateFlagPosition(canvas, flagSize, flagHeight, margin);
        
        ctx.drawImage(flagImage, position.x, position.y, flagSize, flagHeight);
    }
// 飘动:加载flag-fly下选择国旗的同名图片按位置展示
applyFlyingStyle(canvas, ctx, flagImage) {
    const flagSize = Math.min(canvas.width, canvas.height) * 0.3; // 与原样保持一致的大小
    const flagHeight = flagSize * (flagImage.height / flagImage.width); // 实际国旗高度
    const margin = 15; // 统一边距
    const position = this.calculateFlagPosition(canvas, flagSize, flagHeight, margin);

    ctx.drawImage(flagImage, position.x, position.y, flagSize, flagHeight);
}
// 嵌入:根据位置选择在对应角落按圆形截取国旗展示,周围十像素为白色,原头像等比例缩小8%
applyCircleStyle(canvas, ctx, flagImage) {
    const r = 40;               // 国旗圆半径
    const margin = 10;          // 白边
    const totalR = r + margin;

    // 保存原头像并缩小绘制
    const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = 'white';
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    const tmp = document.createElement('canvas');
    tmp.width = canvas.width;
    tmp.height = canvas.height;
    tmp.getContext('2d').putImageData(imgData, 0, 0);

    const s = 0.9;
    const off = (1 - s) / 2;
    ctx.drawImage(tmp, 0, 0, canvas.width, canvas.height,
        canvas.width * off, canvas.height * off,
        canvas.width * s, canvas.height * s);

    // 国旗位置
    const pos = {
        'top-left':     { x: totalR, y: totalR },
        'top-right':    { x: canvas.width - totalR, y: totalR },
        'bottom-left':  { x: totalR, y: canvas.height - totalR },
        'bottom-right': { x: canvas.width - totalR, y: canvas.height - totalR }
    }[this.selectedPosition] || { x: totalR, y: totalR };

    // 绘制白底圆
    ctx.save();
    ctx.beginPath();
    ctx.arc(pos.x, pos.y, totalR, 0, 2 * Math.PI);
    ctx.fillStyle = 'white';
    ctx.fill();

    // 剪切圆形区域并绘制国旗
    ctx.beginPath();
    ctx.arc(pos.x, pos.y, r, 0, 2 * Math.PI);
    ctx.clip();

    const size = r * 2;
    const ratio = flagImage.width / flagImage.height;
    let sx, sy, sw, sh;

    if (ratio > 1) {
        sw = sh = flagImage.height;
        sx = (flagImage.width - sw) / 2;
        sy = 0;
    } else {
        sw = sh = flagImage.width;
        sx = 0;
        sy = (flagImage.height - sh) / 2;
    }

    ctx.drawImage(flagImage, sx, sy, sw, sh,
        pos.x - r, pos.y - r, size, size);
    ctx.restore();
}        

下载

轻轻一点 收工 😎

嵌入式国旗展示问题

国旗风 中,因为图片量较多,目前嵌入式风格,有些国旗展示不够 核心
如下图,虽然配置中设置了左裁取,但是因为国旗核心区域的大小等原因,导致效果不理想🥲。
未来打算,专门设置圆形国旗文件夹,存储处理好的圆形国旗,一劳永逸🫡。

未来展望

  • 添加更多预设的节日主题支持
  • 添加更多其他风格玩法
  • 拓展多端(Andorid、Ios、Web等)

🎟️发车了 🚗

Flutter 之魂 GetX🔥(三)深入掌握依赖管理

🪶序言

🧩什么是依赖?

依赖 就是一个对象运行时需要用到的另一个对象。

例子:

class Car {
  Engine v8 = Engine(); // Car 依赖 Engine
}

Car 离不开 Engine,这就是依赖关系

⚙️ 什么是依赖注入?

依赖注入 就是把对象的创建交给外部系统(或框架)完成。

简单说:
👉 让别人帮我 new,而不是我自己 new

例子:

class Car {
  final Engine v8;
  Car(this.v8); // 依赖由外部注入
}

💡优点

  • 解耦:Car 不关心 Engine 怎么创建
  • 可替换:方便测试或更换实现类

GetX - 依赖管理(Dependency Management)

1. 基本概念

GetX 依赖管理是一种框架机制,用于统一创建、提供、访问和销毁对象,实现对象的集中管理和生命周期控制

它让你不用在代码里到处写 new,而是由框架帮你管理对象的生命周期。

核心思想

  1. 注入(Injection)
    框架帮你创建对象,你只负责使用,不关心对象如何生成。
    🔹 就像你去餐厅,只需要吃菜,不需要自己下厨。
  2. 管理(Management)
    框架管理对象的生命周期:自动销毁、永久存在或手动替换。
  3. 访问(Access)
    注册过的对象可以在任何地方获取,无需传参或重复创建。

💡总结:
框架帮你 new、管你用、自动收拾。
这就是 GetX 依赖管理的精髓。

完整流程示意图如下:

graph TD
依赖注册 --> 立即创建Get.put
依赖注册 --> 懒加载Get.lazyPut
依赖注册 --> 异步创建Get.putAsync
依赖注册 --> 每次新建Get.create
立即创建Get.put --> 获取依赖Get.find
懒加载Get.lazyPut --> 获取依赖Get.find
异步创建Get.putAsync --> 获取依赖Get.find
每次新建Get.create --> 获取依赖Get.find
获取依赖Get.find --> 生命周期管理
生命周期管理 --> 随页面自动销毁
生命周期管理 --> 永久依赖permanent:true
生命周期管理 --> 替换依赖Get.replace
随页面自动销毁 --> 依赖销毁
永久依赖permanent:true --> 依赖销毁
替换依赖Get.replace --> 依赖销毁
依赖销毁 --> 手动销毁Get.delete
依赖销毁 --> 清空所有Get.deleteAll

2. 依赖注册

①. Get.put

  • 功能:立即实例化并注册依赖。
  • 使用场景:应用启动或页面打开时就需要用到的依赖。
  • 参数
参数名 类型 是否必填 默认值 说明
dependency S 要注册的依赖对象实例,例如控制器或服务类对象。
tag String null 可选标签,用于区分同一类型的多个实例,常用于多控制器并存场景。
permanent bool false 是否永久依赖:
true → 对象不会随页面销毁(常驻内存)
false → 页面销毁时自动释放。
builder InstanceBuilderCallback<T> null 可选回调,用于懒加载或自定义对象的创建逻辑(一般不与 dependency 同时使用)。
  • 示例
Get.put(HomeController(), tag: "home", permanent: false);

②. Get.lazyPut

  • 功能:懒加载,仅在第一次使用时创建实例。
  • 使用场景:控制器或服务不是每次都需要时,节省内存资源。
  • 参数
参数名 类型 是否必填 默认值 说明
builder S Function() 必选回调,用于创建依赖对象,第一次使用时才会执行。
tag String null 可选标签,用于区分同一类型的多个实例。
fenix bool false 是否开启“复活”功能:
true → 对象被释放后再次调用 Get.find 会重新创建
false → 对象被释放后再次调用会报错。
  • 示例
Get.lazyPut<HomeController>(() => HomeController(), tag: "home", fenix: true);

③. Get.putAsync

  • 功能:用于异步实例化依赖对象。
  • 使用场景:当对象创建需要异步操作(如数据库初始化、网络加载等)。
  • 参数
参数名 类型 是否必填 默认值 说明
builder Future<S> Function() 必选异步回调,用于创建对象。
tag String null 可选标签,用于区分同一类型的多个实例。
permanent bool false 是否永久依赖:
true → 对象不会随页面销毁
false → 页面销毁时自动释放。
  • 示例
Get.putAsync<HomeController>(
  () async {
    await Future.delayed(Duration(seconds: 1));
    return HomeController();
  },
  tag: "home",
  permanent: false,
);

④. Get.create

  • 功能:每次调用 Get.find 都会创建一个新的实例。
  • 使用场景:适用于需要临时对象或频繁刷新实例的场景。
  • 参数
参数名 类型 是否必填 默认值 说明
builder S Function() 必选回调,用于每次创建新的依赖对象。
tag String null 可选标签,用于区分同一类型的多个实例。
permanent bool true 是否永久依赖:
true → 默认值,对象不会随页面销毁
false → 对象随页面销毁。
  • 示例
Get.create<HomeController>(() => HomeController(), tag: "home", permanent: true);

总结

方法 注册时机 返回实例 是否可重复创建 常用场景 主要参数说明
Get.put<T> 立即注册(立即创建对象) 单例(同类型唯一) 需要立即使用的控制器或服务 dependency: 实例对象
tag: 区分实例
permanent: 是否永久存在
Get.lazyPut<T> 延迟注册(首次使用时创建) 单例(惰性加载) ✅ (可通过 fenix 重建) 仅在用到时才创建的控制器 builder: 对象构造函数
fenix: 被销毁后是否可重建
tag: 标签标识
Get.putAsync<T> 异步注册(等待 Future 完成) 单例 需要异步初始化的依赖(如数据库、SharedPreferences) builder: 异步构造函数
permanent: 是否永久存在
tag: 标签
Get.create<T> 每次调用时创建新实例 非单例(每次新的) 每次都要新建实例的对象 builder: 构造函数
tag: 标签
permanent: 是否永久存在

3. 依赖获取

GetX 中,已经注册的依赖对象可以通过 Get.find<T>() 在任何地方获取,无需手动传递或 new 对象。

①. 基本用法

// 获取默认注册的依赖对象
final homeController = Get.find<HomeController>();

// 如果注册时使用了 tag,需要传入相同标签
// tag: 可选标签,用于区分同一类型的多个实例
final loginController = Get.find<HomeController>(tag: "loginPage");

②. 特点概述

  • 全局访问:注册后的对象可以在任意地方调用 Get.find 获取
  • 类型安全:泛型保证返回对象类型正确
  • 支持标签:区分同一类型的多个实例
  • 生命周期一致:获取到的对象遵循注册时的生命周期规则

③. 补充说明

  • 获取依赖对象时,如果对象不存在,会抛出异常
  • 对于懒加载对象(Get.lazyPut)或异步对象(Get.putAsync),第一次 Get.find 会触发创建

4. 依赖销毁

GetX 中,依赖对象不仅可以自动销毁(随页面生命周期),也可以 手动销毁全部清空 ,从而实现灵活的内存与资源管理。

①. Get.delete

  • 功能:手动销毁指定类型的依赖对象。
  • 使用场景:当对象不再需要时手动释放资源,或在页面关闭后主动清理。
  • 参数
参数名 类型 是否必填 默认值 说明
tag String null 如果注册时使用了标签,销毁时也需传入相同标签。
force bool false 是否强制销毁:true → 即使对象被标记为 permanent 也会被移除false → 默认值,永久依赖不会被销毁。
  • 示例
// 销毁默认依赖对象
Get.delete<HomeController>();

// 销毁带标签的依赖对象
Get.delete<HomeController>(tag: "home");

// 强制销毁永久依赖
Get.delete<HomeController>(force: true);

②. Get.deleteAll

  • 功能:一次性清空所有已注册的依赖对象。
  • 使用场景:适用于应用重启、退出登录、清理缓存等场景。
  • 示例
// 清空所有依赖对象
Get.deleteAll();

③. 自动销毁机制

  • 当依赖注册时未设置 permanent: true,对象会随页面关闭自动销毁
  • 若设置 permanent: true,则必须手动销毁才能释放资源

💡 总结:

  • Get.delete 用于销毁单个依赖
  • Get.deleteAll 用于清空全部依赖,默认依赖会随页面自动释放。

5. 依赖替换

Get.replaceGetX 中用于替换已注册依赖对象 的方法。它的作用是把原来的依赖对象换成新的实例,而不需要手动先删除再注册,可以理解为 更新容器里的依赖

  • 功能

    • 替换已有依赖对象实例
    • 保持依赖容器中类型和标签不变
    • 自动处理生命周期(会销毁旧对象,注册新对象)
  • 使用场景

    1. 热重载或刷新控制器: 页面逻辑变更,需要新的实例覆盖旧实例。
    2. 动态切换服务实现: 比如切换网络请求客户端、用户数据服务等。
    3. 替换依赖而不影响其他地方调用: Get.find<HomeController>() 依然能获取到新实例。
  • 示例

// 原来的控制器
Get.put<HomeController>(HomeController());

// 替换成新实例
Get.replace<HomeController>(HomeController());

// 如果原来注册时用了 `tag` 或 `permanent`,可以一起替换:
Get.replace<HomeController>(
  HomeController(),
  tag: "home",
  permanent: true,
);

6. Bindings

Bindings 是 GetX 中的 依赖注入桥梁,用于在页面加载前自动注册所需的控制器和服务。
它把依赖的创建逻辑从页面中分离出来,实现页面与依赖的解耦自动管理

Bindings 就是「页面加载前,自动执行依赖注册」的机制。

①. 基本使用

(1) 定义 Binding 类

通过继承 Bindings 并重写 dependencies() 方法注册依赖。

class HomeBinding extends Bindings {
  @override
  void dependencies() {
    // 懒加载控制器
    Get.lazyPut<HomeController>(() => HomeController());

    // 永久依赖的全局服务
    Get.put<AuthService>(AuthService(), permanent: true);
  }
}
(2) 路由中绑定 Binding
GetPage(
  name: '/home',
  page: () => HomePage(),
  binding: HomeBinding(),
);

当用户进入 /home 页面时,HomeBinding.dependencies() 会自动执行,
HomeControllerAuthService 会被注册到依赖管理容器中。

(3) 多 Binding 绑定
GetPage(
  name: '/dashboard',
  page: () => DashboardPage(),
  bindings: [
    HomeBinding(),
    UserBinding(),
    SettingBinding(),
  ],
);
(4) 动态绑定(BindingsBuilder)

适合小页面或一次性绑定:

GetPage(
  name: '/login',
  page: () => LoginPage(),
  binding: BindingsBuilder(() {
    Get.put(LoginController());
  }),
);

②. 机制原理

展示:

flowchart TD
页面跳转请求-->是否配置Binding?
是否配置Binding?--是-->执行Binding.dependencies
是否配置Binding?--否-->直接进入页面
执行Binding.dependencies-->注册依赖到GetInstance容器
注册依赖到GetInstance容器-->依赖是否永久
依赖是否永久--是-->标记为permanent页面销毁时保留
依赖是否永久--否-->标记为临时依赖页面销毁时释放
标记为permanent页面销毁时保留-->页面初始化
标记为临时依赖页面销毁时释放-->页面初始化
页面初始化-->控制器与服务注入页面
控制器与服务注入页面-->页面正常运行
页面正常运行-->页面销毁
页面销毁-->依赖是否永久?
依赖是否永久?--否-->从容器移除依赖释放资源
依赖是否永久?--是-->保留依赖供复用
从容器移除依赖释放资源-->页面销毁完成
保留依赖供复用-->页面销毁完成

核心机制说明:

  1. 路由检测阶段
    当执行 Get.to()Get.off() 跳转页面时,GetX 会检查目标 GetPage 是否配置了 binding
  2. 依赖注册阶段
    若存在绑定,框架会自动调用该 Bindingdependencies() 方法,将其中定义的依赖通过
    Get.put()Get.lazyPut() 等方法注册进 全局依赖容器 GetInstance()
  3. 页面初始化阶段
    页面构建时,注册的依赖对象被自动注入(如控制器、服务等),页面可直接通过 Get.find() 获取实例。
  4. 页面销毁阶段
    当页面被关闭时,所有 permanent = false 的依赖会自动释放;
    仅有 permanent = true 的依赖会常驻内存,直到手动删除或程序结束。

7. 管理机制

GetX 的依赖管理机制通过 SmartManagement 控制依赖对象在页面生命周期中的自动释放和保留策略。
可以理解为依赖管理的 智能策略,决定依赖在页面关闭时是否释放或复用。

①. SmartManagement.full

  • 功能:页面销毁时,自动释放所有注册的依赖,包括非 permanent 的依赖。
  • 使用场景:适合页面之间独立、依赖对象不需要跨页面复用的场景。
  • 示例
GetMaterialApp(
  initialRoute: '/home',
  smartManagement: SmartManagement.full,
  getPages: [...],
);
  • 特点
    • 页面关闭时,非永久依赖全部释放
    • 控制器不会残留,节省内存
    • 默认策略

②. SmartManagement.onlyBuilders

  • 功能:页面销毁时,只释放 通过 GetBuilder 创建的依赖Get.put / Get.lazyPut 的依赖保持不变。
  • 使用场景:希望保留全局或懒加载依赖,仅释放页面局部依赖时使用。
  • 示例
GetMaterialApp(
  smartManagement: SmartManagement.onlyBuilders,
);
  • 特点
    • 页面关闭时只清理局部依赖
    • 全局依赖、懒加载依赖不会被销毁
    • 适合控制器跨页面复用的场景

③. SmartManagement.keepFactory

  • 功能:页面销毁时,依赖保持不释放,页面再次打开时复用原实例。
  • 使用场景:依赖对象需要在多个页面间共享,或者页面频繁切换时希望避免重复创建。
  • 示例
GetMaterialApp(
  smartManagement: SmartManagement.keepFactory,
);
  • 特点
    • 页面关闭不会销毁依赖
    • 所有依赖都可以跨页面复用
    • 适合全局服务或单例控制器

总结对比

策略 页面销毁行为 适用场景
full 非永久依赖全部释放 页面独立,节省内存
onlyBuilders 只释放 GetBuilder 创建的依赖 保留全局或懒加载依赖
keepFactory 所有依赖保留,不释放 跨页面复用,单例控制器

8. 依赖作用域

GetX 中,依赖对象的作用域决定了对象 生命周期、可访问性和复用方式
主要分为 页面局部依赖全局依赖

①. 页面局部依赖(Page Scoped Dependency)

  • 定义:依赖对象只在当前页面存在,页面关闭后自动销毁。

  • 注册方式

    • Get.put(controller)(默认 permanent: false
    • Get.lazyPut(controller)(默认非永久)
  • 特点

    • 生命周期绑定页面
    • 节省内存,页面关闭后自动释放
    • 不同页面可以注册同类型控制器而互不影响

②. 全局依赖(Global Dependency)

  • 定义:依赖对象在应用整个生命周期中存在,跨页面可复用。

  • 注册方式

    • Get.put(controller, permanent: true)
    • Get.lazyPut(controller, fenix: true)(复活机制,释放后可再次创建)
  • 特点

    • 生命周期跨页面
    • 多页面共享同一个实例
    • 适合全局服务、单例控制器

总结

类型 生命周期 跨页面访问 常用场景
页面局部依赖 页面关闭销毁 页面专属控制器或服务
全局依赖 程序生命周期/永久 用户状态管理、全局服务、单例控制器
❌