Flutter Provider状态管理图解实战 第二篇

2026-05-30阅读 0热度 0
Pro

Flutter 的状态管理方案中,Provider 家族提供了多种数据绑定方式。除了之前讨论的 ChangeNotifierProvider,今天继续剖析 ListenableProvider、ValueListenableProvider 和 StreamProvider 的实际用法。尽管名称各异,核心逻辑高度一致:绑定数据,再消费数据。

Flutter 47: 图解新的状态管理 Provider (二)

ListenableProvider 用法

1. 数据绑定

先看构造器方式:ListenableProvider({Key key, @required ValueBuilder builder, Disposer dispose, Widget child })。通过 builder 生成一个可监听对象,当该 Provider 从 Widget Tree 移除时,会自动调用 dispose 释放资源。builder 参数不可为空,否则运行时报错。贴个简单示例:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ListenableProvider(
      builder: (_) => User('Flutter', 0),
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(primarySwatch: Colors.blue),
        home: MyHomePage(title: 'Provider Demo')
      )
    );
  }
}

另一种是 .value 方式,直接传入现成的 listenable 实例:

ListenableProvider.value({
  Key key,
  @required T listenable,
  Widget child
})
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ListenableProvider.value(
      listenable: User('Flutter', 0),
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(primarySwatch: Colors.blue),
        home: MyHomePage(title: 'Provider Demo')
      )
    );
  }
}

2. 获取数据

数据消费阶段,不同 Provider 的获取模式几乎一致,常用两种手法。

方式一:Provider.of(context)

class ProviderText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final user = Provider.of(context);
    return Text('${user.getName}');
  }
}

方式二:Consumer Widget 构造器

class ProviderText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer(
      builder: (context, user, _) {
        return Text(user.getName);
      }
    );
  }
}

两种方式都能拿到数据,Consumer 的优势在于仅在 builder 内部重建,粒度更细。

ValueListenableProvider 用法

使用 ValueListenableProvider 时,数据类必须继承自 ValueNotifier,并实现其构造方法。这样就能通过操作 value 触发界面更新。构建一个 Person 实体类作为示例:

// 基础数据类型
class StringBean extends ValueNotifier {
  StringBean(String value) : super(value);
}

// 自定义实体类
class Person extends ValueNotifier {
  Person(User value) : super(value);
  String get getPersonName => value.name;
  void setPersonName(String name) {
    value.name = name;
    notifyListeners();
  }
}

1. 绑定数据

构造器方式:ValueListenableProvider({Key key, @required ValueBuilder> builder, UpdateShouldNotify updateShouldNotify, Widget child }),builder 同样不能为空。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ValueListenableProvider(
      builder: (_) => Person(User('person', 101)),
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(primarySwatch: Colors.blue),
        home: MyHomePage(title: 'Provider Demo')
      )
    );
  }
}

.value 方式:ValueListenableProvider.value({Key key, @required ValueListenable valueListenable, UpdateShouldNotify updateShouldNotify, Widget child })

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ValueListenableProvider.value(
      valueListenable: Person(User('person', 101)),
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(primarySwatch: Colors.blue),
        home: MyHomePage(title: 'Provider Demo')
      )
    );
  }
}

2. 获取数据

依然是 Provider.of 和 Consumer 两种方式,不再重复贴代码。唯一需要留意:数据类更新 value 后必须手动调用 notifyListeners(),否则界面不会刷新。

class ProviderText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final number = Provider.of(context);
    final person = Provider.of(context);
    return Center(
      child: Column(
        children: [
          Text('${number.toString()}==${person.getName}'),
          Consumer(
            builder: (context, user, _) {
              return Text(user.getName);
            }
          )
        ]
      )
    );
  }
}

StreamProvider 用法

1. Stream 简介

Stream 来自 Dart:async 库,专用于处理异步数据流。常用于列表实时刷新、网络轮询等场景。可以理解为一条管道:通过 StreamControllersink.add() 塞入数据,再用 stream 输出处理后的结果。单流与多流的区别后续再深入,先看如何用它做状态管理。

2. 绑定数据

构造器方式:StreamProvider({Key key, @required ValueBuilder> builder, T initialData, ..., Widget child })。务必在 initialData 中提供初始值,否则界面为空。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StreamProvider(
      builder: (_) => StreamController(),
      initialData: Teacher('Teacher', 101),
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(primarySwatch: Colors.blue),
        home: MyHomePage(title: 'Provider Demo')
      )
    );
  }
}

class Teacher {
  var tname;
  var tage;
  Teacher(this.tname, this.tage);
}

// 在 TextField 中触发更新
Expanded(
  child: TextField(
    onChanged: (changed) {
      teacher.tname = changed;
      teacher.tage = 150;
      StreamController().sink.add(teacher);
    },
    controller: _phonecontroller,
    decoration: InputDecoration(
      hintText: '请输入用户名',
      suffixIcon: IconButton(
        icon: Icon(Icons.clear, color: Colors.black45),
        onPressed: () { _phonecontroller.clear(); }
      )
    )
  )
)

.value 方式:直接传入一个 stream,同样需要 initialData。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StreamProvider.value(
      stream: StreamController().stream,
      initialData: Teacher('Teacher', 101),
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(primarySwatch: Colors.blue),
        home: MyHomePage(title: 'Provider Demo')
      )
    );
  }
}

3. 获取数据

class ProviderText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final teacher = Provider.of(context);
    return Center(
      child: Column(
        children: [
          Text('${teacher.tname}'),
          Consumer(
            builder: (context, teacher, _) {
              return Text('${teacher.tname}==${teacher.tage}');
            }
          )
        ]
      )
    );
  }
}

总结

结合上一节的 ChangeNotifierProvider 来看,这几个 Provider 的本质高度相似。翻阅源码就清楚了:

class ChangeNotifierProvider
    extends ListenableProvider
    implements SingleChildCloneableWidget {}

class ChangeNotifier implements Listenable {}

class ValueListenableProvider
    extends AdaptiveBuilderWidget, ValueNotifier>
    implements SingleChildCloneableWidget {}

class ValueNotifier extends ChangeNotifier implements ValueListenable {}

ChangeNotifierProvider 其实就是 ListenableProvider 的子类,因为 ChangeNotifier 实现了 Listenable。而 ValueNotifier 又继承自 ChangeNotifier,所以 ValueListenableProvider 和 ChangeNotifierProvider 在很多场景下可以互换。区别在于绑定的数据类需要继承不同的父类:

class User with ChangeNotifier {}
class Person extends ValueNotifier {}

无论使用哪种 Provider,若采用 .value 绑定方式,记得在 dispose 中关闭对应的 listener,避免内存泄漏:

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

Provider 五种方式(ChangeNotifierProvider、ListenableProvider、ValueListenableProvider、StreamProvider 以及基础的 Provider),经过实际对比,设计思路高度一致:构造器绑定或值绑定,然后通过 Provider.of 或 Consumer 消费。遇到复杂的实体类时,让它继承对应的 Listenable 或 ValueNotifier,再加上 dispose 处理,基本就能流畅运行。

免责声明

本网站新闻资讯均来自公开渠道,力求准确但不保证绝对无误,内容观点仅代表作者本人,与本站无关。若涉及侵权,请联系我们处理。本站保留对声明的修改权,最终解释权归本站所有。

相关阅读

更多
欢迎回来 登录或注册后,可保存提示词和历史记录
登录后可同步收藏、历史记录和常用模板
注册即表示同意服务条款与隐私政策