Home > Enterprise >  Flutter - Calling an async function within an independent class
Flutter - Calling an async function within an independent class

Time:05-18

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

CodePudding user response:

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);
  }

CodePudding user response:

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?

CodePudding user response:

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.

  • Related