Используйте SnackBar с CupertinoPageScaffold
Есть ли способ использовать SnackBar
с CupertinoPageScaffold
?
Я получаю следующую ошибку:
Scaffold.of() called with a context that does not contain a Scaffold.
No Scaffold ancestor could be found starting from the context that was passed to Scaffold.of().
Вызывая SnackBar
в дочернем виджете, используя:
final snackBar = SnackBar(
content: Text('Yay! A SnackBar!'),
action: SnackBarAction(
label: 'Undo',
onPressed: () {
// Some code to undo the change!
},
),
);
// Find the Scaffold in the Widget tree and use it to show a SnackBar!
Scaffold.of(context).showSnackBar(snackBar);
3 ответа
Вы должны включить Scaffold
как CupertinoPageScaffold
не ребенок, то вам нужно отделить код, где вы называете showSnackBar
функция от того из Scaffold
в отдельный класс, который здесь SnackBarBody
потому что SnackBar
и Scaffold
нельзя вызывать из того же Build
функция. Вот полный рабочий пример:
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
void main() => runApp(SnackBarDemo());
class SnackBarDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'SnackBar Demo',
home: CupertinoPageScaffold(
child: SnackBarPage(),
),
);
}
}
class SnackBarPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: SnackBarBody(),
);
}
}
class SnackBarBody extends StatefulWidget {
SnackBarBody({Key key,}):
super(key: key);
@override
_SnackBarBodyState createState() => new _SnackBarBodyState();
}
class _SnackBarBodyState extends State<SnackBarBody> {
@override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
onPressed: () {
final snackBar = SnackBar(content: Text('Yay! A SnackBar!'),action: SnackBarAction(label: 'Undo',
onPressed: () {
// Some code to undo the change!
},
),
);
// Find the Scaffold in the Widget tree and use it to show a SnackBar!
Scaffold.of(context).showSnackBar(snackBar);
},
child: Text('Show SnackBar'),
),
);
}
}
Хотя нельзя использоватьshowSnackBar
сCupertinoPageScaffold
, но вы можете создать настраиваемую функцию SnackBar для iOS (CupertinoPageScaffold), используяOverlayEntry
.
Потому что
Overlay
используетStack
макет, записи оверлея могут использоватьPositioned
ипозиционировать себя в оверлее.
Здесь я создал функциюshowCupertinoSnackBar
для более удобного использования:
import 'package:flutter/cupertino.dart';
void showCupertinoSnackBar({
required BuildContext context,
required String message,
int durationMillis = 3000,
}) {
final overlayEntry = OverlayEntry(
builder: (context) => Positioned(
bottom: 8.0,
left: 8.0,
right: 8.0,
child: CupertinoPopupSurface(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 8.0,
),
child: Text(
widget.message,
style: TextStyle(
fontSize: 14.0,
color: CupertinoColors.secondaryLabel,
),
textAlign: TextAlign.center,
),
),
),
),
);
Future.delayed(
Duration(milliseconds: durationMillis),
overlayEntry.remove,
);
Overlay.of(Navigator.of(context).context)?.insert(overlayEntry);
}
Вы можете настроить SnackBar по своему вкусу. Если вам нужно анимировать наложение, вот еще один пример использованияAnimatedPositioned
внутри виджета с состоянием:
import 'package:flutter/cupertino.dart';
void showCupertinoSnackBar({
required BuildContext context,
required String message,
int durationMillis = 3000,
}) {
const animationDurationMillis = 200;
final overlayEntry = OverlayEntry(
builder: (context) => _CupertinoSnackBar(
message: message,
animationDurationMillis: animationDurationMillis,
waitDurationMillis: durationMillis,
),
);
Future.delayed(
Duration(milliseconds: durationMillis + 2 * animationDurationMillis),
overlayEntry.remove,
);
Overlay.of(Navigator.of(context).context)?.insert(overlayEntry);
}
class _CupertinoSnackBar extends StatefulWidget {
final String message;
final int animationDurationMillis;
final int waitDurationMillis;
const _CupertinoSnackBar({
Key? key,
required this.message,
required this.animationDurationMillis,
required this.waitDurationMillis,
}) : super(key: key);
@override
State<_CupertinoSnackBar> createState() => _CupertinoSnackBarState();
}
class _CupertinoSnackBarState extends State<_CupertinoSnackBar> {
bool _show = false;
@override
void initState() {
super.initState();
Future.microtask(() => setState(() => _show = true));
Future.delayed(
Duration(
milliseconds: widget.waitDurationMillis,
),
() {
if (mounted) {
setState(() => _show = false);
}
},
);
}
@override
Widget build(BuildContext context) {
return AnimatedPositioned(
bottom: _show ? 8.0 : -50.0,
left: 8.0,
right: 8.0,
curve: _show ? Curves.linearToEaseOut : Curves.easeInToLinear,
duration: Duration(milliseconds: widget.animationDurationMillis),
child: CupertinoPopupSurface(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 8.0,
),
child: Text(
widget.message,
style: TextStyle(
fontSize: 14.0,
color: CupertinoColors.secondaryLabel,
),
textAlign: TextAlign.center,
),
),
),
);
}
}
Cupertino widgets should be specific for iOS, come to replace Android
Toasts
but there is no Android
Toast
equivalent in iOS, so you cannot natively add a to a as it will not follow iOS guidelines.
One solution will be to extend
CupertinoScaffold
and add it the
SnackBar
code.
An other is in Mazin Ibrahim answer, note that it isn't recommended to nest Scaffolds.