How to display a loading spinner while getting the current location in Flutter?
Asked Answered
D

3

10

I'm working on a Flutter app which fetches the user's current location, but while the location is being fetched, it shows this ugly red error screen (which disappears once the location is fetched).

Instead of this, I'd like to display a loading spinner or splash screen. I've narrowed the problem down to this method that is called during initState():

void _setCurrentLocation() {
  Geolocator().getCurrentPosition().then((currLoc) {
    setState(() {
      currentLocation = currLoc;
    });
  });
}

The entire source file can also be found here on GitHub.

Thanks in advance!

Daughterinlaw answered 22/10, 2019 at 11:57 Comment(0)
S
13

Use FutureBuilder Widget

call your _setCurrentLocation method inside initState method and assign it to one variable like getLoc.

Future<Position> getLoc;

@override
void initState() {
// TODO: implement initState
getLoc = _setCurrentLocation();
super.initState();
}

Change your method with return statement.

Future<Position> _setCurrentLocation() async {
var Location = await Geolocator().getCurrentPosition();
return Location;
}

Put all your design code inside futurebuilder widget

@override
Widget build(BuildContext context) {
return FutureBuilder(
    future: getLoc,
    builder: (context, data) {
      if (data.hasData) {
        return Text(data.data.toString());
      } else {
        return Center(child: CircularProgressIndicator());
      }
    });
}
Seibel answered 22/10, 2019 at 12:17 Comment(2)
I tried this, however it does not work. The red screen seems to have gone but the CircularProgressIndicator() is displayed infinitely.Daughterinlaw
@UrmilShroff check my updated answer it's working now.Seibel
G
2

The simplest way is to use conditional rendering. currentLocation will be null until _setCurrentLocation set it to a value.

class LocationDisplayPage extends StatefulWidget {
  @override
  _LocationDisplayPageState createState() => _LocationDisplayPageState();
}

class _LocationDisplayPageState extends State<LocationDisplayPage> {
  Position? _currentLocation;

  void _setCurrentLocation() {
    Geolocator().getCurrentPosition().then((currLoc) {
      setState(() {
        _currentLocation = currLoc;
      });
    });
  }

  @override
  void initState() {
    super.initState();
    _setCurrentLocation();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: _currentLocation == null
            ? CircularProgressIndicator()
            : WidgetToRenderLocation(),
      ),
    );
  }
}
Ginsburg answered 22/10, 2020 at 13:54 Comment(3)
I'm sure FutureBuilder is the "right" way in most situations, but I like this answer because the machinery is out in the open to see. The ".then" callback hitting setState which re-evaluates the conditional. With this conditional rendering approach it's clearer how you extend or modify the logic for various situations that can't be handled by FutureBuilder.Ripe
@ChrisNadovich From my experience, this is almost always the better approach, since you probably need to use the data in other places, such as some business logic, or to render another widget. Saving it in the state can avoid multiple asynchronous calls to fetch the same data, which can make things quite messy.Ginsburg
Well, with my "lack of experience" I can even see your experience is valid. :) As you say, there was more going on in my application and it wasn't obvious how to handle it with FutureBuilder, but a simple conditional in the build and judicious calls to setState was very simple for me to use.Ripe
T
-1

usa il widget Visibility()

bool _isLoading = false;

void _setCurrentLocation() {
  _isLoading = true;

  Geolocator().getCurrentPosition().then((currLoc) {
    setState(() {
      _isLoading = false;
      currentLocation = currLoc;
    });
  });
}

 return Scaffold(
      key: scaffoldKey,
      body: Container(
        child: Visibility(
         visible: _isLoading,
          child: Stack(
           children: <Widget>[
             GoogleMap( ...

          replacement: Container(
             child: CircularProgressIndicator(),
           ),

Tighten answered 22/10, 2019 at 12:26 Comment(1)
Implemented this but does not seem to be working. The red screen is still displayed followed by either the GoogleMap or CircularProgressIndicator(), depending on how I define the booleans.Daughterinlaw

© 2022 - 2024 — McMap. All rights reserved.