Flutter snackbar in Hero widget

182 Views Asked by At

I used to had two widgets where the first displays a list and the second is the detail page of a specific item in the list.

class WidgetA extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: _buildAppBar(context),
      body ListView.builder(...)
  }
}



class WidgetB extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: _buildAppBar(context),
      body ....
  }
}

such that WidgetA navigates to WidgetB.

Navigator.push(context,MaterialPageRoute(builder: (context) => WidgetB()));

But since I want a nice animation for the transition, I now use a Hero widget in both Widgets.

// Widget B
@override
Widget build(BuildContext context) {
  return Hero(
    tag: someObject,    
  );
} 

But now the problem is that whenever WidgetB wants to display a snackbar, it is not visible there. If you would however navigate back to WidgetA before the snackbar would have disappeard, you can see it there.

ScaffoldMessenger.of(context).showSnackBar(SnackBar(
      duration: const Duration(seconds: 3), content: Text('some text'),
));

I looked at the Scaffold documentation which states

It is typically not necessary to nest Scaffolds. For example, in a tabbed UI, where the bottomNavigationBar is a TabBar and the body is a TabBarView, you might be tempted to make each tab bar view a scaffold with a differently titled AppBar. Rather, it would be better to add a listener to the TabController that updates the AppBar

So I also tried returning a Simple Column in WidgetB which is wrapped inside a ScaffoldMessenger but none of these solutions works... I did some lookup online and people talk about it that this is intended behaviour. But no official sources say so. How could I nonetheless display a snackbar in the detail page while still using nice Hero transitions?

1

There are 1 best solutions below

0
Karrar On

You can mimck the same effect but with bottomsheet

you can make use of this custom class I've created to make showing bottom sheet easy and accesssible from anywhere inside a project

class BottomSheetManager {
  bool _isBottomSheetVisible = false;
  ScaffoldState? _scaffoldState;
  String? currentMessage;
  Timer? _cancelTimer;
  PersistentBottomSheetController? _persistentBottomSheetController;

  void hideCurrentBottomSheet({bool immediately = false}) {
    if (immediately) {
      _cancelTimer?.cancel();
      _removeBottomSheet();
      return;
    }
    _cancelTimer = Timer(const Duration(seconds: 4), _removeBottomSheet);
  }

  void _removeBottomSheet() {
    _persistentBottomSheetController?.close();
    _cancelTimer?.cancel();
    _cancelTimer = null;
    _persistentBottomSheetController = null;
    _isBottomSheetVisible = false;
    _scaffoldState = null;
    currentMessage = null;
  }

  void showAlertMessage({
    BuildContext? context,
    GlobalKey<ScaffoldState>? scaffoldKey,
    required String message,
  }) =>
      showMessage(message: message, scaffoldKey: scaffoldKey, context: context);

  void showSuccessMessage({
    BuildContext? context,
    GlobalKey<ScaffoldState>? scaffoldKey,
    required String message,
  }) =>
      showMessage(message: message, scaffoldKey: scaffoldKey, context: context, backgroundColor: Colors.green, textColor: Colors.white);

  void showFailureMessage({
    BuildContext? context,
    GlobalKey<ScaffoldState>? scaffoldKey,
    required String message,
  }) =>
      showMessage(message: message, scaffoldKey: scaffoldKey, context: context, backgroundColor: Colors.redAccent, textColor: Colors.white);

  void showMessage({
    BuildContext? context,
    GlobalKey<ScaffoldState>? scaffoldKey,
    required String message,
    Color backgroundColor = Colors.black87,
    Color textColor = Colors.white,
  }) async {
    assert(
    (context == null) != (scaffoldKey == null),
    'you must provide either the current context or the scaffoldMessenger key to show the snack bar',
    );

    if (_isBottomSheetVisible && currentMessage == message) {
      return;
    }

    if (_isBottomSheetVisible) {
      try {
        hideCurrentBottomSheet(immediately: true);
      } catch (err) {
        debugPrint('something went wrong');
      }
      _scaffoldState = null;
      _isBottomSheetVisible = false;
    }
    currentMessage = message;

    final bottomSheetWidget = Container(
      width: double.infinity,
      height: 60,
      padding: const EdgeInsets.only(right: 40),
      alignment: Alignment.centerRight,
      decoration: BoxDecoration(
        color: backgroundColor,
        borderRadius: const BorderRadius.only(
          bottomLeft: Radius.circular(10),
          bottomRight: Radius.circular(10),
        ),
      ),
      child: Text(message, style: TextStyle(color: textColor)),
    );

    if (context != null) {
      debugPrint('context is not null');
      _scaffoldState = Scaffold.of(context);
    } else {
      _scaffoldState = scaffoldKey!.currentState as ScaffoldState;
    }

    _persistentBottomSheetController = _scaffoldState!.showBottomSheet((_) => bottomSheetWidget);
    _isBottomSheetVisible = true;
    hideCurrentBottomSheet();
    _persistentBottomSheetController!.closed.then((value) {
      _removeBottomSheet();
    });
  }
}