'How to pass data back from modal in flutter?

So basically I am using the showModalBottomSheet widget to show a full screen container that has a GestureDetector that runs this onTap:

onTap: () {
  final String testText = "Sup";
  Navigator.of(context).pop(testText);
}

This obviously returns the text when I await the result when I call showModalBottomSheet however, I would also like to set enableDrag: true so that we can swipe the modal away.

My question is:

How can I pass an argument/result back when doing a swipe to dismiss? With a function, I can simple do Navigator.of(context).pop(...) but when we swipe, there is no function and so therefore I can't figure out a way to pass arguments when we swipe to dismiss.

Thank you!



Solution 1:[1]

When you swipe the pop() method gets called and there isn't anyway to override it but I figured out a way to handle this scenario:

You can use then() method on showModalBottomSheet() like this:

showModalBottomSheet(context: context, builder: (context) => SecondPage()).then((value) {
        str = "done";
        print("data: $str");
      });

Keep in mind that the value that future returns the value that gets returned in pop() method otherwise it is null.

Solution 2:[2]

It looks like showModalBottomSheet doesn't have a way to specify the close value. So it always returns null in that case. And there is not much you can do. But the options I see:

  • use result wrapper to return value by reference. Like that:
class MyResult {
    int myValue;
}

class MyBottomWidget ... {
    MyResult result;
    MyBottomWidget(this.result);

    // then you can initialize the value somewhere
    // result.myValue = 5;
}

final result = MyResult();
await showModalBottomSheet(context: context, builder: (_) => MyBottomWidget(result);
// and here you can use your value
print('Result value: ${result.myValue});

  • another way is to return a value if the result of showModalBottomSheet is null which means a modal has been closed / dissmissed.

final result = await showModalBottomSheet(...);
if (result == null) {
    // initialize the value with a value you need when modal is closed.
}

You can make a func wrapper to simplify the process:

Future<T> myShowModalBottomSheet<T>(BuildContext context, WidgetBuilder builder, T dismissValue) async {
  final value = await showModalBottomSheet<T>(context: context, builder: builder);
  return value ?? dismissValue;
}

or like that:

Future<T> myShowModalBottomSheet<T>(BuildContext context, WidgetBuilder builder, T Function() dismissedValueBuilder) async {
  final value = await showModalBottomSheet<T>(context: context, builder: builder);
  return value ?? dismissedValueBuilder();
}
  • the other way is to make your own shoModalBottomSheet that will allow to the specified value. The source code of the function is available so it's not that difficult to implement it. It would be the cleanest solution, but still, it has some downsides as well. First is it's much more to do. The other thing is your solution will be not in sync with the native flutter function. I.e. if flutter will change the behavior of that function or widgets you will need to update your code.
  • search on pub.dev for a package that will have the functionality you need.

Maybe there is some other way, but I'm not aware of it).

Solution 3:[3]

Use this widget and add Navigator. Pop.

->Use this WillPopScope

For More Information Visit Click Here

OR

If you want to pass data in pop. Then see Example:-

import 'package:flutter/material.dart';

void main() {
  runApp(
    const MaterialApp(
      title: 'Returning Data',
      home: HomeScreen(),
    ),
  );
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Returning Data Demo'),
      ),
      body: const Center(
        child: SelectionButton(),
      ),
    );
  }
}

class SelectionButton extends StatelessWidget {
  const SelectionButton({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () {
        _navigateAndDisplaySelection(context);
      },
      child: const Text('Pick an option, any option!'),
    );
  }

  // A method that launches the SelectionScreen and awaits the result from
  // Navigator.pop.
  void _navigateAndDisplaySelection(BuildContext context) async {
    // Navigator.push returns a Future that completes after calling
    // Navigator.pop on the Selection Screen.
    final result = await Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => const SelectionScreen()),
    );

    // After the Selection Screen returns a result, hide any previous snackbars
    // and show the new result.
    ScaffoldMessenger.of(context)
      ..removeCurrentSnackBar()
      ..showSnackBar(SnackBar(content: Text('$result')));
  }
}

class SelectionScreen extends StatelessWidget {
  const SelectionScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Pick an option'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: ElevatedButton(
                onPressed: () {
                  // Close the screen and return "Yep!" as the result.
                  Navigator.pop(context, 'Yep!');
                },
                child: const Text('Yep!'),
              ),
            ),
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: ElevatedButton(
                onPressed: () {
                  // Close the screen and return "Nope." as the result.
                  Navigator.pop(context, 'Nope.');
                },
                child: const Text('Nope.'),
              ),
            )
          ],
        ),
      ),
    );
  }
}

Selection Screen:-

Navigator.pop(context, 'Yep!');

Button Code (Navigator Push Like this):-

 final result = await Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => const SelectionScreen()),

For More Information

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Dharman
Solution 2 Dharman
Solution 3