我首先介绍Flutter和BLoC /存储库模式。
在基本级别上,Bloc模式非常容易掌握: UI将事件发送到Bloc,Bloc触发返回值的Repository方法,Bloc发出具有更新后值的新State,相应的UI被重绘。 您为初始状态提供值。 到目前为止一切顺利。
但是,当您从API获取异步数据时,您会提供什么初始值?
在我的情况下,数据是在加载地图时在用户位置绘制地图并将标记放置在用户位置所需的用户位置坐标。但是我从初始状态值中得到了空值。
当我遵循Flutter Bloc Firebase登录教程时,我看到在void main()
的{{1}}中实例化一个Bloc时,他们正在从中发送事件..
runApp()
我试图重复这种想法,我应该从API获取第一个数据,但它不起作用。坐标为空。
runApp(
BlocProvider<AuthenticationBloc>(
create: (context) {
return AuthenticationBloc(
userRepository: UserRepository(),
)..add(AppStarted());
},
child: Fixit(
userRepository: userRepository,
mapRepository: mapRepository,
// alertRepository: alertRepository
)),
);
}
然后如何从API获取第一个坐标? 一如既往,非常感谢您的时间和帮助。
这些是我的课程:
存储库:
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
// BlocProvider<AlertBloc>(
// create: (context) {
// return AlertBloc(alertRepository: FirebaseAlertRepository())
// ..add(LoadAlerts());
// },
// ),
BlocProvider<MapBloc>(create: (context) {
return MapBloc(mapRepository: MapRepository())
..add(GetLocationStream());
// ..add(CenterMap());
}),
],
child: Scaffold(
BLoC:
class MapRepository {
bool isTracking = false;
final locationManager = Geolocator();
StreamSubscription _positionStreamSubsciption;
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()}');
}
}
LatLng getLocationStream() {
print('getLocationStream() called');
LatLng location;
LocationOptions locationOptions = LocationOptions(
accuracy: LocationAccuracy.bestForNavigation, distanceFilter: 0);
try {
if (isTracking == true) {
_positionStreamSubsciption.cancel();
isTracking = !isTracking;
} else {
_positionStreamSubsciption = locationManager
.getPositionStream(locationOptions)
.listen((Position position) {
if (position != null) {
location = LatLng(position.latitude, position.longitude);
}
isTracking = !isTracking;
print('getLocationStream() location is : $location');
return location;
});
}
} catch (error) {
print('startTracking error: $error');
}
}
void getDirections() {}
}
状态:
class MapBloc extends Bloc<MapEvent, MapState> {
final MapRepository _mapRepository;
// LatLng location;
LatLng locationStream;
StreamSubscription _locationStreamSubscription;
MapBloc({@required MapRepository mapRepository})
: assert(mapRepository != null), // || streamSubscription != null),
_mapRepository = mapRepository; //,
// _locationStreamSubscription = streamSubscription;
MapState get initialState => LocationStream(locationStream);
@override
Stream<MapState> mapEventToState(MapEvent event) async* {
// center map location
// if (event is CenterMap) {
// yield* _mapCenterMapToState(event);
// }
// if (event is LocationUpdated) {
// yield MapCentered(event.updatedLocation,event.);
// }
// user location
if (event is GetLocationStream) {
yield* _mapGetLocationStreamToState(event);
}
// if (event is UpdateLocation) {
// yield LocationStream(event.location);
// }
}
// Stream<MapState> _mapCenterMapToState(CenterMap event) async* {
// _mapRepository.getLocation();
//// (location) => add(LocationUpdated(location));
// // CANT GET LOCATION TO PASS IN MapLoaded()
// print('_mapCenterMapToState location is : ${_mapRepository.getLocation()}');
// yield MapCentered(location, locationStream);
// }
Stream<MapState> _mapGetLocationStreamToState(
GetLocationStream event) async* {
locationStream = LatLng(_mapRepository.getLocationStream().latitude,
_mapRepository.getLocationStream().longitude);
// _mapRepository.getLocationStream().;
// (location) => add(UpdateLocation(location));
// add(UpdateLocation(locationStream));
print('_mapGetLocationStreamToState() locationStream is: $locationStream ');
yield LocationStream(locationStream);
}
}
事件:
abstract class MapState {
const MapState();
@override
List<Object> get props => [];
}
class MapLoading extends MapState {}
class LocationStream extends MapState {
final LatLng location;
const LocationStream(this.location);
@override
List<Object> get props => [location];
@override
String toString() => 'LocationStream {location: $location}';
}
UI:
abstract class MapEvent {
const MapEvent();
@override
List<Object> get props => [];
}
class GetLocationStream extends MapEvent {}
主要:
class MapScreen extends StatelessWidget {
final String name;
final MapRepository _mapRepository;
final MapController _mapController;
MapScreen(
{Key key, @required this.name, @required MapRepository mapRepository})
: assert(mapRepository != null),
_mapRepository = mapRepository,
_mapController = MapController(),
super(key: key);
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
// BlocProvider<AlertBloc>(
// create: (context) {
// return AlertBloc(alertRepository: FirebaseAlertRepository())
// ..add(LoadAlerts());
// },
// ),
BlocProvider<MapBloc>(create: (context) {
return MapBloc(mapRepository: MapRepository());
// ..add(GetLocationStream());
// ..add(CenterMap());
}),
],
child: BlocBuilder<MapBloc, MapState>(
bloc: MapBloc(mapRepository: _mapRepository),
builder: (BuildContext context, MapState state) {
LatLng userLocation = (state as LocationStream).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, //LatLng(_position.latitude, _position.longitude), //
minZoom: 10.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');
},
),
),
],
),
],
),
),
SizedBox(
height: 10,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5)),
onPressed: () {
_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),
),
),
),
],
),
],
),
),
),
);
}),
);
}
}