Используйте 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.

Другие вопросы по тегам