Flutter - FutureBuilder works well on Android but not well on iOS

Viewed 96

I implemented the FutureBuilder with the code below in order to get the distance from the user and the item that he wants buy but I get a weird result between Android and iOS.

On Android works well and I get the distance for each item. But on iOS I don't have the distance for each item and infact some item has the distance and some items get null value.

enter image description here

class ProductHorizontalListItem extends StatelessWidget {
  const ProductHorizontalListItem({
    Key? key,
    required this.product,
    required this.coreTagKey,
    this.onTap,
  }) : super(key: key);

  final Product product;
  final Function? onTap;
  final String coreTagKey;

  @override
  Widget build(BuildContext context) {
    final PsValueHolder valueHolder =
    Provider.of<PsValueHolder>(context, listen: false);

    Future<double> getCurrentLocation() async {
          Position position = await Geolocator.getCurrentPosition();
          double lat = position.latitude;
          double long = position.longitude;
          final double distanceInMeters = Geolocator.distanceBetween(
            double.parse(position.latitude.toString()),
            double.parse(position.longitude.toString()),
            double.parse(product.itemLocation!.lat.toString()),
            double.parse(product.itemLocation!.lng.toString()),
          );

            return Future.value(distanceInMeters);

        }

        return FutureBuilder<double>(
                future: getCurrentLocation(),
            builder: (BuildContext context, AsyncSnapshot<double> snapshot) {
        return InkWell(
        onTap: onTap as void Function()?,
        child: Container(
            margin: const EdgeInsets.only(
                left: PsDimens.space4, right: PsDimens.space4,
                bottom: PsDimens.space12),
            child: Text(
                '${snapshot.data}',
                textAlign: TextAlign.start,
                style: Theme.of(context).textTheme.caption!.copyWith(
                    color: PsColors.textColor3
                )))

        );});
  }
}

I also tried to handle the states of FutureBuilder in each way but nothing.

iOS works bad, why?

I'm on Flutter 3.0.5 with Android Studio Chipmunk.


UPDATE CODE WITH STATE MANAGEMENT

class ProductHorizontalListItem extends StatelessWidget {
  const ProductHorizontalListItem({
    Key? key,
    required this.product,
    required this.coreTagKey,
    this.onTap,
  }) : super(key: key);

  final Product product;
  final Function? onTap;
  final String coreTagKey;

  @override
  Widget build(BuildContext context) {
    final PsValueHolder valueHolder =
    Provider.of<PsValueHolder>(context, listen: false);

    Future<double> getCurrentLocation() async {
          Position position = await Geolocator.getCurrentPosition();
          double lat = position.latitude;
          double long = position.longitude;
          final double distanceInMeters = Geolocator.distanceBetween(
            double.parse(position.latitude.toString()),
            double.parse(position.longitude.toString()),
            double.parse(product.itemLocation!.lat.toString()),
            double.parse(product.itemLocation!.lng.toString()),
          );

            return Future.value(distanceInMeters);

        }

        return FutureBuilder<double>(
                future: getCurrentLocation(),
            builder: (BuildContext context, AsyncSnapshot<double> snapshot) {
            print(snapshot);
        if (snapshot.connectionState==ConnectionState.waiting) {
           return const Text('Loading...');
        }
        if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
            return InkWell(
            onTap: onTap as void Function()?,
            child: Container(
            margin: const EdgeInsets.only(
                left: PsDimens.space4, right: PsDimens.space4,
                bottom: PsDimens.space12),
            child: Text(
                '${snapshot.data}',
                textAlign: TextAlign.start,
                style: Theme.of(context).textTheme.caption!.copyWith(
                    color: PsColors.textColor3
                )))

        );}
        if (snapshot.hasError) {
            return const Text('Error');
                                  }
            return Container();
          }
        });
  }
}

With state management, instead of null some item are stuck on "Loading..."

1 Answers

First, you should check the snapshot state before accessing its data. Otherwise you can get a null value, since the treatment has not been finished yet. Check snapshot.connectionState and snapshot.hasData before accessing snapshot.data.

Then, there is no need to convert latitudes and longitudes to String, then back to double.

Eventually, you can replace the definition of final Function? onTap; by a VoidCallback, to avoid parsing it in the Inkwell button.

Try this out:

@override
Widget build(BuildContext context) {
  Future<double> getCurrentLocation() async {
    Position position = await Geolocator.getCurrentPosition();
    final double distanceInMeters = Geolocator.distanceBetween(
      position.latitude,
      position.longitude,
      product.itemLocation!.latitude,
      product.itemLocation!.longitude,
    );
    return Future.value(distanceInMeters);
  }

  return FutureBuilder<double>(
    future: getCurrentLocation(),
    builder: (BuildContext context, AsyncSnapshot<double> snapshot) {
      if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
        return InkWell(
          onTap: onTap,
          child: Container(
            margin: const EdgeInsets.only(
              left: PsDimens.space4,
              right: PsDimens.space4,
              bottom: PsDimens.space12,
            ),
            child: Text(
              '${snapshot.data}',
              textAlign: TextAlign.start,
              style: Theme.of(context).textTheme.caption!.copyWith(
                    color: PsColors.textColor3,
                  ),
            ),
          ),
        );
      } else if (!snapshot.hasData) {
        // Handle error case
        return const Text('error');
      } else {
        // Display a loader or whatever to wait for the Future to complete
        return const Center(child: CircularProgressIndicator());
      }
    },
  );
}

I tried the code by replacing position and product.itemLocation by location of actual cities, and the distanceInMeters is correct.

Related