#flutter #flutter-alertdialog
#флаттер #flutter-alertdialog
Вопрос:
Я добавил всплывающее окно (alertdialog) в свой проект flutter, в котором есть streambuilder. Сначала это не сработало, но после создания его асинхронным и добавления кода, подобного приведенному ниже
await Future.delayed(Duration(milliseconds: 50));
Он начал работать нормально. Это всплывающее окно будет возникать после определенных изменений данных в базе данных. Позже я захотел добавить в свой проект второе всплывающее окно для второго значения базы данных, разница в том, что у этого есть текстовое поле для ввода пользователем и, конечно, контроллер для этого. Когда я попробовал это всплывающее окно, оно работало нормально, но в конце всплывающего окна оно выдавало
type 'Future<dynamic>' is not a subtype of type 'Widget'
ошибка с красным / желтым фоном ошибки. Разница между этими 2 всплывающими окнами заключается в том, что, как я уже сказал, у одного из них есть контроллер для ввода, что я здесь делаю не так?
Вот полный код:
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:assets_audio_player/assets_audio_player.dart';
import 'package:flutter/scheduler.dart';
import 'numbers.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:sayi_avi/homescreen.dart';
Numbers myNumbers = Numbers();
void main(){
runApp(
GameScreen()
);
}
class GameScreen extends StatefulWidget {
static String id ='gamescreen';
@override
_GameScreenState createState() => _GameScreenState();
}
class _GameScreenState extends State<GameScreen> {
bool _initialized = false;
bool _error = false;
TextEditingController _controller;
void initializeFlutterFire() async {
try {
// Wait for Firebase to initialize and set `_initialized` state to true
await Firebase.initializeApp();
setState(() {
_initialized = true;
});
} catch(e) {
// Set `_error` state to true if Firebase initialization fails
setState(() {
_error = true;
});
}
}
@override
void initState() {
initializeFlutterFire();
super.initState();
getCurrentUser();
_controller = TextEditingController();
}
void dispose() {
_controller.dispose();
super.dispose();
}
final _auth =FirebaseAuth.instance;
User loggedInUser;
final _firestore = FirebaseFirestore.instance;
final String collectionPath = 'users';
String docPath;
var userPath;
DocumentReference userdoc;
var userSnapshot;
String gameResu<
String sendednumber='';
List<dynamic> kullanicisayilari = [];
List<dynamic> rakipsayilari = [];
List<dynamic> sonuc = [];
void getCurrentUser() async{
try{
final user = await _auth.currentUser;
if(user !=null){
loggedInUser =user;
docPath = loggedInUser.uid;
userPath = _firestore.collection(collectionPath);
userdoc = userPath.doc(docPath);
userSnapshot = userdoc.snapshots();
}
}catch(e){
print(e);
}
}
Expanded attachNumber(number,imagenumber){
return Expanded(
child:FlatButton(
onPressed: (){
setState(() {
if(!myNumbers.numberStatus[1]){
myNumbers.buttonValues['numberimage1'] = imagenumber;
myNumbers.numberStatus[1] =true;
myNumbers.decimals[1]=number;
}else if(!myNumbers.numberStatus[2]){
myNumbers.buttonValues['numberimage2'] = imagenumber;
myNumbers.numberStatus[2] =true;
myNumbers.decimals[2]=number;
}else if(!myNumbers.numberStatus[3]){
myNumbers.buttonValues['numberimage3'] = imagenumber;
myNumbers.numberStatus[3] =true;
myNumbers.decimals[3]=number;
}else if(!myNumbers.numberStatus[4]){
myNumbers.buttonValues['numberimage4'] = imagenumber;
myNumbers.numberStatus[4] =true;
myNumbers.decimals[4]=number;
}
});
final assetsAudioPlayer = AssetsAudioPlayer();
assetsAudioPlayer.open(
Audio("assets/audios/click.wav"),
);
},
padding: EdgeInsets.all(0),
child: Image.asset('images/$imagenumber'),
),
);
}
Expanded showDeleteNumbers(statusNumber,number){
return Expanded(
child:FlatButton(
onPressed: (){
setState(() {
myNumbers.decimals[statusNumber]='';
myNumbers.numberStatus[statusNumber] =false;
myNumbers.buttonValues[number] = 'nonumber.png';
});
},
child: Image.asset('images/' myNumbers.buttonValues['$number']),
),
);
}
Future<void> sendnumber() {
sendednumber="";
for (var numbers in myNumbers.decimals.values){
sendednumber = sendednumber numbers;
}
Random rnd;
int min = 10000;
int max = 100000;
rnd = new Random();
var r = min rnd.nextInt(max - min);
kullanicisayilari.add(sendednumber "|" r.toString());
return userPath
.doc(docPath)
.update({'atilansayi': kullanicisayilari})
.then((value) => print("User Updated"))
.catchError((error) => print("Failed to update user: $error"));
}
/*
List<Widget> getUserNumbers(){
return
}
*/
Text getUserNumbers(kullanicisayilari){
for(var number in kullanicisayilari){
return Text(number);
};
}
//This one is working fine
_showMaterialDialog(String type) async{
if(type=="win"){
gameResult = "You Win, Gratz!";
}else if(type=="lose"){
gameResult = "You Lose :(";
}
print("buraya girdi");
print(gameResult);
await Future.delayed(Duration(milliseconds: 50));
showDialog (
context: context,
builder: (_) => AlertDialog(
title: Text("Result"),
content: Text(gameResult),
actions: <Widget>[
FlatButton(
child: Text('Close'),
onPressed: () {
Navigator.pushNamed(context, HomeScreen.id);
},
)
],
));
}
//This one is causing errors
_showMaterialDialogNumber() async{
await Future.delayed(Duration(milliseconds: 100));
showDialog (
context: context,
builder: (_) => AlertDialog(
title: Text("Start"),
content: TextField(
controller: _controller,
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Enter your Number',
),
),
actions: <Widget>[
FlatButton(
child: Text('Submit'),
onPressed: () {
Navigator.of(context).pop();
},
)
],
));
}
/*
*/
@override
Widget build(BuildContext context) {
if(_error) {
return Text('error-game', textDirection: TextDirection.ltr);
}
// Show a loader until FlutterFire is initialized
if (!_initialized) {
return Text('Loading', textDirection: TextDirection.ltr);
}
return StreamBuilder<DocumentSnapshot>(
stream: userSnapshot,
builder: (BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
if(snapshot.hasData){
Map<String, dynamic> userDocument = snapshot.data.data();
print(collectionPath);
print(docPath);
print(snapshot.data);
print(userDocument);
gameResult = userDocument['status'];
//This one works fine
if(gameResult =="win" || gameResult =="lose"){
return _showMaterialDialog(gameResult);
}
//This one causing errors
if(gameResult=="on"){
return _showMaterialDialogNumber();
}
kullanicisayilari = userDocument['atilansayi'];
List<dynamic> kullanicisayilariDuz = [];
List<dynamic> rakipsayilariDuz = [];
List<dynamic> sonuclarDuz = [];
for (var numbers in kullanicisayilari){
var splittedNumber = numbers.split('|');
kullanicisayilariDuz.add(splittedNumber[0]);
}
rakipsayilari = userDocument['rakipsallama'];
sonuc = userDocument['sonuc'];
for (var sonuclar in sonuc){
var splittedSonuc = sonuclar.split('|');
sonuclarDuz.add(splittedSonuc[0]);
}
for (var rakipsayi in rakipsayilari){
var splittedRakipSayi = rakipsayi.split('|');
rakipsayilariDuz.add(splittedRakipSayi[0]);
}
print(myNumbers.decimals);
return MaterialApp(
home:Scaffold(
appBar: AppBar(
backgroundColor: Colors.amberAccent,
title: Text('Sayı Avı Oyun Ekranı'),
),
body:Column(
children: <Widget>[
Expanded(
flex: 80,
child: Row(
children: <Widget>[
Expanded(
flex: 40,
child: Column(
children: <Widget>[
for(var numbers in kullanicisayilariDuz)Text(numbers),
]
),
),
Expanded(
flex: 10,
child: Column(
children: <Widget>[
for(var numbers in sonuclarDuz)Text(numbers),
]
),
),
Expanded(
flex: 50,
child: Column(
children: <Widget>[
for(var numbers in rakipsayilariDuz)Text(numbers),
]
),
),
],
),
),
Expanded(
flex:10,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
showDeleteNumbers(1,'numberimage1'),
showDeleteNumbers(2,'numberimage2'),
showDeleteNumbers(3,'numberimage3'),
showDeleteNumbers(4,'numberimage4'),
Expanded(
child:FlatButton(
onPressed: (){
sendnumber();
},
child: Image.asset('images/send.png'),
),
),
],
),
),
Expanded(
flex: 10,
child: Row(
children: <Widget>[
attachNumber('1','one.png'),
attachNumber('2','two.png'),
attachNumber('3','three.png'),
attachNumber('4','four.png'),
attachNumber('5','five.png'),
attachNumber('6','six.png'),
attachNumber('7','seven.png'),
attachNumber('8','eight.png'),
attachNumber('9','nine.png'),
attachNumber('0','zero.png'),
],
),
),
],
),
),
);
}
},
);
}
}
Заранее спасибо.
Ответ №1:
build
Ваша функция GameScreen
должна возвращать Widget
:
Widget build(...) {}
Однако при отображении диалоговых окон вы:
return _showMaterialDialog();
Эта диалоговая функция возвращает a Future<>
, который не может быть виджетом. Это объясняет ошибку.
Я бы предпочел объявить их явно, и они должны возвращать диалоги внутри асинхронной функции следующим образом:
Future _showMaterialDialog() async {
...
return showDialog(...);
}
Кстати, использование того же контекста, передаваемого в параметр функции, должно быть лучше:
Future _showMaterialDialog(BuildContext context) {
// use the local 'context' to build the dialog
}
Наконец, чтобы правильно использовать эти диалоговые окна, просто покажите их и верните, наконец, виджет:
if (...) {
_showMaterialDialog(context);
}
return MaterialApp(...);
И вам не нужны те два delayed
, которые вы добавили, если вы разрешите отображать время пользовательского интерфейса.
Действительно, из-за использования a StreamBuilder
пользовательский интерфейс еще не отображается, вам нужно дождаться, когда основной конвейер рендеринга будет сброшен с помощью addPostFrameCallback
:
WidgetsBinding.instance.addPostFrameCallback((_) {
_showMaterialDialog(context);
}
PS: помните, что Flutter — это все виджеты, подумайте о том, чтобы преобразовать ваш код в небольшие виджеты, чтобы не делать много вещей только в одном классе.
Комментарии:
1. Спасибо за ответ, это сработало, но устранение двух задержек, вызывающих проблемы. Я не знаю почему.
2. @Faruk Я думаю, это из-за рендеринга StreamBuilder. Я отредактировал свой ответ, чтобы включить эту часть, вы должны попробовать и дать мне знать.