前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter局部刷新三剑客

Flutter局部刷新三剑客

作者头像
用户1907613
发布2024-06-27 20:22:09
2430
发布2024-06-27 20:22:09
举报
文章被收录于专栏:Android群英传

局部刷新作为提高Flutter页面性能的重要手段,是每一个Flutter老手都必须掌握的技巧。当然,我们不用非得使用Riverpod、Provider、Bloc这些状态管理工具来实现局部刷新,Flutter框架本身也给我们提供了很多方便快捷的刷新方案,今天要提的就是Notifier三剑客,用它来处理局部刷新,代码优雅又方便,可谓是居家必备之良器。

ChangeNotifier

ChangeNotifier作为数据提供方,给出了响应式编程的基础,我们先来看看ChangeNotifier的源码。

作为一个mixin,它就是实现了Listenable,这又是个什么呢?

这个抽象类,实际上就是实现了addListener和removeListener两个监听的处理。所以接下来我们看看ChangeNotifier是如何实现者两个方法的。

源码很简单,就是创建的listener添加到_listeners列表中。

移除也很简单。最后看下核心的notifyListeners方法。

这个方法就是遍历_listeners,来触发监听Callback。整体就是一个标准的「订阅-发布」流程。

作为Notifier家族的长辈,它的使用会略复杂一些,我们来看一个例子。首先,需要mixin一个ChangeNotifier。

代码语言:javascript
复制

dart
class CountNotifier with ChangeNotifier {
  int count = 0;

  void increase() {
    ++count;
    notifyListeners();
  }
}

然后再创建一个TestWidget来调用这个ChangeNotifier。

代码语言:javascript
复制

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

  @override
  State<StatefulWidget> createState() {
    return _CountNotifierState();
  }
}

class _CountNotifierState extends State<CountNotifierWidget> {
  final CountNotifier _countNotify = CountNotifier();
  int _count = 0;

  @override
  void initState() {
    super.initState();
    _countNotify.addListener(updateCount);
  }

  void updateCount() {
    setState(() {
      _count = _countNotify.count;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text("Test: $_count"),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _countNotify.increase(),
        child: const Icon(Icons.add),
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();
    _countNotify.removeListener(updateCount);
  }
}

这样当我们修改ChangeNotifier的value的时候,就会Callback到updateCount实现刷新。

这样就形成了一个响应式的基础模型,数据修改,监听者刷新UI,完成了响应式的同时,也实现了局部刷新的功能,提高了性能。

ValueNotifier

在使用ChangeNotifier的时候,每次在修改变量时,都需要手动调用notifyListeners()方法,所以,Flutter创建了一个新的组件——ValueNotifier,它的源码如下。

从源码可以看见,ValueNotifier就是在set方法中,帮你调用了下notifyListeners()方法。同时,ValueNotifier封装了一个泛型变量,简化了ChangeNotifier的创建过程,所以大部分时间我们都是直接使用ValueNotifier。

那么有了它之后,我们就可以省去新建类的步骤,对于单一的基础类型变量,直接创建ValueNotifier即可,就像上面的例子,我们可以直接改造成下面这样。

代码语言:javascript
复制

dart
class _CountNotifierState extends State<CountNotifierWidget> {
  final ValueNotifier<int> _countNotify = ValueNotifier(0);

  @override
  void initState() {
    super.initState();
    _countNotify.addListener(updateCount);
  }

  void updateCount() {
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text("Test: ${_countNotify.value}"),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _countNotify.value++,
        child: const Icon(Icons.add),
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();
    _countNotify.removeListener(updateCount);
  }
}

这样我们就简化了不少的模板代码。

ValueListenableBuilder

我们从ChangeNotifier到ValueNotifier,逐步减少了模板代码的创建,但是依然还有很多问题,比如我们还是需要手动addListener、removeListener或者是dispose,同时,还需要使用setState来刷新页面,如果Context控制不好,很容易造成整个页面的刷新。因此,Flutter在它们的基础之上,又提供了ValueListenableBuilder来解决上面这些问题。

我们继续改造上面的例子。

代码语言:javascript
复制

dart
class _CountNotifierState extends State<CountNotifierWidget> {
  final ValueNotifier<int> _countNotify = ValueNotifier(0);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ValueListenableBuilder<int>(
          valueListenable: _countNotify,
          builder: (context, value, child) {
            return Text('Value: $value');
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _countNotify.value++,
        child: const Icon(Icons.add),
      ),
    );
  }

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

可以发现,我们使用ValueListenableBuilder来根据ValueNotifier的改变而刷新Widget。这样不仅简化了代码模板,而且不再使用setState来进行页面刷新。

ValueListenableBuilder作为一个非常经典的Widget,在它的注释中,就有很多教程和示例。

再看它的源码。

这里需要接收3个参数,其中valueListenable用来接收ValueNotifier,builder用来构建Widget,而child,用来创建不依赖ValueNotifier构建的Widget(这是一个很经典的性能优化的例子,如果子构建成本高,并且不依赖于通知符的值,我们将使用它进行优化)。

这个优化方案非常经典,在Flutter的很多地方都有使用这个技巧,特别是动画这块的处理。通常来说ValueNotifier对应ValueListenableBuilder,Listenable、ChangeNotifier对应AnimatedBuilder。

自定义类型

在使用自定义类型时,例如一个包装类,那么当你改变它的某个属性值时,ValueListenableBuilder是不会刷新的,我们来看下面这个例子。

代码语言:javascript
复制

dart
class Wrapper {
  int age;
  String name;

  Wrapper({this.age = 0, this.name = ''});
}

class _CountNotifierState extends State<CountNotifierWidget> {
  final ValueNotifier<Wrapper> _countNotify = ValueNotifier(Wrapper());

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ValueListenableBuilder<Wrapper>(
          valueListenable: _countNotify,
          builder: (context, value, child) {
            return Text('Value: ${value.age}');
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _countNotify.value.age = _countNotify.value.age + 1,
        child: const Icon(Icons.add),
      ),
    );
  }

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

这样的话,ValueListenableBuilder就失去作用了,其原因也很简单,ValueNotifier所监听的数据其实并未发生改变,实例的内存地址没发生改变,所以,直接创建一个新的对象,就可以触发更新了,就像下面这样。

代码语言:javascript
复制

dart
onPressed: () => _countNotify.value = Wrapper(age: 10),

自定义类型局部刷新

上面这种自定义模型的刷新方法还是略显复杂了一点,每次更新的时候,都要copy一下数据来实现更新,实际上,ValueNotifier继承自ChangeNotifier,所以可以通过手动调用notifyListeners的方式来进行刷新,我们改造下上面的例子。

代码语言:javascript
复制

dart
class WrapperNotifier extends ValueNotifier<Wrapper> {
  WrapperNotifier(Wrapper value) : super(value);

  void increment() {
    value.age++;
    notifyListeners();
  }
}

// 调用处
_countNotify.increment();

通过这种方式,我们可以实现当模型内部变量更新时,局部进行刷新了。

本文原创公众号:群英传,授权转载请联系微信(Tomcat_xu),授权后,请在原创发表24小时后转载。

< END >

作者:徐宜生

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-06-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 群英传 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ChangeNotifier
  • ValueNotifier
  • ValueListenableBuilder
    • 自定义类型
      • 自定义类型局部刷新
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档