Я объявил функцию в своем main.dart, которая поможет мне получить deviceId
.
Я пытаюсь установить это в переменную независимого класса, конструктор которого позже создается в одном из моих виджетов.
Функция:
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
}
}
Вызов его в классе как:
class _Data {
String? fullname = '';
String doctor = 's';
String? deviceId = await _getId();
}
Я получаю ошибку ниже:
The await expression can only be used in an async function.
Полный код:
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
),
),
),
)
],
),
)
),
);
}
}
Он отображает правильное значение для полного имени при нажатии кнопки «Отправить», но выдает ошибку для вызова идентификатора устройства.
Ключевое слово Ждите может использоваться только с функцией асинхронный.
только с функцией, но здесь вы вызвали await _getId()
внутри класса, а не внутри функции.
попробуй это.
@override
void initState() {
// TODO: implement initState
super.initState();
_getId().then((value) {
_data.deviceId = value;
});
//print(_data.deviceId);
}
Я обновил свой ответ. проверить его, но все еще не знаю, что вы ищете.
какое идеальное место для хранения этого? Я поместил его в RegisterPageState
Метод initState объявлен только внутри класса State. Так что RegisterPageState в порядке.
что, если я хочу быть доступным в своем StatefulWidget? Потому что позже я буду создавать разные состояния в зависимости от условий.
Я не носитель английского языка. Итак, не могли бы вы пояснить приведенную выше фразу.
Есть ли способ вызвать этот код внутри класса class RegisterPage extends StatefulWidget
. Я хочу создать свое состояние на основе значения из deviceID
Давайте продолжить обсуждение в чате.
Вы хотите инициализировать deviceId
из результата асинхронной операции. Некоторые варианты:
Объявите Future<String?> deviceId = _getId();
и потребуйте от всех вызывающих абонентов await
при доступе к нему.
Инициализируйте его позже:
class _Data {
String? deviceId;
_Data() {
_getId().then((id) => deviceId = id);
}
}
Обратите внимание, что при таком подходе вызывающие абоненты не будут уведомлены, когда deviceId
в конечном итоге инициализируется.
Заставьте вызывающие стороны инициализировать ваш класс асинхронно:
class _Data {
String? deviceId;
_Data._(this.deviceId);
static Future<_Data> newData() async {
var id = await _getId();
return _Data._(id);
}
}
Отчасти связано: Как ждать будущего в глобальном главном дротике флаттера?
Мне нравится второй способ вызова _getId() с помощью конструктора _Data
. Будет ли _data.deviceId
установлен, как только будет создан экземпляр объекта класса _Data
?
@MohitC _data.deviceId
будет немедленно инициализирован в null
. Поскольку желаемое значение получается асинхронно, оно не будет установлено на это значение до неопределенного времени в будущем. И, как я уже сказал, вызывающие абоненты не смогут узнать, когда он в конечном итоге будет установлен. Это не тот подход, который подходит для всех случаев.
что делает линия _Data._(this.deviceId);
?
@MohitC Это именованный конструктор (чье имя _
, что делает его закрытым). См. stackoverflow.com/a/57816373.
Второй подход, вероятно, является наихудшим подходом, поскольку он блокирует вас наличием обнуляемого идентификатора устройства, а также вашей модели данных, которая не инициализируется после выполнения конструктора, а также уже перечисленной ловушкой отсутствия уведомления, когда она выполнена инициализация.
Многие вещи, которые вы делаете, будут считаться плохой практикой. Выложу несколько изменений и наконец отвечу на ваш вопрос. Определите свой класс _Data следующим образом:
class _Data {
String? fullname;
String doctor;
String? deviceId;
_Data(this.fullname, this.doctor, this.deviceId);
}
Таким образом, ваша модель данных не жестко запрограммирована. Вы также можете сделать поля окончательными, если у вас нет веских причин этого не делать.
Объявите переменную состояния данных:
_Data? _data;
Определите метод initData внутри вашего состояния:
Future<void> initData() async {
var dat = _Data("fn", "doc", await _getId());
setState(() {_data = dat; });
}
Наконец переопределить initState:
@override
void initState() {
super.initState();
initData();
}
Внутри вашего метода сборки вы можете обратить внимание на то, является ли поле нулевым (проверив, было ли оно инициализировано). Здесь есть много возможностей для улучшения, как только вы запустите его. Во-первых, вы не должны делать метод getId верхнего уровня, было бы лучше иметь его внутри службы, и вам следует рассмотреть возможность использования будущего компоновщика вместо того, чтобы атрибут состояния _data обнулялся.
спасибо за предложения, я буду следовать, все еще очень новичок в флаттере. Что, если я хочу, чтобы изначально был сам deviceId, и на основе значения deviceId я хочу создать разные состояния. Можно ли вызвать этот код в классе виджетов Stateful?
Вы можете получить deviceId аналогично получению данных в моем примере. Вы можете поместить свою логику, которая зависит от deviceId, внутри функции initData. Обратите внимание, что deviceId является постоянным для одного устройства, и невозможно предсказать его значение для случайного устройства. Кроме того, да, этот код может (и должен быть) помещен в состояние вашего StatefulWidget, а не в StatefulWidget, обратите внимание на различие.
Я пытаюсь получить идентификатор устройства и проверю, зарегистрирован ли этот идентификатор устройства на моем сервере или нет через REST API. Если он зарегистрирован, я хочу создать другое состояние. Если он не зарегистрирован, я хочу создать состояние отображаемого виджета (страница регистрации). Какие-либо предложения?
Сначала я бы выделил это в блок или службу. Учитывая, что вы хотите сделать довольно сложную логику инициализации, второй подход jamesdlin, который вы упомянули больше всего, будет для вас худшим из возможных, поскольку конструктор модели данных - ужасное место для такой логики. Его третий подход также является плохой практикой, если вы добавите эту логику, поскольку модель не должна также заниматься выполнением HTTP-запросов. Вы можете дождаться ответа сервера внутри метода initData и поместить туда свою логику, а потом выделить ее в блок или сервис. Попробуйте задать новый вопрос.
Да, я это ясно вижу. Каково решение? Как мне присвоить его значение одной из переменных класса?