Flutter进阶系列:深层树的优雅实现/最佳实践 StatefulBuilder & Builder

113 阅读2分钟

一、问题

flutter 因为渲染机制,层级越深渲染代价越大。怎么解决这个问题呢?大体分为以下种:

1、将局部组件缓存;

2、将方法组件抽出成继承 StatefulWidget 和 StatelessWidget 的子组件,利用系统优化机制;

3、使用 RepaintBoundray 包裹频繁变化的组件,变化限制在低层级;

4、使用 Provider 等第三方库;

...

今天讨论第 2 条:虽然我一直知道这样可以优化,但是在不那么计较性能的页面我都是采用的函数组件,原因有三条:

1)、文件数量额外多出一些;

2)、拆分麻烦(其实是懒);

3)、不够优雅;

那么有没有一种实现可以完美满足以上三种最佳实现呢???

当然有!!!


二、解决方案

今天无意间研究 flutter 源码时发现了寻找了很久的最佳实践:

Builder & StatefulBuilder

1、Builder 源码:

typedef WidgetBuilder = Widget Function(BuildContext context);
class Builder extends StatelessWidget {
  const Builder({
    Key? key,
    required this.builder,
  }) : assert(builder != null),
       super(key: key);

  final WidgetBuilder builder;

  @override
  Widget build(BuildContext context) => builder(context);
}

2、StatefulBuilder 源码:

typedef StatefulWidgetBuilder = Widget Function(BuildContext context, StateSetter setState);
class StatefulBuilder extends StatefulWidget {
  const StatefulBuilder({
    Key? key,
    required this.builder,
  }) : assert(builder != null),
       super(key: key);

  final StatefulWidgetBuilder builder;

  @override
  State<StatefulBuilder> createState() => _StatefulBuilderState();
}

class _StatefulBuilderState extends State<StatefulBuilder> {
  @override
  Widget build(BuildContext context) => widget.builder(context, setState);
}

3、使用

用 Builder 包裹函数组件相等于创造了一个继承 StatelessWidget 的类组件;

用 StatefulBuilder 包裹函数组件相等于创造了一个继承 StatefulWidget 的类组件;

1)、Builder 示例:

Widget build(BuildContext context) {

  return Scaffold(
    body: Builder(
      builder: (BuildContext context) {
        return Center(
          child: TextButton(
            onPressed: () {
              print(Scaffold.of(context).hasAppBar);
            },
            child: Text('hasAppBar'),
          ),
        );
      },
    ),
  );
}

2)、StatefulBuilder 示例:

await showDialog<void>(
  context: context,
  builder: (BuildContext context) {
    int? selectedRadio = 0;
    return AlertDialog(
      content: StatefulBuilder(
        builder: (BuildContext context, StateSetter setState) {
          return Column(
            mainAxisSize: MainAxisSize.min,
            children: List<Widget>.generate(4, (int index) {
              return Radio<int>(
                value: index,
                groupValue: selectedRadio,
                onChanged: (int? value) {
                  setState(() => selectedRadio = value);
                },
              );
            }),
          );
        },
      ),
    );
  },
);

三、总结

实现很简单(为什么我之前没想到,没自己实现呢?),掌握这两个组件的使用很重要;但是实现思路和封装思维更重要;开飞机很爽,但是造飞机是更酷!