我对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),
),
),
),
],
),
],
),
),
),
),
);
}
}
答案 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);
}
})
地图现在会在用户位置加载,但不会随其一起移动,用户位置图标会不断更新,并且在按下时中心按钮会使地图在用户位置居中。
希望这个漫长的问题和答案会帮助其他刚开始的人。 干杯。