屏幕加载时不会在用户位置绘制地图

时间:2020-03-04 08:27:00

标签: flutter bloc

我对Flutter / Dart还是很陌生,并且正在使用flutter_bloc bloc /存储库模式,并且在我的应用程序中遇到了奇怪的行为。 所有打印均显示所有流和块模式均正常工作,传入状态带有坐标,并且拥有最新坐标的变量永远不会为null,但是在加载MapScreen时,它不会在userLocation坐标上绘制地图。 另外,当按下中心按钮时,有时会出现The method 'move' was called on null.错误。

在MapScreen userLocation中,其值来自传入状态

return BlocBuilder<MapBloc, MapState>(
//        bloc: MapBloc(mapRepository: _mapRepository),
        builder: (BuildContext context, MapState state) {
      userLocation = (state as LocationStream).location;
//      if (state is LocationStream) {
//        userLocation = (state).location;
//      }
      return Scaffold( 
然后

用于在用户位置绘制地图

child: FlutterMap(
                    options: MapOptions(
                      center: userLocation,
                      minZoom: 5.0,
                      maxZoom: 19.0,
                    ),
                    mapController: _mapController,

在地图上显示用户

Marker(
       point: userLocation,
       height: 200,
       width: 200,
       builder: (context) => IconButton(
           icon: Icon(Icons.location_on),
           color: Colors.red,
           iconSize: 60,
           onPressed: () {
            print('icon tapped');
           },
       ),
     ),

并使地图重新居中于用户位置。

RaisedButton(
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(5)),
                      onPressed: () {
                        print(
                            ' @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@   userLocation is $userLocation');
                        _mapController.move(userLocation, 16);
                      },
                      color: Colors.red,
                      child: Padding(
                        padding: EdgeInsets.all(8.0),
                        child: Text(
                          'center',
                          style: TextStyle(color: Colors.white, fontSize: 30),
                        ),
                      ),
                    ),

奇怪的行为是,有时当我按下中心按钮时会出现The method 'move' was called on null.错误,但并非总是如此。

有时,每次我按一下它都会起作用,但是有时几次后它会发现null值。

在第一次构建时,中心按钮会按预期工作并使地图居中,但在热重载或热重启动后始终会找到null值。

您能看到我在做什么吗? 非常感谢您的时间和帮助。

完整的用户界面代码:

class MapScreen extends StatelessWidget {
  final String name;
  final MapRepository _mapRepository;
  final MapController _mapController;
//  final AlertRepository _alertRepository;
  List<Marker> alerts;
  LatLng userLocation;
  MapScreen(
      {Key key, @required this.name, @required MapRepository mapRepository})
      : assert(mapRepository != null),
        _mapRepository = mapRepository,
        _mapController = MapController(),
//        _alertRepository = alertRepository,
        super(key: key);


  @override
  Widget build(BuildContext context) {
    return BlocBuilder<MapBloc, MapState>(
//        bloc: MapBloc(mapRepository: _mapRepository),
        builder: (BuildContext context, MapState state) {
      userLocation = (state as LocationStream).location;
//      if (state is LocationStream) {
//        userLocation = (state).location;
//      }
      return Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.transparent,
          elevation: 0,
          title: Text(
            'Home',
            style: TextStyle(color: Colors.orangeAccent, fontSize: 40),
          ),
          actions: <Widget>[
            IconButton(
              icon: Icon(
                Icons.exit_to_app,
                color: Colors.orange,
                size: 35,
              ),
              onPressed: () {
                BlocProvider.of<AuthenticationBloc>(context).add(
                  LoggedOut(),
                );
              },
            ),
          ],
        ),
        backgroundColor: Colors.white,
        body: SafeArea(
          minimum: EdgeInsets.symmetric(horizontal: 20),
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Container(
                  height: 570,
                  width: 320,
                  child: FlutterMap(
                    options: MapOptions(
                      center: userLocation,
                      minZoom: 5.0,
                      maxZoom: 19.0,
                    ),
                    mapController: _mapController,
                    layers: [
                      //
//        PolygonLayer(polygonOpts, map, stream)
//                    PolygonLayerOptions(
//                      polygons:
//                    ),
                      TileLayerOptions(
                          urlTemplate:
                              'https://api.openrouteservice.org/mapsurfer/{z}/{x}/{y}.png?api_key=5b3ce3597851110001cf62484c4b65d85bc844eca3a2c6b9f300ddf4',
//                              urlTemplate:
//                                  'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
                          subdomains: ['a', 'b', 'c'],
                          keepBuffer: 20),
                      new MarkerLayerOptions(
                        markers: [
                          Marker(
                            point: userLocation,
                            height: 200,
                            width: 200,
                            builder: (context) => IconButton(
                              icon: Icon(Icons.location_on),
                              color: Colors.red,
                              iconSize: 60,
                              onPressed: () {
                                print('icon tapped');
                              },
                            ),
                          ),
//                          Marker(
//                            height: ,
//                            builder: BlocBuilder<AlertBloc,AlertState>(
//                                builder: (BuildContext context, AlertState state) {
//                                  alerts = (state as AlertsUpdated).alerts;
//                                  return
//                                }),
//                          )
                        ],
                      ),
                    ],
                  ),
                ),
                SizedBox(
                  height: 10,
                ),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: <Widget>[
                    RaisedButton(
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(5)),
                      onPressed: () {
                        print(
                            ' @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@   userLocation is $userLocation');
                        _mapController.move(userLocation, 16);
                      },
                      color: Colors.red,
                      child: Padding(
                        padding: EdgeInsets.all(8.0),
                        child: Text(
                          'center',
                          style: TextStyle(color: Colors.white, fontSize: 30),
                        ),
                      ),
                    ),
                    RaisedButton(
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(5)),
                      onPressed: () {
                        //TODO  this goes actually in a alert icon callbac, here just navigates icons vc
                      },
                      color: Colors.red,
                      child: Padding(
                        padding: EdgeInsets.all(8.0),
                        child: Text(
                          'alert',
                          style: TextStyle(color: Colors.white, fontSize: 30),
                        ),
                      ),
                    ),
                  ],
                ),
              ],
            ),
          ),
        ),
      );
    });
  }
}

我还整理了一个示例应用程序,以备您查看。 https://github.com/vinnytwice/flutter_app

更新:

在发现FlutterMap必须位于有状态的小部件中之后,我更改了我的代码。在进入状态时,我对State类型进行了检查,并使用来自state的新值调用了setState()。错误。如果我正确记得该错误,则该构建器将重建建筑小部件类型的错误。 .. BlocBuilder不再是正确的选择,因此我将其替换为BlocListener,它在States上具有回调,并在那里调用setState()。 现在,即使在热重载或热重启动后,FlutterMap仍会在buttonPressed上的userLocation上绘制。并且不再出现The method 'move' was called on null.错误。

在屏幕加载时,仍未在userLocation上绘制FlutterMap。 可以看到我所缺少的吗?可能是一些初始坐标? 无论如何.. setState()不应跟踪userLocation的使用位置并重建那些小部件吗?

如果我在setState()之后立即调用_mapController.move(userLocation,16);地图将跟随用户位置,这对于分步导航器很有用,但不是我所需要的。进入新状态时将其平移,地图即会移动。

所以新代码是:

// stateful widget using BlocListener:

class MapScreen extends StatefulWidget {
  final String name;
  final MapRepository _mapRepository;
//  MapController _mapController;
//  final AlertRepository _alertRepository;

  MapScreen(
      {Key key, @required this.name, @required MapRepository mapRepository})
      : assert(mapRepository != null),
        _mapRepository = mapRepository,
//        _mapController = MapController(),
//        _alertRepository = alertRepository,
        super(key: key);

  @override
  _MapScreenState createState() => _MapScreenState();
}

class _MapScreenState extends State<MapScreen> {
  List<Marker> alerts;

  LatLng userLocation;

  MapController _mapController = MapController();

  @override
  Widget build(BuildContext context) {
    return BlocListener<MapBloc, MapState>(
//        bloc: MapBloc(mapRepository: _mapRepository),
      listener: (BuildContext context, MapState state) {
//      userLocation = (state as LocationStream).location;
        if (state is LocationStream) {
          setState(() {
            userLocation = (state).location;
          });
        }
      },
      child: Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.transparent,
          elevation: 0,
          title: Text(
            'Home',
            style: TextStyle(color: Colors.orangeAccent, fontSize: 40),
          ),
          actions: <Widget>[
            IconButton(
              icon: Icon(
                Icons.exit_to_app,
                color: Colors.orange,
                size: 35,
              ),
              onPressed: () {
//              BlocProvider.of<AuthenticationBloc>(context).add(
//                LoggedOut(),
//              );
              },
            ),
          ],
        ),
        backgroundColor: Colors.white,
        body: SafeArea(
          minimum: EdgeInsets.symmetric(horizontal: 20),
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Container(
                  height: 570,
                  width: 320,
                  child: FlutterMap(
                    options: MapOptions(
                      center: userLocation,
                      minZoom: 5.0,
                      maxZoom: 19.0,
                    ),
                    mapController: _mapController,
                    layers: [
                      //
//        PolygonLayer(polygonOpts, map, stream)
//                    PolygonLayerOptions(
//                      polygons:
//                    ),
                      TileLayerOptions(
                          urlTemplate:
                              'https://api.openrouteservice.org/mapsurfer/{z}/{x}/{y}.png?api_key=5b3ce3597851110001cf62484c4b65d85bc844eca3a2c6b9f300ddf4',
//                              urlTemplate:
//                                  'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
                          subdomains: ['a', 'b', 'c'],
                          keepBuffer: 20),
                      new MarkerLayerOptions(
                        markers: [
                          Marker(
                            point: userLocation,
                            height: 200,
                            width: 200,
                            builder: (context) => IconButton(
                              icon: Icon(Icons.location_on),
                              color: Colors.red,
                              iconSize: 60,
                              onPressed: () {
                                print('icon tapped');
                              },
                            ),
                          ),
//                          Marker(
//                            height: ,
//                            builder: BlocBuilder<AlertBloc,AlertState>(
//                                builder: (BuildContext context, AlertState state) {
//                                  alerts = (state as AlertsUpdated).alerts;
//                                  return
//                                }),
//                          )
                        ],
                      ),
                    ],
                  ),
                ),
                SizedBox(
                  height: 10,
                ),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: <Widget>[
                    RaisedButton(
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(5)),
                      onPressed: () {
                        print(
                            ' @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@   userLocation is $userLocation');
                        _mapController.move(userLocation, 16);
                      },
                      color: Colors.red,
                      child: Padding(
                        padding: EdgeInsets.all(8.0),
                        child: Text(
                          'center',
                          style: TextStyle(color: Colors.white, fontSize: 30),
                        ),
                      ),
                    ),
                    RaisedButton(
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(5)),
                      onPressed: () {
                        //TODO  this goes actually in a alert icon callbac, here just navigates icons vc
                      },
                      color: Colors.red,
                      child: Padding(
                        padding: EdgeInsets.all(8.0),
                        child: Text(
                          'alert',
                          style: TextStyle(color: Colors.white, fontSize: 30),
                        ),
                      ),
                    ),
                  ],
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
} 

1 个答案:

答案 0 :(得分:0)

我终于做到了。我最终使用了一种方法,以为我会被分类为Stream<LatLng> userLocation,所以一周前我就搁置了,因为我还不知道如何正确使用期货。

在存储库中,我使用一种仅返回Future<LatLng>的方法:

Future<LatLng> getLocation() async {
    print('getLocation() called');
    Position position;
    LatLng location;
    try {
      position = await locationManager
          .getCurrentPosition(
              desiredAccuracy: LocationAccuracy.bestForNavigation)
          .timeout(Duration(seconds: 5));
      location = LatLng(position.latitude, position.longitude);
      print('getLocation() location is: $location');
      return location;
    } catch (error) {
      print(
          'getLocation(): error getting current location: ${error.toString()}');
    }
  }

现在从BlocProvider中的main()发送两个事件:

BlocProvider<MapBloc>(create: (context) {
          return MapBloc(
            mapRepository: mapRepository,
          )
            ..add(GetLocationStream())
            ..add(GetInitialLocation());
        }),

在bloc中产生一个状态,该状态的值来自`getLocation()。

Stream<MapState> _mapCenterMapToState(GetInitialLocation event) async* {
    location = await _mapRepository.getLocation();
    print('_mapCenterMapToState location is : ${_mapRepository.getLocation()}');
    yield MapLoading(location);
  } 

BlocListener中,我添加了一个新的状态检查,并使用状态值来移动地图。

BlocListener<MapBloc, MapState>(
            listener: (BuildContext context, MapState state) {
          if (state is LocationStream) {
            setState(() {
              userLocation = (state).location;
            });
          }
          if (state is MapLoading) {
            userLocation = (state).location;
            print(
                ' @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ MapBloc initial state location is : $userLocation');
            _mapController.move(userLocation, 16);
          }
        })

地图现在会在用户位置加载,但不会随其一起移动,用户位置图标会不断更新,并且在按下时中心按钮会使地图在用户位置居中。

希望这个漫长的问题和答案会帮助其他刚开始的人。 干杯。