Reputation: 1331
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
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
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
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