0

tldr: Trying to call an async Future (network call) method in initState, and based on return value of this method trying to do some other stuff. A check on this value fails because the check is executed before the async method is able to set its value.


I am trying to build an app that will:

1. Get device id when opened.
2. Check with an API if device id is registered. Get corresponding user data if registered.
3. If not registered -> push to registration page
4. If registered -> Simply poll location through a rest API to server

Not sure if its the best way, but with my limited knowledge of flutter I am trying to get all of this done in initState of my Stateful widget.

I have a future method that takes the device id and returns user info. I am trying to validate this user info with a check and take the next step accordingly (whether to send to registration page or start polling location)

Below is the structure of code I am trying:

class HomePage extends StatefulWidget {
  const HomePage ({Key? key}) : super(key: key);
  @override
  State<StatefulWidget> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {

  String deviceId = '';
  var users;

  Future<List> fetchUserInfo(String deviceId) async {
    // returns a list containing user info
  }

  Future<String> sendLocation(String currentLocation, String username) async {
    // sends data to a server
  }

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

    // gets device id from another Future outside class
    getDeviceId().then((value) {
      deviceId = value;
    });

    // Fetch user details from server
    try {
      fetchUserInfo(deviceId).then((value) {
        users = value;
      });
    } catch (e) {
      Fluttertoast.showToast(msg: e.toString());
    }

    if (users.length != 1) {
      // send user to registration page
      Navigator.pushReplacement(
        context,
        MaterialPageRoute(
            builder: (context) => RegisterPage(_data.deviceId)
        )
      );
    } else {
      // Poll location changes
      location.onLocationChanged.listen((LocationData currentLocation) {
        // Construct location string
        // Construct username
        sendLocation(currentLocation, username).then((value) async {
          //...
        });
      });
    }
  } // initState

  Widget _buildWidget() {
    if (!_registered) {
      return Container(
        // return a text widget that says "loading"
      );
    } else {
      return Container(
        // return a text widget that says "Registered. Started polling location"
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('Manager'),
        ),
        body: Center( child: _buildWidget()),
    );
  }
}


Problem is, on check if (users.length != 1) it throws below error

The following NoSuchMethodError was thrown building Builder: The getter 'length' was called on null. Receiver: null Tried calling: length

I understand that its happening because check executes before my async call which gets user info finishes. I cant call it with await since initState is not an async method. Is there any way to wait for execution of call so that user check happens after its value is set?

Any other implementation suggestions are most welcome.

MohitC
  • 3,952
  • 1
  • 29
  • 49

0 Answers0