为什么在 flutter_web 中更新 HtmlView 时出现 _state != _PlatformViewState.disposed is not true 异常?

Why do I get _state != _PlatformViewState.disposed is not true exception while updating HtmlView in flutter_web?

我正在尝试使用 HtmlView 在我的 flutter_web 应用程序中显示 Google 地图,但无法在标记更改时可靠地更新地图。它有一半时间工作正常。请看下面的代码。我订阅了 InitState() 中的标记更改。当标记数据更新并 listen returns 时,我每隔一次得到 get _state != _PlatformViewState.disposed 异常并且它每隔一次工作。

查看 Flutter_web 平台视图代码,似乎每次使用不同的 ViewType 创建新的 HtmlView 时都会处理以前的 HtmlView。重绘逻辑似乎有问题,因为在处理视图后立即发生此异常。此异常后的构建调用成功呈现 HtmlView。

══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following assertion was thrown building HtmlView(dependencies: [Directionality], state:
_HtmlViewState#705f2):
Assertion failed: org-dartlang-app:///packages/flutter_web/src/services/platform_views.dart:789:12
_state != _PlatformViewState.disposed
is not true

Either the assertion indicates an error in the framework itself, or we should provide substantially
more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
  https://github.com/flutter/flutter/issues/new?template=BUG.md

When the exception was thrown, this was the stack:
dart:sdk_internal 4543:15                                        assertFailed
removeOnPlatformViewCreatedListener
package:flutter_web/…/services/platform_views.dart:789
package:flutter_web/src%5Crendering%5Cplatform_view.dart 471:21  set viewController
updateRenderObject (package:flutter_web/src%5Cwidgets%5Cplatform_view.dart:752:17)
update (package:flutter_web/src%5Cwidgets%5Cframework.dart:4670:12)
updateChild (package:flutter_web/src%5Cwidgets%5Cframework.dart:2896:14)
performRebuild (package:flutter_web/src%5Cwidgets%5Cframework.dart:3908:16)
rebuild (package:flutter_web/src%5Cwidgets%5Cframework.dart:3713:5)
update (package:flutter_web/src%5Cwidgets%5Cframework.dart:4055:5)
updateChild (package:flutter_web/src%5Cwidgets%5Cframework.dart:2896:14)
update (package:flutter_web/src%5Cwidgets%5Cframework.dart:5069:14)
updateChild (package:flutter_web/src%5Cwidgets%5Cframework.dart:2896:14)
performRebuild (package:flutter_web/src%5Cwidgets%5Cframework.dart:3908:16)
rebuild (package:flutter_web/src%5Cwidgets%5Cframework.dart:3713:5)
update (package:flutter_web/src%5Cwidgets%5Cframework.dart:3953:5)
updateChild (package:flutter_web/src%5Cwidgets%5Cframework.dart:2896:14)
updateChildren (package:flutter_web/src%5Cwidgets%5Cframework.dart:4769:32)
update (package:flutter_web/src%5Cwidgets%5Cframework.dart:5186:17)
updateChild (package:flutter_web/src%5Cwidgets%5Cframework.dart:2896:14)
performRebuild (package:flutter_web/src%5Cwidgets%5Cframework.dart:3908:16)
rebuild (package:flutter_web/src%5Cwidgets%5Cframework.dart:3713:5)
update (package:flutter_web/src%5Cwidgets%5Cframework.dart:4164:5)
updateChild (package:flutter_web/src%5Cwidgets%5Cframework.dart:2896:14)
performRebuild (package:flutter_web/src%5Cwidgets%5Cframework.dart:3908:16)
rebuild (package:flutter_web/src%5Cwidgets%5Cframework.dart:3713:5)
update (package:flutter_web/src%5Cwidgets%5Cframework.dart:4164:5)
updateChild (package:flutter_web/src%5Cwidgets%5Cframework.dart:2896:14)
updateChildren (package:flutter_web/src%5Cwidgets%5Cframework.dart:4769:32)
update (package:flutter_web/src%5Cwidgets%5Cframework.dart:5186:17)
updateChild (package:flutter_web/src%5Cwidgets%5Cframework.dart:2896:14)
performRebuild (package:flutter_web/src%5Cwidgets%5Cframework.dart:3908:16)
rebuild (package:flutter_web/src%5Cwidgets%5Cframework.dart:3713:5)
update (package:flutter_web/src%5Cwidgets%5Cframework.dart:4055:5)
updateChild (package:flutter_web/src%5Cwidgets%5Cframework.dart:2896:14)
performRebuild (package:flutter_web/src%5Cwidgets%5Cframework.dart:3908:16)
rebuild (package:flutter_web/src%5Cwidgets%5Cframework.dart:3713:5)
update (package:flutter_web/src%5Cwidgets%5Cframework.dart:4164:5)
updateChild (package:flutter_web/src%5Cwidgets%5Cframework.dart:2896:14)
performRebuild (package:flutter_web/src%5Cwidgets%5Cframework.dart:3908:16)
rebuild (package:flutter_web/src%5Cwidgets%5Cframework.dart:3713:5)
update (package:flutter_web/src%5Cwidgets%5Cframework.dart:4055:5)
updateChild (package:flutter_web/src%5Cwidgets%5Cframework.dart:2896:14)
update (package:flutter_web/src%5Cwidgets%5Cframework.dart:5069:14)
updateChild (package:flutter_web/src%5Cwidgets%5Cframework.dart:2896:14)
performRebuild (package:flutter_web/src%5Cwidgets%5Cframework.dart:3908:16)
rebuild (package:flutter_web/src%5Cwidgets%5Cframework.dart:3713:5)
update (package:flutter_web/src%5Cwidgets%5Cframework.dart:3953:5)
updateChild (package:flutter_web/src%5Cwidgets%5Cframework.dart:2896:14)
update (package:flutter_web/src%5Cwidgets%5Cframework.dart:5069:14)
updateChild (package:flutter_web/src%5Cwidgets%5Cframework.dart:2896:14)
performRebuild (package:flutter_web/src%5Cwidgets%5Cframework.dart:3908:16)
rebuild (package:flutter_web/src%5Cwidgets%5Cframework.dart:3713:5)
update (package:flutter_web/src%5Cwidgets%5Cframework.dart:4164:5)
updateChild (package:flutter_web/src%5Cwidgets%5Cframework.dart:2896:14)
performRebuild (package:flutter_web/src%5Cwidgets%5Cframework.dart:3908:16)
rebuild (package:flutter_web/src%5Cwidgets%5Cframework.dart:3713:5)
update (package:flutter_web/src%5Cwidgets%5Cframework.dart:4055:5)
updateChild (package:flutter_web/src%5Cwidgets%5Cframework.dart:2896:14)
performRebuild (package:flutter_web/src%5Cwidgets%5Cframework.dart:3908:16)
rebuild (package:flutter_web/src%5Cwidgets%5Cframework.dart:3713:5)
buildScope (package:flutter_web/src%5Cwidgets%5Cframework.dart:2347:31)
drawFrame (package:flutter_web/src%5Cwidgets%5Cbinding.dart:710:49)
[_handlePersistentFrameCallback] (package:flutter_web/src%5Crendering%5Cbinding.dart:286:5)
[_invokeFrameCallback]
package:flutter_web/…/scheduler/binding.dart:1030
handleDrawFrame
package:flutter_web/…/scheduler/binding.dart:963
[_handleDrawFrame]
package:flutter_web/…/scheduler/binding.dart:874
<fn> (package:flutter_web_ui/src%5Cengine.dart:164:21)
════════════════════════════════════════════════════════════════════════════════════════════════════
class Panics extends StatefulWidget {
  final bool selectOnly;
  final bool showAddButton;
  final bool boListGrid;
  final bool boShowAppBar;

  Panics(this.selectOnly,
      [this.showAddButton = true,
      this.boListGrid = true,
      this.boShowAppBar = true]);


  @override
  _PanicsState createState() =>
      _PanicsState(selectOnly, showAddButton, boListGrid, boShowAppBar);
}

class _PanicsState extends State<Panics> {
  final bool selectOnly;
  final bool showAddButton;
  final bool boListGrid;
  final bool boShowAppBar;
  String createdViewId = 'hello-world-html';
  String strMap;
  bool inProgress = true;
  // Subscription
  StreamSubscription subscription;
  List<fs.DocumentSnapshot> panics;

  Map<String, bool> boShowGrid;
  List<fs.DocumentReference> isSelected;
  final key = GlobalKey<ScaffoldState>();

  // final _foldingCellKey = GlobalKey<SimpleFoldingCellState>();

  _PanicsState(
      this.selectOnly, this.showAddButton, this.boListGrid, this.boShowAppBar);



@override
  void initState() {
    debugPrint("PANICS: initState");
    // TODO: implement initState
    isSelected = List<fs.DocumentReference>();
    boShowGrid = Map<String, bool>();
    showPanics(null);
    fs.Query ref;
        if (Globals.role == Role.Manager) {
      // Make a reference to firestore
      ref = firestore()
          .collection('locations')
          .where("hotelId", "==", Globals.hotelID);
    } else {
      // Make a reference to firestore
      ref = firestore()
          .collection('locations')
          .where("userId", "==", Globals.userId);
    }

    ref = ref.where("status", "==", "new");

      subscription = ref.onSnapshot.listen((data) {
        showPanics(data.docs);
      });
    super.initState();
  }


  void showPanics(List<fs.DocumentSnapshot> panics) {
    String markers = "";
    String latlng = "var myLatLng = {lat: 41.850033, lng: -87.6500523}";
    DateTime max = DateTime.fromMicrosecondsSinceEpoch(0);
    fs.GeoPoint lastPanicPosition;

if (panics != null) {
     panics.forEach((data) {
      Panic panic = Panic.fromSnapshot(data);

      String strIcon = (panic.status == 'new')?"http://maps.google.com/mapfiles/ms/icons/red-dot.png":"http://maps.google.com/mapfiles/ms/icons/green-dot.png";
      String marker =
          "var marker = new google.maps.Marker({position: new google.maps.LatLng(${panic.position.latitude}, ${panic.position.longitude})," 
            + "map: map, icon:"$strIcon", title: '${Globals.globals.getUserName(panic.userId)} needs help!'});\n";
      markers += marker;
      DateTime time = panic.timeStamp;
            if (time.compareTo(max) > 0 ) {
        max = time;
        lastPanicPosition = panic.position;
      }
   });
         latlng = "var myLatLng = {lat: ${lastPanicPosition.latitude}, lng: ${lastPanicPosition.longitude}}";
}

debugPrint("Panics: showPanic markers = $markers");
debugPrint("Panics: showPanic latlng = $latlng");

    String createdViewUpdate = DateTime.now().toString();
    rootBundle.loadString('map.html').then((value) {
    String strMapUpdate = value.replaceAll(new RegExp(r'var myLatLng = {lat: 41.850033, lng: -87.6500523}'), latlng);
      strMapUpdate = strMapUpdate.replaceAll(new RegExp(r'markers'), markers);
      debugPrint ("Panics strMapUpdate =: $strMapUpdate");
      ui.platformViewRegistry.registerViewFactory(
          createdViewId,
          (int viewId) => IFrameElement()
            ..width = (MediaQuery.of(context).size.width - 400).toString()
            ..height = MediaQuery.of(context).size.height.toString()
            ..srcdoc = strMapUpdate
            ..style.border = 'none');
    });
if ((panics != null)&&(mounted)) {
    setState(() {
      inProgress = false;
      this.createdViewId = createdViewUpdate;
      this.panics = panics;
    });
}
  }


  @override
  Widget build(BuildContext context) {
  debugPrint("Panics build");
    return (((Globals.globals == null) || (Globals.documentID == null))
        //Uncomment this to use roomList
        //    ||(Globals.roomList.isEmpty)
        )
        ? Scaffold(body: Text("Loading..."))
        : (selectOnly)
            ? Scaffold(
                key: key,
                appBar: AppBar(title: Text(Globals.hotelName + ":Alerts")),
                body: _buildBody(context),
              )
            : (showAddButton)
                ? Scaffold(
                    floatingActionButton: FloatingActionButton(
                      heroTag: "panic",
                      backgroundColor: Colors.greenAccent,
                      mini: true,
                      child: Icon(Icons.person),
                      onPressed: () => setState(() {
                        _deletePanics(context);
                      }),
                    ),
                    floatingActionButtonLocation:
                        FloatingActionButtonLocation.endFloat,
                    appBar: (boShowAppBar)
                        ? AppBar(
                            title: Text(
                                (boListGrid) ? "Alerts list" : "Alerts grid"))
                        : null,
                    body: Row (
                            children: <Widget>[
                                  Container(
                                      width: MediaQuery.of(context).size.width - 400,
                                      child: HtmlView(
                                      viewType: createdViewId,
                                      onPlatformViewCreated: _onPlatformViewCreated,
                                      )),
                                  Container(
                                      width: 400,
                                      child: _buildBody(context),
                                  )
                            ]
                    )
                  )
                : (boShowAppBar)
                    ? Scaffold(
                        appBar: AppBar(title: Text("Panics")),
                        body: _buildBody(context),
                      )
                    : Scaffold(
                        body: _buildBody(context),
                      );
  }

我能够通过在构建 HtmlView 之前将对 ui.platformViewRegistry.registerViewFactory 的调用移动到构建函数来解决这个问题。我不得不通过将 HtmlView 包装到 FutureBuilder 小部件中来延迟 HtmlView 的重新加载,以便构建函数有时间从 Firebase 获取标记信息。请参阅下面的代码

  @override
  Widget build(BuildContext context) {
    debugPrint("Panics: build: boShowAppBar = $boShowAppBar ");
    //showPanics(null);
    return (((Globals.globals == null) || (Globals.documentID == null))
        //Uncomment this to use roomList
        //    ||(Globals.roomList.isEmpty)
        )
        ? Scaffold(body: Text("Loading..."))
        : (selectOnly)
            ? Scaffold(
                key: key,
                appBar: AppBar(title: Text(Globals.hotelName + ":Alerts")),
                body: _buildBody(context),
              )
            : Scaffold(
                    floatingActionButton: (showAddButton) ? FloatingActionButton(
                      heroTag: "panic",
                      backgroundColor: Colors.greenAccent,
                      mini: true,
                      child: Icon(Icons.delete),
                      tooltip: "Delete selected",
                      onPressed: () => setState(() {
                        _deletePanics(context);
                      }),
                    ):null,
                    floatingActionButtonLocation:
                        FloatingActionButtonLocation.endFloat,
                    appBar: (boShowAppBar)
                        ? AppBar(
                            title: Text(
                                (boListGrid) ? "Alerts list" : "Alerts grid"),
                            actions: <Widget>[
                              FlatButton(
                                  onPressed: () => setState(() {
                                        _deletePanics(context);
                                      }),
                                  child: Text("Delete selected",
                                      style: TextStyle(color: Colors.white))),
                            ],
                          )
                        : null,
                    body: Row(children: <Widget>[
                      Container(
                        width: 400,
                        child: _buildBody(context),
                      ),
                        Container(
                        width: MediaQuery.of(context).size.width - 400,
                        child: FutureBuilder(
                          future: Future.delayed(Duration(seconds: 1)),
                          builder: (c,s) => (s.connectionState == ConnectionState.done)?showMap():Container(),)
                      )
                    ]));
  }
  Widget showMap() {
    debugPrint("PANICS: showMap()");
    showPanics(this.panics);
    return HtmlView(      viewType: createdViewId,
                          onPlatformViewCreated: _onPlatformViewCreated,
                        );
  }