StreamProvider:避免重复构建

时间:2020-07-03 10:29:39

标签: flutter flutter-provider

我有一个使用相机插件的Flutter项目。在此插件中,我计算每个帧上的每个FPS,然后使用EventChannel将结果发送回Flutter。在Flutter端,我正在使用StreamProvider来仅更新显示实时FPS的TextWidget。

在我的小部件树顶部:

StreamProvider<FPSData>.value(value: initiated==false? null : streamControllerFPS.stream) 

我正在通过以下方式将fps值添加到streamController中

FPSData fpsData = new FPSData(event['fps'])
streamControllerFPS.add(fpsData);

class FPSData{
  int value;
  FPSData(dynamic data){
    value = data.round();
  }
}

在我的TextWidget(小部件树的底部)中:

class FPSWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    debugPrint("Building FPSWidget");
    FPSData fpsData;
    fpsData = Provider.of<FPSData>(context);
    if(fpsData==null)
      return Container();
    return Text("fps: "+fpsData.value.toString());
  }
} 

所有这些工作都按预期进行:每次将新的FPS值添加到流中时,FPS文本小部件都会重新绘制。

现在,我想知道的是,当FPS值更改时,是否有可能仅重绘窗口小部件。示例:如果上一个FPS值为31,新值也为31,我想跳过重建文本小部件的操作。 我知道使用ChangeNotifier很明显,但是我希望可以使用StreamProvider进行相同的操作。

到目前为止,我已经尝试了两种不同的解决方案:

  1. 结合.where()和.listen()方法
StreamProvider<FPSData>.value(value: initiated==false? null : streamControllerFPS.stream.where((newFPS) => newFPS.value!=lastFPSValue))

我使用.listen()方法保存 lastFPSValue 。似乎只有在FPS更改时才调用Provider.of,但是即使将StreamController和Stream设置为广播,我的TextWidget中也只能得到一个空值。

  1. 添加updateShouldNotify: (_, __) => true,但这也没有解决。似乎该问题可能是由于先前的FPSData(31)始终与新的FPSData(31)不同而引起的。我不知道如何使它们相等。

如果有人可以帮助我,我将不胜感激!预先感谢

2 个答案:

答案 0 :(得分:0)

在FPSData类中添加bool运算符似乎可以解决问题!我不会将其视为已被接受的答案,因为这样做可能会有更有效/正确的方式。

@immutable
class FPSData{
  final value;
  FPSData(this.value){
  }
  bool operator ==(Object fpsDataCompare) {
    return ((fpsDataCompare as FPSData).value == value);
  }
}

答案 1 :(得分:0)

Provider有一个名为Selector的整洁小部件,它使用提供的值并仅在满足条件时(例如,值不同时)才更新构建器

final value = context.select<FPSData, int>((FPSData fps) => fps.value); //this will update the fps value and rebuild only if the previous and new value are different
if(value == null) return Container();
return Text("fps: $value ");

这与Provider程序包的Selector小部件相同

return Selector<FPSData, int>(
  selector: (_, fps) => fps.value,
  shouldRebuild: (prev, curr) => prev != curr, //this compare the previous and current value, if you don't set this it will just use the default 
  //DeepCollectionEquality().equals(value, selected) which does the same, but works  too if the parameters are iterables (List, Map, etc)
  builder: (_, value, __) {
    if(value == null) return Container();
    return Text("fps: $value");
  },
);

第一个代码实际上是Provider 4.1.0中添加的一个新实现,但不太冗长,而且第一个代码不允许您更改shouldREbuild方法,以防您想应用更多的逻辑,而不仅仅是检查它们是否不同

也要添加

添加updateShouldNotify:(_,__)=>是,但这还行不通 出来。看来问题可能是由于事实 以前的FPSData(31)总是不同于新的 FPSData(31)。我不知道如何使它们相等。

那是因为您总是返回true,而不检查它们是否确实不同,也许可以这样尝试

updateShouldNotify: (prev, curr) => prev?.value != curr?.value

现在它将检查值是否不同并通知它们是否