Показать диалоговое окно оповещения при загрузке главного экрана приложения автоматически
Я хочу показать диалоговое окно оповещения на основе условия. Не основано на взаимодействии с пользователем, таком как событие нажатия кнопки.
Если флаг установлен в диалоговом окне предупреждения о состоянии приложения, в противном случае его нет.
Ниже приведен пример диалогового окна оповещения, которое я хочу показать
void _showDialog() {
// flutter defined function
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: new Text("Alert Dialog title"),
content: new Text("Alert Dialog body"),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
Я пытался вызвать этот метод внутри метода сборки виджета на главном экране, но он выдает ошибку -
The context used to push or pop routes from the Navigator must be that of a widget that is a descendant of a Navigator widget.
E/flutter ( 3667): #0 Navigator.of.<anonymous closure> (package:flutter/src/widgets/navigator.dart:1179:9)
E/flutter ( 3667): #1 Navigator.of (package:flutter/src/widgets/navigator.dart:1186:6)
E/flutter ( 3667): #2 showDialog (package:flutter/src/material/dialog.dart:642:20)
Проблема в том, что я не знаю, откуда мне вызывать этот метод _showDialog?
7 ответов
Я бы поместил это в initState
из State
(из StatefulWidget
).
В этом примере ниже, он отображает предупреждение, когда устройство не подключено к Wi-Fi, показывая кнопку [попробуйте еще раз], если это не так.
import 'package:flutter/material.dart';
import 'package:connectivity/connectivity.dart';
void main() => runApp(MaterialApp(title: "Wifi Check", home: MyPage()));
class MyPage extends StatefulWidget {
@override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
bool _tryAgain = false;
@override
void initState() {
super.initState();
_checkWifi();
}
_checkWifi() async {
// the method below returns a Future
var connectivityResult = await (new Connectivity().checkConnectivity());
bool connectedToWifi = (connectivityResult == ConnectivityResult.wifi);
if (!connectedToWifi) {
_showAlert(context);
}
if (_tryAgain != !connectedToWifi) {
setState(() => _tryAgain = !connectedToWifi);
}
}
@override
Widget build(BuildContext context) {
var body = Container(
alignment: Alignment.center,
child: _tryAgain
? RaisedButton(
child: Text("Try again"),
onPressed: () {
_checkWifi();
})
: Text("This device is connected to Wifi"),
);
return Scaffold(
appBar: AppBar(title: Text("Wifi check")),
body: body
);
}
void _showAlert(BuildContext context) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text("Wifi"),
content: Text("Wifi not detected. Please activate it."),
)
);
}
}
Вы должны обернуть содержимое внутри другого Widget
(желательно без гражданства).
Пример:
Меняться от:
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Trial',
home: Scaffold(
appBar: AppBar(title: Text('List scroll')),
body: Container(
child: Text("Hello world"),
)));
}
}
к этому:
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Trial',
home: Scaffold(
appBar: AppBar(title: Text('List scroll')), body: new MyHome()));
}
}
class MyHome extends StatelessWidget { // Wrapper Widget
@override
Widget build(BuildContext context) {
Future.delayed(Duration.zero, () => showAlert(context));
return Container(
child: Text("Hello world"),
);
}
void showAlert(BuildContext context) {
showDialog(
context: context,
builder: (context) => AlertDialog(
content: Text("hi"),
));
}
}
Примечание: Обратитесь сюда для того, чтобы обернуть показывать предупреждение внутри Future.delayed(Duration.zero,..)
Просто переопределить initState
и позвони своему _showDialog
метод внутри Timer.run()
@override
void initState() {
super.initState();
Timer.run(() => _showDialog());
}
Вот как я добился этого простым способом:
Над методом сборки вашего главного экрана (или любого желаемого виджета):
Future checkFirstRun(BuildContext context) async { SharedPreferences prefs = await SharedPreferences.getInstance(); bool isFirstRun = prefs.getBool('isFirstRun') ?? true; if (isFirstRun) { // Whatever you want to do, E.g. Navigator.push() prefs.setBool('isFirstRun', false); } else { return null; } }
Затем в initState вашего виджета:
@override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) => checkFirstRun(context)); }
Это гарантирует, что функция будет запущена после создания виджета.
Я решил это с помощью пакета, разработанного Flutter Community. здесь https://pub.dev/packages/after_layout
Добавьте это в свой pubspec.yaml
after_layout: ^1.0.7+2
И попробуйте ниже пример
import 'package:after_layout/after_layout.dart';
import 'package:flutter/material.dart';
class DialogDemo extends StatefulWidget {
@override
_DialogDemoState createState() => _DialogDemoState();
}
class _DialogDemoState extends State<DialogDemo>
with AfterLayoutMixin<DialogDemo> {
@override
void initState() {
super.initState();
}
@override
void afterFirstLayout(BuildContext context) {
_neverSatisfied();
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: Container(
decoration: BoxDecoration(color: Colors.red),
),
);
}
Future<void> _neverSatisfied() async {
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: Text('Rewind and remember'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('You will never be satisfied.'),
Text('You\’re like me. I’m never satisfied.'),
],
),
),
actions: <Widget>[
FlatButton(
child: Text('Regret'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
}
Если вы используете блоки, используйте
BlocListener
как было предложено @mirkancal в этом ответе: Flutter: bloc, как показать диалоговое окно с предупреждением
для меня я использую это, поэтому каждый раз, когда пользователь нажимает на страницу, пользователь показывает этот отказ от ответственности
void showDisclaimerPopup() {
showDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text(
'Q and A ?',
),
content: SizedBox(
width: double.maxFinite,
child: ListView.builder(
shrinkWrap: true,
itemCount: disclaimerPoints.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'• ',
style: TextStyle(fontSize: 18),
),
Expanded(
child: Text(
disclaimerPoints[index],
style: const TextStyle(fontSize: 16),
),
),
],
),
);
},
),
),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(25.0)),
),
actions: [
Center(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25),
),
),
child: Text(
'okay',
style: txtSubheadBlack,
),
onPressed: () {
Navigator.pop(context);
},
),
),
],
),
); }
@override void initState() {
super.initState();
Future.delayed(Duration.zero, () => showDisclaimerPopup()); }