Senthur Kumaran
Senthur Kumaran

Reputation: 1331

showModelBottomSheet is not opening after Future.delay

I have two showModalBottomSheet(). In the First modalSheet there is a IconButton(). When the IconButton tapped, The First sheet should be closed and after 2 seconds the second modalSheet should be opened.

When I use the Future.delay function between the Navigator.pop(context) and the second showModalBottomSheet(), It throws some error and the second sheet is not opening.

Code:

IconButton(
           onPressed: () {
                        taskerFilterController.applyingLangaugePairChanges().whenComplete(
                          () {
                            Navigator.pop(context);
                            Future.delayed(const Duration(seconds: 2), () {
                              if (mainLayoutController.contactType == 1 && mainLayoutController.locationName.isEmpty) {
                                showModalBottomSheet(context: context, builder: (BuildContext context) => const TaskType(), isScrollControlled: true);
                              }
                            });
                          },
                        );
                      },
                      icon: const Icon(Icons.done, size: 25, color: kGreen)),

If I remove the Future.delay it was working fine or if I remove the Navigator.pop(context) it was fine but the first modalSheet has to be closed before opening the second modalSheet.

Error:

    Null check operator used on a null value
I/flutter (32026): #0      Element.widget (package:flutter/src/widgets/framework.dart:3483:31)
I/flutter (32026): #1      debugCheckHasMediaQuery.<anonymous closure> (package:flutter/src/widgets/debug.dart:296:17)
I/flutter (32026): #2      debugCheckHasMediaQuery (package:flutter/src/widgets/debug.dart:311:4)
I/flutter (32026): #3      showModalBottomSheet (package:flutter/src/material/bottom_sheet.dart:1254:10)
I/flutter (32026): #4      CatAndSubcat.build.<anonymous closure>.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:tolk2go/Utility/cat_and_subcat.dart:37:33)
I/flutter (32026): #5      new Future.delayed.<anonymous closure> (dart:async/future.dart:422:39)
I/flutter (32026): #6      Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:18:15)
I/flutter (32026): #7      _Timer._runTimers (dart:isolate-patch/timer_impl.dart:398:19)
I/flutter (32026): #8      _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:429:5)
I/flutter (32026): #9      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)

How do solve this? Is there any way to close the showModalBottomSheet() without using the Navigator.pop(context)?

Upvotes: 0

Views: 94

Answers (3)

Senthur Kumaran
Senthur Kumaran

Reputation: 1331

Both answers above are correct. Those are the cause of the issue. Variable shadowing is the exact issue here. For using scaffold key to open the bottom sheet there are some difference between scaffoldKey.showbottomsheet() and showModalBottomSheet() in the UI. I want to use the showModalBottomSheet() and the two bottom sheet are in different class in my case. So, to solve the issue, I passed both context in the class constructor.

First Bottom Sheet:

showModalBottomSheet(
                      context: context,
                      builder: (BuildContext modelContext) => CatAndSubcat(mainContext: context, secContext: modelContext),
                      isScrollControlled: true);

CatAndSub Class:

    class CatAndSubcat extends StatelessWidget {
          const CatAndSubcat({required this.mainContext, required this.secContext, super.key});
        
          final BuildContext mainContext;
        
          final BuildContext secContext;
 @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(10.0),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.start,
        mainAxisSize: MainAxisSize.min,
        children: [
          GetBuilder<TaskerFilterController>(
            builder: (taskerFilterController) {
              return Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  IconButton(onPressed: taskerFilterController.cancelingLangaugePairChanges, icon: const Icon(Icons.close, size: 25, color: kOrange)),
                  Obx(() => Text(kLanguage.value.iNeedHelpWithTasks, style: Theme.of(context).textTheme.titleSmall)),
                  IconButton(
                      onPressed: () {
                        taskerFilterController.applyingLangaugePairChanges().whenComplete(
                          () {
                            Navigator.pop(secContext);
                            Future.delayed(const Duration(seconds: 2), () {
                              if (mainLayoutController.contactType == 1 &&
                                  mainLayoutController.locationName.isEmpty &&
                                  mainLayoutController.taskTypeFirstTime) {
                                mainLayoutController.taskTypeFirstTime = false;
                                showModalBottomSheet(
                                    context: mainContext,
                                    builder: (BuildContext secContext) => TaskType(mainContext: mainContext, secContext: secContext),
                                    isScrollControlled: true);
                              }
                            });
                          },
                        );
                      },
                      icon: const Icon(Icons.done, size: 25, color: kGreen)),
                ],
              );
            },
          ),

Second Bottom Sheet:

taskerFilterController.applyingLangaugePairChanges().whenComplete(
                          () {
                            Navigator.pop(secContext);
                            Future.delayed(const Duration(seconds: 2), () {
                              if (mainLayoutController.contactType == 1 &&
                                  mainLayoutController.locationName.isEmpty &&
                                  mainLayoutController.taskTypeFirstTime) {
                                mainLayoutController.taskTypeFirstTime = false;
                                showModalBottomSheet(
                                    context: mainContext,
                                    builder: (BuildContext secContext) => TaskType(mainContext: mainContext, secContext: secContext),
                                    isScrollControlled: true);
                              }
                            });
                          },
                        );

Upvotes: 0

Ramon Farizel
Ramon Farizel

Reputation: 185

The issue is likely due to the context being disposed when you pop the first modal sheet. This can be resolved by using a GlobalKey to provide a valid context for the second showModalBottomSheet.

    final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();

    // ...

    IconButton(
      onPressed: () {
        taskerFilterController.applyingLangaugePairChanges().whenComplete(
          () async {
            Navigator.pop(context);
            await Future.delayed(const Duration(seconds: 2));
            if (mainLayoutController.contactType == 1 && mainLayoutController.locationName.isEmpty) {
              scaffoldKey.currentState.showBottomSheet((context) => const TaskType());
            }
          },
        );
      },
      icon: const Icon(Icons.done, size: 25, color: Green)
    ),

    // ...
    
    Scaffold(
      key: scaffoldKey,
      // ...
    )

Remember to assign the scaffoldKey to your Scaffold widget's key property.

Upvotes: 1

Dhafin Rayhan
Dhafin Rayhan

Reputation: 8429

To show the second modal bottom sheet, use the context from the widget instead of the context from the first modal bottom sheet. To do this, you can rename the inner context to prevent variable shadowing.

class MyWidget extends StatelessWidget {
  const MyWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return FilledButton(
      onPressed: () {
        showModalBottomSheet(
          context: context,
          builder: (modalContext) { // Rename `context` to `modalContext`
            return IconButton(
              onPressed: () {
                Navigator.pop(modalContext); // Use `modalContext` to pop the first modal
                Future.delayed(const Duration(seconds: 2), () {
                  showModalBottomSheet(
                    context: context, // This context is from MyWidget's build method
                    builder: (_) => const TaskType(),
                  );
                });
              },
              icon: Icon(Icons.done),
            );
          },
        );
      },
      child: Text('Show first bottom sheet'),
    );
  }
}

Upvotes: 2

Related Questions