I'm new to Flutter and Dart, and I'm trying to build a Flutter app which displays the device information on the screen. For this purpose I'm trying to use this library: 'device_info' from here: https://pub.dartlang.org/packages/device_info#-readme-tab-
In the 'build' method of the MyApp class, I am trying to instantiate the object from 'device_info' package and call a property which happens to be an async property. Since the default build method is not asynchronous, how do I call this property in the build method? Following is my code:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
AndroidDeviceInfo androidDeviceInfo = await deviceInfoPlugin.androidInfo;
return MaterialApp(
title: 'My Device Info',
home: Scaffold(
appBar: AppBar(
title: Text('My Device Info'),
),
body: Center(
child: Text('Device model:' + 'Moto'),
),
),
);
}
}
Why is the build method on State, and not StatefulWidget? Putting a Widget build(BuildContext context) method on State rather than putting a Widget build(BuildContext context, State state) method on StatefulWidget gives developers more flexibility when subclassing StatefulWidget.
The call to the async method starts an asynchronous task. However, because no Await operator is applied, the program continues without waiting for the task to complete. In most cases, that behavior isn't expected.
To call a function of a parent, you can use the callback pattern. In this example, a function on the color selected is passed to the child. The child calls the function when a button is pressed: import 'package:flutter/material.
In Flutter this can be done using a stateful widget and calling your code in the initState function. What if you want to call it from a stateless widget? Well, that's possible too. Use a stateful widget as a your root widget that you can provide a callback function too to execute your startup logic.
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 Future
s, 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;
}
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.
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With