getDataInArea<T> function

Stream<List<T>> getDataInArea<T>({
  1. required Area area,
  2. required Query source,
  3. required DocumentMapper<T> mapper,
  4. required String locationFieldNameInDB,
  5. LocationAccessor<T>? locationAccessor,
  6. List<ItemFilter<T>>? clientSidefilters,
  7. DistanceMapper<T>? distanceMapper,
  8. DistanceAccessor<T>? distanceAccessor,
  9. bool sortDecending = false,
  10. List<QueryConstraint>? serverSideConstraints,
  11. List<OrderConstraint>? serverSideOrdering,
})

Provides as Stream of lists of data items of type T that have a location field in a specified area sorted by the distance of to the areas center. area : The area that constraints the query source : The source FireStore document collection mapper : mapping function that gets applied to every document in the query. Typically used to deserialize the Map returned from FireStore locationFieldInDb : The name of the data field in your FireStore document. Need to make the location based search on the server side locationAccessor : As this is a generic function it cannot know where your location is stored in you generic type. optional if you don't use distanceMapper and don't want to sort by distance Therefore pass a function that returns a valur from the location field inside your generic type. distanceMapper : optional mapper that gets the distance to the center of the area passed to give you the chance to save this inside your item if you use a distanceMapper you HAVE to pass locationAccessor clientSideFilters : optional list of filter functions that execute a .where() on the result on the client side distanceAccessor : if you have stored the distance using a distanceMapper passing this accessor function will prevent additional distance computing for sorting. sortDecending : if the resulting list should be sorted descending by the distance to the area's center. If you don't provide loacationAccessor or distanceAccessor no sorting is done. This Sorting is done one the client side serverSideConstraints : If you need some serverside filtering besides the Area pass a list of QueryConstraint serverSideOrdering : If you need some serverside ordering you can pass a List of OrderConstraints Using serverSideConstraints or serverSideOrdering almost always requires to create an index for this field. Check your debug output for a message from FireStore with a link to create them

Implementation

Stream<List<T>> getDataInArea<T>(
    {required Area area,
    required Query source,
    required DocumentMapper<T> mapper,
    required String locationFieldNameInDB,
    LocationAccessor<T>? locationAccessor,
    List<ItemFilter<T>>? clientSidefilters,
    DistanceMapper<T>? distanceMapper,
    DistanceAccessor<T>? distanceAccessor,
    bool sortDecending = false,
    List<QueryConstraint>? serverSideConstraints,
    List<OrderConstraint>? serverSideOrdering}) {
  assert(
    (distanceAccessor == null) || (distanceMapper != null && distanceAccessor != null),
  );

  if (serverSideOrdering != null) {
    serverSideOrdering.insert(0, new OrderConstraint(locationFieldNameInDB, false));
  }

  var constraints = getLocationsConstraint(locationFieldNameInDB, area);
  if (serverSideConstraints != null) {
    constraints.addAll(serverSideConstraints);
  }

  var query = buildQuery(collection: source, constraints: constraints, orderBy: serverSideOrdering);

  // as we replace items ouside the target circle at the corners of the surrounding square with null we have to filter
  // them out on the clients side
  if (clientSidefilters != null) {
    clientSidefilters..insert(0, (item) => item != null);
  } else {
    clientSidefilters = [(item) => item != null];
  }

  return getDataFromQuery<T /*!*/ >(
      query: query,
      mapper: (docSnapshot) {
        // get a real objects from FireStore
        var item = mapper(docSnapshot);
        double distance;
        if (locationAccessor != null) {
          distance = area.distanceToCenter(locationAccessor(item!));
          // We might get places outside the target circle at the corners of the surrounding square
          if (distance > area.radiusInKilometers) {
            return null;
          }
          if (distanceMapper != null) {
            return distanceMapper(item, distance);
          }
        }
        return item;
      },
      clientSidefilters: clientSidefilters,
      orderComparer: distanceAccessor != null // i this case we don't have to calculate the distance again
          ? (item1, item2) => sortDecending
              ? distanceAccessor(item1).compareTo(distanceAccessor(item2))
              : distanceAccessor(item2).compareTo(distanceAccessor(item1))
          : locationAccessor != null
              ? (item1, item2) => sortDecending
                  ? area
                      .distanceToCenter(locationAccessor(item1))
                      .compareTo(area.distanceToCenter(locationAccessor(item2)))
                  : area
                      .distanceToCenter(locationAccessor(item2))
                      .compareTo(area.distanceToCenter(locationAccessor(item1)))
              : null);
}