Flutter 进阶

本节将介绍一些 Flutter 进阶概念,帮助你提升开发技能。


渲染原理

Flutter 使用 Skia(自 3.0 起逐步切换到 Impeller)渲染引擎直接绘制 UI,不使用原生组件。

Widget 树与 Element 树

  • Widget 树: 描述 UI 的配置信息(不可变)
  • Element 树: Widget 的实例化对象(可变)
  • RenderObject 树: 负责实际渲染

理解渲染原理有助于优化 UI 性能,避免不必要的重建。


InheritedWidget - 数据传递

InheritedWidget 允许数据沿着 Widget 树向下传递,子 Widget 可以获取祖先的数据。

实例:自定义 InheritedWidget

// 定义数据容器
class MyData extends InheritedWidget {
  final int value;

  const MyData({
    super.key,
    required this.value,
    required super.child,
  });

  // 便捷方法:获取最近的 MyData
  static MyData of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MyData>()!;
  }

  // 判断是否需要重建子 Widget
  @override
  bool updateShouldNotify(MyData oldWidget) {
    return value != oldWidget.value;
  }
}

// 使用
class ParentWidget extends StatelessWidget {
  const ParentWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return MyData(
      value: 42,
      child: const ChildWidget(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    // 获取祖先的 MyData
    final myData = MyData.of(context);
    return Text('Value: ${myData.value}');
  }
}

RepaintBoundary - 局部重绘

使用 RepaintBoundary 可以限制重绘区域,提高性能。

实例:RepaintBoundary 使用

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

  @override
  Widget build(BuildContext context) {
    return RepaintBoundary(
      child: Container(
        // 这个区域的绘制不会影响外部
        color: Colors.red,
        child: const Text('独立区域'),
      ),
    );
  }
}

Keys 的深入理解

Keys 帮助 Flutter 识别 Widget,区分新 Widget 和已有 Widget。

ValueKey vs ObjectKey vs UniqueKey

类型说明
ValueKey使用值作为键(如 ID、数字、字符串)
ObjectKey使用对象引用作为键
UniqueKey每次创建生成唯一键,破坏重建

CustomPainter - 自定义绘制

使用 CustomPainter 可以实现完全自定义的图形绘制。

实例:CustomPainter 使用

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

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: MyCirclePainter(),
      size: const Size(200, 200),
    );
  }
}

class MyCirclePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    // 绘制圆形
    final paint = Paint()
      ..color = Colors.blue
      ..style = PaintingStyle.fill;

    final center = Offset(size.width / 2, size.height / 2);
    final radius = size.width / 2 - 10;

    canvas.drawCircle(center, radius, paint);

    // 绘制边框
    final borderPaint = Paint()
      ..color = Colors.black
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2;

    canvas.drawCircle(center, radius, borderPaint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
}

Stream 和 Future

实例:Stream 用法

// 创建 Stream
Stream<int> countStream(int max) async* {
  for (int i = 1; i <= max; i++) {
    await Future.delayed(const Duration(seconds: 1));
    yield i;  // 发送值
  }
}

// 使用 StreamBuilder
class StreamExample extends StatelessWidget {
  const StreamExample({super.key});

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<int>(
      stream: countStream(10),
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return Text('计数: ${snapshot.data}');
        } else if (snapshot.hasError) {
          return Text('错误: ${snapshot.error}');
        }
        return const CircularProgressIndicator();
      },
    );
  }
}

总结

本 Flutter 入门教程涵盖了以下内容:

  • Flutter 安装和环境配置
  • Widget 基础(StatelessWidget 和 StatefulWidget)
  • 布局系统(Row、Column、Stack)
  • 用户输入处理
  • 状态管理(setState、Provider)
  • 网络请求和数据存储
  • 导航和路由
  • 测试和发布

继续深入学习可以关注:

  • BLoC 模式
  • Riverpod 状态管理
  • GetX 路由和状态管理
  • Flutter 性能优化
  • Flutter Web 和桌面开发