基础入门 Flutter for OpenHarmony:Transform 变换组件详解

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
🎯 欢迎来到 Flutter for OpenHarmony 社区!本文将深入讲解 Flutter 中 Transform 变换组件的使用方法,带你从基础到精通,掌握这一重要的视觉效果组件。


一、Transform 组件概述

Transform 是 Flutter 中用于对子组件进行各种变换的组件。通过 Transform,我们可以实现旋转、缩放、平移、倾斜等视觉效果,为应用增添丰富的动态感和交互性。Transform 使用矩阵变换来修改子组件的外观,但不会影响其布局。

📋 Transform 组件特点

特点 说明
不影响布局 变换只影响视觉效果,不影响组件的布局位置
多种变换类型 支持旋转、缩放、平移、倾斜等变换
高性能 使用 GPU 加速,变换操作非常高效
可组合 多个变换可以嵌套组合使用
支持动画 可以与动画组件配合实现动态效果

变换在 UI 设计中的作用

变换在用户界面设计中有着广泛的应用:

  1. 视觉动感:通过旋转和缩放增加界面的动感
  2. 交互反馈:按钮点击时的缩放效果
  3. 图标复用:通过翻转实现图标的镜像效果
  4. 动画效果:加载指示器的旋转动画
  5. 3D 效果:卡片翻转等 3D 视觉效果
  6. 创意布局:倾斜卡片等创意设计

💡 使用场景:Transform 广泛应用于旋转动画、按钮点击效果、图标翻转、加载指示器、卡片翻转、3D 效果等场景。


二、Transform.rotate 旋转变换

旋转变换是最常用的变换之一,可以让组件围绕中心点旋转。

2.1 最简单的旋转

最基础的旋转只需要设置 angle 参数:

dart 复制代码
Transform.rotate(
  angle: 0.5,  // 弧度值
  child: Container(
    width: 100,
    height: 100,
    color: Colors.blue,
  ),
)

代码解析:

  • angle: 0.5:设置旋转角度为 0.5 弧度(约 28.6 度)
  • child:要应用旋转效果的子组件
  • 默认围绕组件中心点旋转

2.2 角度与弧度

Flutter 使用弧度作为角度单位,需要导入 dart:math 库:

dart 复制代码
import 'dart:math' as math;

Transform.rotate(
  angle: math.pi / 4,  // 45度
  child: Container(...),
)

常用角度对照表:

角度 弧度值 代码表示
15° π/12 math.pi / 12
30° π/6 math.pi / 6
45° π/4 math.pi / 4
90° π/2 math.pi / 2
180° π math.pi
360° 2 * math.pi

2.3 设置旋转中心点

通过 originalignment 参数可以设置旋转中心:

dart 复制代码
Transform.rotate(
  angle: math.pi / 4,
  origin: const Offset(0, 0),  // 以左上角为旋转中心
  child: Container(...),
)

Transform.rotate(
  angle: math.pi / 4,
  alignment: Alignment.topLeft,  // 左上角对齐
  child: Container(...),
)

2.4 完整示例

dart 复制代码
class RotateExample extends StatelessWidget {
  const RotateExample({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('旋转示例')),
      body: Center(
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            _buildRotatedBox(0, '0°'),
            _buildRotatedBox(math.pi / 6, '30°'),
            _buildRotatedBox(math.pi / 4, '45°'),
            _buildRotatedBox(math.pi / 2, '90°'),
          ],
        ),
      ),
    );
  }

  Widget _buildRotatedBox(double angle, String label) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        Transform.rotate(
          angle: angle,
          child: Container(
            width: 60,
            height: 60,
            color: Colors.blue,
          ),
        ),
        const SizedBox(height: 8),
        Text(label),
      ],
    );
  }
}

三、Transform.scale 缩放变换

缩放变换可以放大或缩小组件的显示大小。

3.1 基础缩放

dart 复制代码
Transform.scale(
  scale: 1.5,  // 放大 1.5 倍
  child: Container(
    width: 100,
    height: 100,
    color: Colors.green,
  ),
)

scale 参数说明:

效果
> 1 放大
= 1 原始大小
< 1 缩小

3.2 分别设置水平和垂直缩放

dart 复制代码
Transform.scale(
  scaleX: 1.5,  // 水平方向放大 1.5 倍
  scaleY: 0.8,  // 垂直方向缩小到 0.8 倍
  child: Container(
    width: 100,
    height: 100,
    color: Colors.orange,
  ),
)

3.3 缩放中心点

dart 复制代码
Transform.scale(
  scale: 1.5,
  alignment: Alignment.bottomRight,  // 以右下角为中心缩放
  child: Container(...),
)

四、Transform.translate 平移变换

平移变换可以将组件在水平或垂直方向上移动。

4.1 基础平移

dart 复制代码
Transform.translate(
  offset: const Offset(50, 30),  // 向右移动 50,向下移动 30
  child: Container(
    width: 100,
    height: 100,
    color: Colors.purple,
  ),
)

Offset 参数说明:

  • 第一个参数:水平偏移量(正值向右,负值向左)
  • 第二个参数:垂直偏移量(正值向下,负值向上)

4.2 平移不影响布局

需要注意的是,Transform.translate 只影响视觉位置,不影响布局:

dart 复制代码
Row(
  children: [
    Container(width: 50, height: 50, color: Colors.red),
    Transform.translate(
      offset: const Offset(25, 0),  // 视觉上向右移动
      child: Container(width: 50, height: 50, color: Colors.blue),
    ),
    Container(width: 50, height: 50, color: Colors.green),
  ],
)

蓝色方块视觉上移动了,但仍占据原来的布局空间。


五、Transform.flip 翻转变换

翻转变换可以水平或垂直翻转组件。

5.1 水平翻转

dart 复制代码
Transform.flip(
  flipX: true,  // 水平翻转
  child: const Icon(Icons.arrow_forward, size: 48),
)

5.2 垂直翻转

dart 复制代码
Transform.flip(
  flipY: true,  // 垂直翻转
  child: const Icon(Icons.arrow_upward, size: 48),
)

5.3 同时翻转

dart 复制代码
Transform.flip(
  flipX: true,
  flipY: true,
  child: const Icon(Icons.arrow_forward, size: 48),
)

适用场景

  • 创建镜像效果
  • 复用图标(如将右箭头翻转为左箭头)
  • 创建对称图案

六、组合变换

多个变换可以通过嵌套的方式组合使用。

6.1 旋转 + 缩放

dart 复制代码
Transform.rotate(
  angle: math.pi / 4,
  child: Transform.scale(
    scale: 1.2,
    child: Container(
      width: 100,
      height: 100,
      color: Colors.blue,
    ),
  ),
)

6.2 平移 + 旋转

dart 复制代码
Transform.translate(
  offset: const Offset(20, 20),
  child: Transform.rotate(
    angle: math.pi / 6,
    child: Container(...),
  ),
)

6.3 变换顺序的重要性

变换的顺序会影响最终效果:

dart 复制代码
// 先旋转再平移
Transform.translate(
  offset: const Offset(50, 0),
  child: Transform.rotate(angle: math.pi / 4, child: ...),
)

// 先平移再旋转(效果不同)
Transform.rotate(
  angle: math.pi / 4,
  child: Transform.translate(offset: const Offset(50, 0), child: ...),
)

七、动画效果

Transform 经常与动画组件配合使用,实现动态效果。

7.1 AnimatedRotation

dart 复制代码
AnimatedRotation(
  turns: _turns,  // 旋转圈数
  duration: const Duration(milliseconds: 500),
  child: Container(...),
)

7.2 AnimatedScale

dart 复制代码
AnimatedScale(
  scale: _scale,
  duration: const Duration(milliseconds: 300),
  child: Container(...),
)

7.3 AnimatedSlide

dart 复制代码
AnimatedSlide(
  offset: _offset,  // 偏移量(相对于自身大小)
  duration: const Duration(milliseconds: 300),
  child: Container(...),
)

八、完整代码示例

下面是一个完整的、可以直接运行的 main.dart 文件,展示了 Transform 组件的各种用法:

dart 复制代码
import 'dart:math' as math;
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Transform 组件示例',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        useMaterial3: true,
      ),
      home: const TransformDemoPage(),
    );
  }
}

class TransformDemoPage extends StatefulWidget {
  const TransformDemoPage({super.key});

  @override
  State<TransformDemoPage> createState() => _TransformDemoPageState();
}

class _TransformDemoPageState extends State<TransformDemoPage>
    with SingleTickerProviderStateMixin {
  late AnimationController _rotationController;
  double _scaleValue = 1.0;
  double _rotationValue = 0.0;

  @override
  void initState() {
    super.initState();
    _rotationController = AnimationController(
      duration: const Duration(seconds: 2),
      vsync: this,
    )..repeat();
  }

  @override
  void dispose() {
    _rotationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Transform 变换组件详解'),
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            _buildSection('一、旋转变换', [
              const Text('不同角度的旋转效果:'),
              const SizedBox(height: 12),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  _buildRotatedBox(0, '0°'),
                  _buildRotatedBox(math.pi / 6, '30°'),
                  _buildRotatedBox(math.pi / 4, '45°'),
                  _buildRotatedBox(math.pi / 2, '90°'),
                ],
              ),
            ]),
            const SizedBox(height: 24),
            _buildSection('二、缩放变换', [
              const Text('不同比例的缩放效果:'),
              const SizedBox(height: 12),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  _buildScaledBox(0.5, '0.5x'),
                  _buildScaledBox(0.75, '0.75x'),
                  _buildScaledBox(1.0, '1.0x'),
                  _buildScaledBox(1.25, '1.25x'),
                ],
              ),
            ]),
            const SizedBox(height: 24),
            _buildSection('三、平移变换', [
              const Text('不同方向的平移效果:'),
              const SizedBox(height: 12),
              Container(
                height: 80,
                decoration: BoxDecoration(
                  border: Border.all(color: Colors.grey[300]!),
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: [
                    _buildTranslatedBox(const Offset(0, 0), '原始'),
                    _buildTranslatedBox(const Offset(20, 0), '右移'),
                    _buildTranslatedBox(const Offset(0, 15), '下移'),
                    _buildTranslatedBox(const Offset(15, 15), '右下'),
                  ],
                ),
              ),
            ]),
            const SizedBox(height: 24),
            _buildSection('四、翻转变换', [
              const Text('水平和垂直翻转效果:'),
              const SizedBox(height: 12),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  _buildFlippedBox(false, false, '原始'),
                  _buildFlippedBox(true, false, '水平翻转'),
                  _buildFlippedBox(false, true, '垂直翻转'),
                  _buildFlippedBox(true, true, '双向翻转'),
                ],
              ),
            ]),
            const SizedBox(height: 24),
            _buildSection('五、组合变换', [
              const Text('多个变换组合使用:'),
              const SizedBox(height: 12),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  Column(
                    children: [
                      Transform.rotate(
                        angle: math.pi / 6,
                        child: Transform.scale(
                          scale: 1.2,
                          child: Container(
                            width: 60,
                            height: 60,
                            color: Colors.blue,
                          ),
                        ),
                      ),
                      const SizedBox(height: 8),
                      const Text('旋转+缩放', style: TextStyle(fontSize: 12)),
                    ],
                  ),
                  Column(
                    children: [
                      Transform.translate(
                        offset: const Offset(10, 10),
                        child: Transform.rotate(
                          angle: math.pi / 4,
                          child: Container(
                            width: 60,
                            height: 60,
                            color: Colors.green,
                          ),
                        ),
                      ),
                      const SizedBox(height: 8),
                      const Text('平移+旋转', style: TextStyle(fontSize: 12)),
                    ],
                  ),
                  Column(
                    children: [
                      Transform.scale(
                        scale: 1.1,
                        child: Transform.flip(
                          flipX: true,
                          child: Container(
                            width: 60,
                            height: 60,
                            color: Colors.orange,
                          ),
                        ),
                      ),
                      const SizedBox(height: 8),
                      const Text('缩放+翻转', style: TextStyle(fontSize: 12)),
                    ],
                  ),
                ],
              ),
            ]),
            const SizedBox(height: 24),
            _buildSection('六、旋转动画', [
              const Text('持续旋转的加载指示器:'),
              const SizedBox(height: 12),
              Center(
                child: AnimatedBuilder(
                  animation: _rotationController,
                  builder: (context, child) {
                    return Transform.rotate(
                      angle: _rotationController.value * 2 * math.pi,
                      child: Container(
                        width: 48,
                        height: 48,
                        decoration: BoxDecoration(
                          border: Border.all(color: Colors.blue, width: 3),
                          borderRadius: BorderRadius.circular(24),
                        ),
                        child: const Center(
                          child: Icon(Icons.refresh, color: Colors.blue),
                        ),
                      ),
                    );
                  },
                ),
              ),
            ]),
            const SizedBox(height: 24),
            _buildSection('七、交互式缩放', [
              const Text('拖动滑块控制缩放:'),
              const SizedBox(height: 12),
              Container(
                padding: const EdgeInsets.all(16),
                decoration: BoxDecoration(
                  color: Colors.grey[100],
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Column(
                  children: [
                    Transform.scale(
                      scale: _scaleValue,
                      child: Container(
                        width: 100,
                        height: 100,
                        decoration: BoxDecoration(
                          color: Colors.purple,
                          borderRadius: BorderRadius.circular(12),
                        ),
                        child: const Center(
                          child: Text(
                            '缩放',
                            style: TextStyle(color: Colors.white),
                          ),
                        ),
                      ),
                    ),
                    const SizedBox(height: 16),
                    Row(
                      children: [
                        const Text('缩放: '),
                        Text(_scaleValue.toStringAsFixed(2)),
                      ],
                    ),
                    Slider(
                      value: _scaleValue,
                      min: 0.5,
                      max: 2.0,
                      onChanged: (value) {
                        setState(() {
                          _scaleValue = value;
                        });
                      },
                    ),
                  ],
                ),
              ),
            ]),
            const SizedBox(height: 24),
            _buildSection('八、交互式旋转', [
              const Text('拖动滑块控制旋转角度:'),
              const SizedBox(height: 12),
              Container(
                padding: const EdgeInsets.all(16),
                decoration: BoxDecoration(
                  color: Colors.grey[100],
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Column(
                  children: [
                    Transform.rotate(
                      angle: _rotationValue,
                      child: Container(
                        width: 100,
                        height: 100,
                        decoration: BoxDecoration(
                          color: Colors.teal,
                          borderRadius: BorderRadius.circular(12),
                        ),
                        child: const Center(
                          child: Text(
                            '旋转',
                            style: TextStyle(color: Colors.white),
                          ),
                        ),
                      ),
                    ),
                    const SizedBox(height: 16),
                    Row(
                      children: [
                        const Text('角度: '),
                        Text('${(_rotationValue * 180 / math.pi).toStringAsFixed(0)}°'),
                      ],
                    ),
                    Slider(
                      value: _rotationValue,
                      min: 0,
                      max: 2 * math.pi,
                      onChanged: (value) {
                        setState(() {
                          _rotationValue = value;
                        });
                      },
                    ),
                  ],
                ),
              ),
            ]),
            const SizedBox(height: 24),
            _buildSection('九、图标翻转复用', [
              const Text('通过翻转复用图标:'),
              const SizedBox(height: 12),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  Column(
                    children: [
                      const Icon(Icons.arrow_forward, size: 32, color: Colors.blue),
                      const SizedBox(height: 4),
                      const Text('右箭头', style: TextStyle(fontSize: 12)),
                    ],
                  ),
                  Column(
                    children: [
                      Transform.flip(
                        flipX: true,
                        child: const Icon(Icons.arrow_forward, size: 32, color: Colors.green),
                      ),
                      const SizedBox(height: 4),
                      const Text('左箭头', style: TextStyle(fontSize: 12)),
                    ],
                  ),
                  Column(
                    children: [
                      const Icon(Icons.arrow_upward, size: 32, color: Colors.orange),
                      const SizedBox(height: 4),
                      const Text('上箭头', style: TextStyle(fontSize: 12)),
                    ],
                  ),
                  Column(
                    children: [
                      Transform.flip(
                        flipY: true,
                        child: const Icon(Icons.arrow_upward, size: 32, color: Colors.purple),
                      ),
                      const SizedBox(height: 4),
                      const Text('下箭头', style: TextStyle(fontSize: 12)),
                    ],
                  ),
                ],
              ),
            ]),
            const SizedBox(height: 32),
          ],
        ),
      ),
    );
  }

  Widget _buildSection(String title, List<Widget> children) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          title,
          style: const TextStyle(
            fontSize: 18,
            fontWeight: FontWeight.bold,
          ),
        ),
        const SizedBox(height: 8),
        ...children,
      ],
    );
  }

  Widget _buildRotatedBox(double angle, String label) {
    return Column(
      children: [
        Transform.rotate(
          angle: angle,
          child: Container(
            width: 50,
            height: 50,
            decoration: BoxDecoration(
              color: Colors.blue,
              borderRadius: BorderRadius.circular(4),
            ),
          ),
        ),
        const SizedBox(height: 8),
        Text(label, style: const TextStyle(fontSize: 12)),
      ],
    );
  }

  Widget _buildScaledBox(double scale, String label) {
    return Column(
      children: [
        Transform.scale(
          scale: scale,
          child: Container(
            width: 50,
            height: 50,
            decoration: BoxDecoration(
              color: Colors.green,
              borderRadius: BorderRadius.circular(4),
            ),
          ),
        ),
        const SizedBox(height: 8),
        Text(label, style: const TextStyle(fontSize: 12)),
      ],
    );
  }

  Widget _buildTranslatedBox(Offset offset, String label) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        Transform.translate(
          offset: offset,
          child: Container(
            width: 40,
            height: 40,
            decoration: BoxDecoration(
              color: Colors.orange,
              borderRadius: BorderRadius.circular(4),
            ),
          ),
        ),
        const SizedBox(height: 4),
        Text(label, style: const TextStyle(fontSize: 10)),
      ],
    );
  }

  Widget _buildFlippedBox(bool flipX, bool flipY, String label) {
    return Column(
      children: [
        Transform.flip(
          flipX: flipX,
          flipY: flipY,
          child: Container(
            width: 50,
            height: 50,
            decoration: BoxDecoration(
              color: Colors.purple,
              borderRadius: BorderRadius.circular(4),
            ),
            child: const Center(
              child: Text('F', style: TextStyle(color: Colors.white, fontSize: 20)),
            ),
          ),
        ),
        const SizedBox(height: 8),
        Text(label, style: const TextStyle(fontSize: 12)),
      ],
    );
  }
}

九、总结

Transform 组件是 Flutter 中实现视觉效果变换的核心组件。通过本文的学习,我们掌握了:

  1. Transform 组件的基本概念:了解了变换的原理和特点
  2. 旋转变换:学会了使用 Transform.rotate 实现旋转效果
  3. 缩放变换:学会了使用 Transform.scale 实现缩放效果
  4. 平移变换:学会了使用 Transform.translate 实现平移效果
  5. 翻转变换:学会了使用 Transform.flip 实现翻转效果
  6. 组合变换:学会了如何组合多个变换效果
  7. 动画效果:学会了与动画组件配合实现动态效果

💡 学习建议:Transform 是一个非常强大的组件,可以实现各种复杂的视觉效果。注意变换不影响布局的特点,合理使用可以实现意想不到的效果。


📚 延伸阅读

相关推荐
空白诗3 小时前
基础入门 Flutter for OpenHarmony:DecoratedBox 装饰盒子组件详解
flutter
键盘鼓手苏苏3 小时前
Flutter for OpenHarmony:random_string 简单灵活的随机字符串生成器(验证码、密钥、UUID) 深度解析与鸿蒙适配指南
开发语言·flutter·华为·rust·harmonyos
无巧不成书021811 小时前
【开源鸿蒙+Flutter实战】Step Two复盘(DAY8-14)|复杂页面落地·多终端适配·状态保持实战指南
flutter·开源·harmonyos
sdff1139612 小时前
【HarmonyOS】鸿蒙Flutter跨设备流转技术实战指南
flutter·wpf·harmonyos
无巧不成书021812 小时前
开源鸿蒙+Flutter实战复盘Step Three(DAY15-19)全场景动效·性能降级·工程闭环 终篇指南
flutter·开源·harmonyos
No丶slovenly13 小时前
flutter笔记-输入框
前端·笔记·flutter
阿林来了13 小时前
Flutter三方库适配OpenHarmony【flutter_speech】— 开发环境搭建
flutter·harmonyos·鸿蒙
sdff1139614 小时前
【Flutter】NewsHub跨平台开发:Flutter适配鸿蒙实战教程
flutter·华为·harmonyos
无巧不成书021814 小时前
【开源鸿蒙+Flutter实战】Step One复盘(DAY1-7)|环境闭环+网络请求+列表交互 全避坑(真机验证版)
flutter·开源·harmonyos