How do I call async property in Widget build method

Solution 1:

I would suggest you to use a FutureBuilder:

import 'package:flutter/material.dart';

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  // save in the state for caching!
  DeviceInfoPlugin _deviceInfoPlugin;

  @override
  void initState() {
    super.initState();
    _deviceInfoPlugin = DeviceInfoPlugin();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My Device Info',
      home: Scaffold(
        appBar: AppBar(
          title: Text('My Device Info'),
        ),
        body: FutureBuilder<AndroidDeviceInfo>(
          future: _deviceInfoPlugin.androidInfo,
          builder: (BuildContext context, AsyncSnapshot<AndroidDeviceInfo> snapshot) {
            if (!snapshot.hasData) {
              // while data is loading:
              return Center(
                child: CircularProgressIndicator(),
              );
            } else {
              // data loaded:
              final androidDeviceInfo = snapshot.data;
              return Center(
                child: Text('Android version: ${androidDeviceInfo.version}'),
              );
            }
          },
        ),
      ),
    );
  }
}

In general, when using FutureBuilder or Futures, you have to keep in mind that the enclosing widget can be rebuilt at any time (e.g. because the device was rotated, or the keyboard is shown). That means the build method is called again.

In this particular case it's not a problem because the plugin caches the value and returns it instantly, but in general you should NEVER create or get a Future inside of the build method. Instead, do it from initState or a click event handler:

import 'package:flutter/material.dart';

class FooWidget extends StatefulWidget {
  @override
  _FooWidgetState createState() => _FooWidgetState();
}

class _FooWidgetState extends State<FooWidget> {
  Future<int> _bar;

  @override
  void initState() {
    super.initState();
    _bar = doSomeLongRunningCalculation();
  }

  void _retry() {
    setState(() {
      _bar = doSomeLongRunningCalculation();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        FutureBuilder<int>(
          future: _bar,
          builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
            if (snapshot.hasData) {
              return Text('The answer to everything is ${snapshot.data}');
            } else {
              return Text('Calculating answer...');
            }
          },
        ),
        RaisedButton(
          onPressed: _retry,
          child: Text('Retry'),
        )
      ],
    );
  }
}

Future<int> doSomeLongRunningCalculation() async {
  await Future.delayed(Duration(seconds: 5)); // wait 5 sec
  return 42;
}

Solution 2:

build() expects a sync result, so using async/await is inappropriate in build().

Either use FutureBuilder where you return a placeholder Container() while the async result is not yet available or move the async code to initState() and update the state using setState when the value becomes available to have the build be executed again.

Solution 3:

You can achieve it by use of await/async (By @Günter Zöchbauer said not need to import lib as per latest version of dart.)

and call functions out of build method.

  _getAndroidDeviceInfo() async{
    AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
    print(androidInfo.device);
  }

   _get_iOS_DeviceInfo() async{
    IosDeviceInfo iosDeviceInfo = await deviceInfo.iosInfo;
    print(iosDeviceInfo.model);
  }