2

I have declared a function in my main.dart that would help me get the deviceId. I am trying to set this to a variable of an independent class, whose constructor is later created in one of my widget.

Function:

Future<String?> _getId() async {
  var deviceInfo = DeviceInfoPlugin();
  if (Platform.isIOS) { // import 'dart:io'
    var iosDeviceInfo = await deviceInfo.iosInfo;
    return iosDeviceInfo.identifierForVendor; // Unique ID on iOS
  } else {
    var androidDeviceInfo = await deviceInfo.androidInfo;
    return androidDeviceInfo.androidId; // Unique ID on Android
  }
}

Calling it in a class as:

class _Data {
  String? fullname = '';
  String doctor = 's';
  String? deviceId = await _getId();
}

I get below error:

The await expression can only be used in an async function.


Complete code:

import 'dart:io';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:device_info_plus/device_info_plus.dart';

Future<String?> _getId() async {
  var deviceInfo = DeviceInfoPlugin();
  if (Platform.isIOS) { // import 'dart:io'
    var iosDeviceInfo = await deviceInfo.iosInfo;
    return iosDeviceInfo.identifierForVendor; // Unique ID on iOS
  } else {
    var androidDeviceInfo = await deviceInfo.androidInfo;
    return androidDeviceInfo.androidId; // Unique ID on Android
  }
}

void main() => runApp(const MaterialApp(
  title: 'Manager',
  home: RegisterPage(),
));


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

class _Data {
  String? fullname = '';
  String doctor = 's';
  String? deviceId = await _getId();
}

class _RegisterPageState extends State<RegisterPage> {
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  _Data _data = _Data();

  void submit() {
      _formKey.currentState!.save();
      print('Printing the login data.');
      print('Fullname: ${_data.fullname}');
      print('Doctor: ${_data.doctor}');
      print('Device Id: ${_data.getDeviceId()}');
  }

  @override
  Widget build(BuildContext context) {
    final Size screenSize = MediaQuery.of(context).size;

    return Scaffold(
      appBar: AppBar(
        title: const Text('Manager'),
      ),
      body: Container(
          padding: const EdgeInsets.all(20.0),
          child: Form(
            key: _formKey,
            child: ListView(
              children: <Widget>[
                TextFormField(
                    keyboardType: TextInputType.text,
                    decoration: const InputDecoration(
                        hintText: 'John Doe',
                        labelText: 'Full name'
                    ),
                    onSaved: (String? value) {
                      _data.fullname = value;
                    }
                ),
                Container(
                  width: screenSize.width,
                  margin: const EdgeInsets.only(
                      top: 20.0
                  ),
                  child: ElevatedButton(
                    onPressed: submit,
                    child: const Text(
                      'Register',
                      style: TextStyle(
                          color: Colors.white
                      ),
                    ),
                  ),
                )
              ],
            ),
          )
      ),
    );
  }
}

It displays correct value for fullname on clicking submit but gives error for calling device id

3 Answers 3

4

await keyword can only used with async function.

only with function, but here you called await _getId() inside a class not inside a function.

try this.


  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    _getId().then((value) {
      _data.deviceId = value;
    });
    //print(_data.deviceId);
  }
Sign up to request clarification or add additional context in comments.

8 Comments

Yeah i can clearly see that. What is the solution? How do I assign its value to one of class variables?
I have updated my answer. check it out, but still don't know that you are looking for.
what is the ideal place to keep this? I have placed it inside the RegisterPageState
initState method only declared inside of State class. So RegisterPageState is fine.
what if I want to be accessible in my StatefulWidget? Because I would later be creating different states based on conditions
|
1

You want to initialize deviceId from the result of an asynchronous operation. Some options:

  • Declare Future<String?> deviceId = _getId(); and require all callers to await it when accessing it.

  • Initialize it later:

    class _Data {
      String? deviceId;
    
      _Data() {
        _getId().then((id) => deviceId = id);
      }
    }
    

    Note that with this approach, callers will not be notified when deviceId is eventually initialized.

  • Make callers initialize your class asynchronously:

    class _Data {
      String? deviceId;
    
      _Data._(this.deviceId);
    
      static Future<_Data> newData() async {
        var id = await _getId();
        return _Data._(id);
      }
    }
    

Somewhat related: How to await future in global main dart of flutter?

5 Comments

I like the second way of calling _getId() with then inside _Data's constructor. Would _data.deviceId be set as soon as _Data class object is instantiated?
@MohitC _data.deviceId will be initialized to null immediately. Since the desired value is obtained asynchronously, it will not be set to that value until some indeterminate time in the future. And, as I said, there would be no way for callers to know when it eventually does get set. It is not an approach that would be appropriate for all cases.
what does the line _Data._(this.deviceId); do?
@MohitC It's a named constructor (whose name is _, which makes it private). See stackoverflow.com/a/57816373.
The second approach is likely the worst approach as it locks you into having a nullable deviceId as well as your data model being uninitialized after the constructor executes, as well as the already listed pitfall of having no notification when it's done initializing.
1

Many of the things you are doing would be considered bad practice. I will lay out a few changes and finally answer your question. Define your _Data class as follows:

class _Data {
  String? fullname; 
  String doctor; 
  String? deviceId; 
  _Data(this.fullname, this.doctor, this.deviceId);
}

This way your data model is not hardcoded. You may also consider making the fields final unless you have a good reason not to.

Declare the data state variable:

_Data? _data;

Define the initData method inside your state:

  Future<void> initData() async {
    var dat = _Data("fn", "doc", await _getId());
    setState(() {_data = dat; });
  }

Finally override initState:

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

Inside your build method you can pay attention to whether or not the field is null (checking if it has been initialized). There's a bunch of room for improvement here once you get it running. First you should not make the getId method top level, it would be better to have it inside a service, and you should consider using a future builder instead of having the _data state attribute nullable.

5 Comments

thank you for suggestions, I will follow through, still very new to flutter. What if I want the deviceId initially itself, and based on value of deviceId i want to create different states. Can this code be called in Stateful widget class?
You can obtain the deviceId in a similar way as data was obtained in my example. You can put your logic which depends on deviceId inside the initData function. Note that the deviceId is constant for one device and there's no way to predict its value for a random device. Also, yes, this code can (and is meant to be) put inside the state of your StatefulWidget, not in the StatefulWidget, note the distinction.
I am trying to get a device id and will check whether that device id is registered with my server or not via a REST API. If its registered, then I want to create a different state. If its not registered, then I want to create the State of shown widget (registration page). Any suggestions?
I would first separate that into a bloc or a service. Considering you want to do rather complex initialization logic, the second approach of jamesdlin which you mentioned liking the most will be the worst possible for you as the constructor of a data model is a horrible place for such logic. His third approach is also bad practice once you add that logic, as the model should not also be concerned with making http requests. You can await the server's response inside the initData method and place your logic there, and later separate it into the bloc or service. Consider asking a new question.
Asked another question for it stackoverflow.com/questions/72310005/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.