Как предоставить данные из DataProvider с начальным состоянием, используя шаблон bloc / Repository / DataProvider? - PullRequest
0 голосов
/ 29 февраля 2020

Я начинаю с Flutter и паттерна BLoC / Repository.

На базовом c уровне Blo c паттерн довольно легко gr asp: пользовательский интерфейс отправляет события в Blo c, Blo c вызывает метод Repository, который возвращает значения, Blo c отправляет новое состояние с обновленным значением, соответствующий интерфейс пользователя перерисовывается. Вы предоставляете начальное состояние со значениями. Пока все хорошо.

Но какие начальные значения вы предоставляете, когда получаете асинхронные данные из API?

В моем случае, Данные - это координаты для пользовательской позиции, необходимой для рисования карты в положение пользователя при его загрузке и размещение маркера на месте пользователя. Но я получаю нулевое значение из начальных значений состояния.

Когда я следую учебному пособию по входу в Flatter Blo c Firebase, я увидел, что при создании экземпляра blo c в void main() s runApp() они отправка события из него ..

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(

Как мне получить первые координаты из API? Как всегда, большое спасибо за ваше время и помощь.

Это мои занятия:

Репозиторий:

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() {}
}

BLo C:

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}';
}

Событие:

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),
                              ),
                            ),
                          ),
                        ],
                      ),
                    ],
                  ),
                ),
              ),
            );
          }),
    );
  }
}

Main:

void main() {
  BlocSupervisor.delegate = SimpleBlocDelegate();
  WidgetsFlutterBinding.ensureInitialized();
  BlocSupervisor.delegate = SimpleBlocDelegate();
  final UserRepository userRepository = UserRepository();
  final MapRepository mapRepository = MapRepository();
//  final AlertRepository alertRepository = AlertRepository();
  runApp(
    BlocProvider<AuthenticationBloc>(
        create: (context) {
          return AuthenticationBloc(
            userRepository: UserRepository(),
          )..add(AppStarted());
        },
        child: Fixit(
          userRepository: userRepository,
          mapRepository: mapRepository,
//            alertRepository: alertRepository
        )),
  );
}

class Fixit extends StatelessWidget {
  final UserRepository _userRepository;
  final MapRepository _mapRepository;

  Fixit(
      {Key key,
      @required UserRepository userRepository,
      @required MapRepository mapRepository}) //,
//      @required AlertRepository alertRepository})
      : assert(userRepository != null),
        _userRepository = userRepository,
        _mapRepository = mapRepository,
        super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
        builder: (context, state) {
          if (state is Unauthenticated) {
            return LoginScreen(userRepository: _userRepository);
          }
          if (state is Authenticated) {
            return MapScreen(
                mapRepository: _mapRepository, name: state.displayName);
          }
          if (state is Unauthenticated) {
            return LoginScreen(userRepository: _userRepository);
          }
          return SplashScreen();
        },
      ),
    );
  }
}
...