在SingleChildScrollView中时,GoogleMap的onCameraIdle停止触发

时间:2019-07-14 11:29:56

标签: google-maps flutter

我有一个设计,其中SingleChildScrollView在屏幕的前50%处具有GoogleMap,而在较低的50%中具有项目列表。用户可以向上滚动整个视图以查看所有列表。但是,有时,如果用户滚动页面,则Map会停止触发onCameraIdle,而只是不会再次触发它。

onCameraMove和onTap可以正常工作。只是onCameraIdle不会触发。

 SingleChildScrollView(
        child: Column(
          children: <Widget>[
            Stack(
              children: <Widget>[
                Container(
                  height: screenSize.height / 2,
                  child: GoogleMap(
                    key: Key("GMap"),
                    mapType: MapType.normal,
                    markers: Set<Marker>.of(markers.values),
                    gestureRecognizers: Set()
                      ..add(Factory<PanGestureRecognizer>(
                          () => PanGestureRecognizer()))
                      ..add(
                        Factory<VerticalDragGestureRecognizer>(
                            () => VerticalDragGestureRecognizer()),
                      )
                      ..add(
                        Factory<HorizontalDragGestureRecognizer>(
                            () => HorizontalDragGestureRecognizer()),
                      )
                      ..add(
                        Factory<ScaleGestureRecognizer>(
                            () => ScaleGestureRecognizer()),
                      ),
                    initialCameraPosition: CameraPosition(
                        target: LatLng(14.551620, 121.053329), zoom: 14.5),
                    onMapCreated: (GoogleMapController controller) {
                      if (!_controller.isCompleted) {
                        _controller.complete(controller);
                        _lastCameraPosition = CameraPosition(
                            target: LatLng(14.551620, 121.053329), zoom: 14.5);
                      }
                    },
                    myLocationEnabled: true,
                    myLocationButtonEnabled: true,
                    onCameraIdle: () {
                      print("547: onCameraIdle");
                      _fetchOffers();
                    },
                    onCameraMove: (value) {
                      print("552: onCameraMove");
                      _lastCameraPosition = value;
                    },
                    onTap: (value) {
                      // Load items for current view if deselecting a marker
                      print('556: Tapped outside');
                    },
                  ),
                ),
                Positioned(
                  top: 50,
                  right: 20,
                  child: Container(
                    height: 30,
                    decoration: BoxDecoration(
                        border: Border.all(
                            color: _userBalance > 0
                                ? globals.themeColor4
                                : globals.themeColor2,
                            width: 2),
                        boxShadow: [
                          BoxShadow(
                            blurRadius: 10.0,
                            color: Colors.black.withOpacity(.5),
                            offset: Offset(3.0, 4.0),
                          ),
                        ],
                        color: Colors.white,
                        borderRadius: BorderRadius.all(Radius.circular(10.0))),
                    child: Center(
                      child: Padding(
                        padding: EdgeInsets.fromLTRB(10, 5, 10, 5),
                        child: Text(
                          "Balance: \u{20B1} ${_userBalance.toStringAsFixed(0)}",
                          style: TextStyle(
                            color: Colors.black,
                            fontSize: 14,
                          ),
                        ),
                      ),
                    ),
                  ),
                ),
              ],
            ),
            AnimatedContainer(
              color: Colors.white,
              // Use the properties stored in the State class.
              width: double.infinity,
              height: _loaderHeight,
              // Define how long the animation should take.
              duration: Duration(seconds: 1),
              // Provide an optional curve to make the animation feel smoother.
              curve: Curves.fastOutSlowIn,
              child: Center(
                child: Text(
                  "Loading, please wait",
                  style: TextStyle(color: Colors.grey),
                ),
              ),
            ),
            Container(
              color: Colors.white,
              child: _offers == null
                  ? Container(
                      child: Padding(
                        padding: EdgeInsets.all(30),
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.start,
                          children: <Widget>[
                            Icon(MdiIcons.foodAppleOutline,
                                size: 60, color: globals.themeColor4),
                            Padding(padding: EdgeInsets.only(right: 20)),
                            Expanded(
                              child: Column(
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: <Widget>[
                                  Text("Fetching offers",
                                      style: TextStyle(
                                          color: globals.themeColor4,
                                          fontWeight: FontWeight.bold)),
                                  Padding(padding: EdgeInsets.only(top: 5)),
                                  Text(
                                      "We are fetching offers for you, please hold on...",
                                      style: TextStyle(color: Colors.grey)),
                                ],
                              ),
                            ),
                          ],
                        ),
                      ),
                    )
                  : Column(children: _offers),
            ),
          ],
        ),
      ),

任何人以前都遇到过这个问题并有解决方案吗?

2 个答案:

答案 0 :(得分:0)

您可以将ListView或CustomScrollView与KeepAlive一起使用

这可以防止在滚动到视图之外时将小部件扔掉。

我也建议您深入研究ScrollController class

答案 1 :(得分:0)

我复制了您的样本的简单版本,但无法重现onCameraIdle无法触发的问题。

现在,根据我的示例,您可能会误解了某些行为,但实际上是滚动视图的行为接管了(因为所有这些都在滚动视图内):

  1. 有时地图上的向下手势会拉动滚动视图而不是地图。
  2. 向上的手势将滚动滚动视图,而不是与地图进行交互。

但是在您的其余代码中没有任何进一步的细节,或者没有mcve可以很容易地重现您的问题,很难说出真正的情况。

但是,作为Patrick Kelly mentioned,缺少KeepAlive也有可能最终导致暂时废弃地图小部件。这就是为什么建议使用ListView的原因,因为它内置于此功能。

另一方面,您也可以实现AutomaticKeepAliveClientMixin以获得类似的效果,如https://stackoverflow.com/a/51738269/6668797所示(但要注意小部件处理的警告)。

无论如何,这是我的示例,我不得不对您的_fetchOffers()是什么进行过有根据的猜测:

class _MyHomePageState extends State<MyHomePage> {

  // testing
  int fetchCount = 0;
  List<Widget> _offers;
  _fetchOffers() {
    fetchCount++;
    // simulate varying data
    var rng = new Random();
    int start = rng.nextInt(10);
    int end = start + 3 + rng.nextInt(30);
    // build sample list
    List<Widget> list = new List();
    for (int i = start; i < end; i++) {
      list.add(Text('offer$i', style: new TextStyle(fontSize: 30.0)));
    }

    // assuming you are using setState()
    setState(() {
      _offers = list;
    });
  }

  // from google maps sample
  Completer<GoogleMapController> _controller = Completer();
  static final CameraPosition _kGooglePlex = CameraPosition(
    target: LatLng(37.42796133580664, -122.085749655962),
    zoom: 14.4746,
  );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(widget.title)),
      body: SingleChildScrollView(
        child: Column(
          children: <Widget>[
            Stack(
              children: <Widget>[
                Container(
                  height: MediaQuery.of(context).size.height / 2,
                  child: GoogleMap(
                    mapType: MapType.normal,
                    initialCameraPosition: _kGooglePlex,
                    gestureRecognizers: Set()
                      ..add(Factory<PanGestureRecognizer>(() => PanGestureRecognizer()))
                      ..add(Factory<VerticalDragGestureRecognizer>(() => VerticalDragGestureRecognizer()))
                      ..add(Factory<HorizontalDragGestureRecognizer>(() => HorizontalDragGestureRecognizer()))
                      ..add(Factory<ScaleGestureRecognizer>(() => ScaleGestureRecognizer())),
                    onMapCreated: (GoogleMapController controller) {
                      _controller.complete(controller);
                    },
                    onCameraIdle: () {
                      _fetchOffers();
                    },
                  ),
                ),
              ]
            ),
            Container(
              child: _offers == null
                  ? Container(child: Text("Fetching offers"))
                  : Column(children: _offers)
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
          // for logging _fetchOffers activity
          child: Text(fetchCount.toString())
      ),
    );
  }
}

onCameraIdle每次都为我解雇,我可以通过不断变化的商品数据以及fetchCount日志直观地确认这一点。