【问题标题】:In flutter, how can I prevent expensive child widget from getting rebuilt multiple times?在颤振中,如何防止昂贵的子小部件多次重建?
【发布时间】:2020-05-13 08:51:12
【问题描述】:

我有 2 个小部件。一个父 StatefulWidget 在第一次满载期间多次更改状态,一个子 StatelessWidget 构建起来很昂贵。

子小部件构建成本高的原因是它使用google_maps_flutter 库,该库使用网络视图在屏幕上显示谷歌地图。

如果可能,我希望此子小部件中的build() 函数只执行一次。

但是,每当由于状态更改而多次构建父窗口小部件时,似乎子窗口小部件也被构建多次。因此,我在第一次加载屏幕时看到了一些卡顿/滞后。

防止子小部件多次重建的最佳方法是什么?

以下是示例代码。

父小部件

class ParentWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
   Completer<GoogleMapController> _controller = Completer();
   LocationPosition _myPosition;

   @override
   void initState() {
     super.initState();
     initialize();
   }

   void initialize(){
     ....// other initialization logic which may call `setState()` multiple times
   }

   Set<Marker> getMarkers(){
     ....
   }

   @override
   Widget build(BuildContext context) {
     return Scaffold(body: GoogleMapWidget(_controller, _myPosition, getMarkers()));
   }
}

子小部件

class GoogleMapWidget extends StatelessWidget {
  static const double ZOOM = 15;
  final Completer<GoogleMapController> _controller;
  final Set<Marker> markers;
  final LocationPosition _myPosition;
  GoogleMapWidget(this._controller, this._myPosition, this.markers);

  @override
  Widget build(BuildContext context) {
    print("Rebuilt"); // <-- This gets printed multiple times, which is not something I want.
    return GoogleMap(
        mapType: MapType.normal,
        initialCameraPosition: CameraPosition(
          target: LatLng(_myPosition.lat, _myPosition.lng),
          zoom: ZOOM,
        ),
        onMapCreated: (GoogleMapController controller) {
          _controller.complete(controller);
        },
        markers: markers);
  }
}

【问题讨论】:

  • 我会使用其中一种状态管理系统,而不是尝试手动管理 setState。查看提供程序包。它具有仅在必要时构建的选项。

标签: flutter


【解决方案1】:

每次调用 setState 时,都会触发重建。在 initState 中调用它可能不是一个好主意。多次调用它是不行的。

Importance of Calling SetState inside initState

为避免多次重建,

  • 初始化本地字段中的所有对象,只有当所有对象都准备好后,才能调用 setState 一次。

  • 除了在 setState 中设置变量之外,不要做任何其他处理。

  • 不要在 build 方法中触发 http/api 调用。即使您是,也请添加条件逻辑以仅在需要时这样做。

  • 在调用 setState 之前,您可能需要检查对象是否实际更改。

特别是谷歌地图包,请查看 MapController 方法,因为您可以使用提供的方法而不是设置小部件本身的属性。这样,谷歌地图包可以确定是否需要 setState。

【讨论】:

  • initState中调用setState的原因是为了检索[1.用户当前位置],然后 [2.获取附近的标记位置]。这些需要执行一次,因此我将它们放入initState。这不是放置它们的最佳位置吗?
  • 我假设这两个调用都是异步的。所以可以在 initState 中启动它,当它们完成时,您可以调用 setState(在 initState 方法之外)。还有为什么你需要在获取位置后设置状态,你不能等到你得到标记。
  • 获取用户位置需要500ms~2000ms,获取位置需要500ms~3000ms。我们通过首先显示 [“我们正在确定您的位置...”] 来向用户提供早期 UX 反馈,当用户的位置信息可用时,我们会显示 [“我们正在寻找您附近的兴趣点...”]更好的用户体验。这就是setState 被多次调用的原因。
猜你喜欢
  • 1970-01-01
  • 2020-08-05
  • 1970-01-01
  • 2020-07-23
  • 1970-01-01
  • 2019-01-14
  • 1970-01-01
  • 2020-09-07
  • 2021-03-18
相关资源
最近更新 更多