How do I navigator.push a showModalBottomSheet for flutter? - flutter

How do I navigator.push a showModalBottomSheet for flutter?
class SetRepeatButton extends StatefulWidget {
#override
_SetRepeatButtonState createState() => _SetRepeatButtonState();
}
class _SetRepeatButtonState extends State<SetRepeatButton> {
void _repeatMenu(BuildContext context) {
showModalBottomSheet(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
backgroundColor: Colors.white,
context: context,
builder: (builder) => Padding(
padding: const EdgeInsets.all(20.0),
child: Container(
height: MediaQuery.of(context).copyWith().size.height / 3,
child: Column(
children: <Widget>[
],
),
),
)
);
}
#override
Widget build(BuildContext context) {
return Builder(
builder: (builder) => FlatButton(
color: Colors.white,
textColor: Colors.grey,
disabledColor: Colors.grey,
disabledTextColor: Colors.black,
onPressed: () async {
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => _repeatMenu()),
);
},
child: Row(
),
),
);
}
}

what do you want to achieve?
if just to show the bottomsheet, just call the function in on press.
but it will be a little different if you want to catch the variable passed from the bottomsheet to the main context
you can try like this to show bottomsheet
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: SetRepeatButton(),
);
}
}
class SetRepeatButton extends StatefulWidget {
#override
_SetRepeatButtonState createState() => _SetRepeatButtonState();
}
class _SetRepeatButtonState extends State<SetRepeatButton> {
void _repeatMenu(BuildContext context) {
showModalBottomSheet(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
backgroundColor: Colors.white,
context: context,
builder: (builder) => Padding(
padding: const EdgeInsets.all(20.0),
child: Container(
height: MediaQuery.of(context).copyWith().size.height / 3,
child: Column(
children: <Widget>[
Text('Text'),
],
),
),
));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Text'),
),
body: Center(
child: RaisedButton(
color: Colors.white,
textColor: Colors.grey,
disabledColor: Colors.grey,
disabledTextColor: Colors.black,
onPressed: () async {
_repeatMenu(context);
},
child: Text('Text'),
),
),
);
}
}

Related

Issue with provider when navigating between screens

I'm trying to learn more about provider but I'm facing an issue with an app I'm developing, specifically when I navigate to a screen. When I press on the button to take me to the history_screen is when I get an error from Provider, all the providers are declared at the top of the tree so not really sure why there's still an error.
Hopefully someone can help me!
See my code below and the error I get:
Main.dart
void main() {
runApp(
MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
StreamProvider<List<Meds>>.value(
value: MedicinesNotifier().medicinesStream(),
initialData: MedicinesNotifier().meds,
updateShouldNotify: (_, __) => true),
//ChangeNotifierProvider<MedicinesNotifier>(create: (_) => MedicinesNotifier()),
ProxyProvider<List<Meds>,MedicinesNotifier>(
update:
(BuildContext context, List<Meds> meds1, MedicinesNotifier? medNoti) => MedicinesNotifier.med(meds1)
),
ChangeNotifierProvider<SearchHistoryData>(create: (_) => SearchHistoryData()),
],
child: MaterialApp(
title: 'Flutter Demo',
/*theme: ThemeData(
primarySwatch: Colors.blueGrey,
),*/
home: MyHomePage(),
),
);
}
}
home_page.dart
class _MyHomePageState extends State<MyHomePage> {
List<Meds> medFiltered = [];
TextEditingController searchController = new TextEditingController();
String searchText = "";
#override
Widget build(BuildContext context) {
bool isSearching = searchController.text.isNotEmpty;
return Scaffold(
bottomNavigationBar: BottomBar(),
body: SafeArea(
child: Container(
padding: EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
Container(
child: TextField(
controller: searchController,
decoration: InputDecoration(
labelText: "Search",
border: new OutlineInputBorder(
borderSide: new BorderSide(
color: Theme.of(context).primaryColor,
),
borderRadius: BorderRadius.circular(30.0),
),
prefixIcon: Icon(Icons.search),
),
onChanged: (value) {
Provider.of<MedicinesNotifier>(context, listen: false)
.changeSearchString(value);
},
),
),
Expanded(
child:
ListView.builder(
shrinkWrap: true,
itemCount: isSearching == true
? context
.watch<MedicinesNotifier>()
.meds
.length
: context
.watch<List<Meds>>()
.length,
itemBuilder: (context, index) {
Meds med = isSearching == true
? context
.watch<MedicinesNotifier>()
.meds
[index]
: context
.watch<List<Meds>>()[index]; //medsStreamed[index];
return ListTile(
onTap: () {
context.read<MedicinesNotifier>().addHistory(med);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChatScreen(
med: context.watch<MedicinesNotifier>().findMedicine(med),
)));
},
title: Text(med.name),
subtitle: Text(med.description.substring(0, 10)),
);
},
),
),
],
),
),
),
);
}
}
BottomBar widget
class BottomBar extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BottomAppBar(
shape: CircularNotchedRectangle(),
color: Theme.of(context).primaryColor,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
IconButton(
icon: Icon(Icons.search),
color: Colors.white,
iconSize: 40.0,
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => MyHomePage()));
},
),
IconButton(
color: Colors.white,
icon: Icon(Icons.history_edu),
iconSize: 40.0,
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => HistoryScreen()));
},**//Causing the issue**
),
]),
);
}
}
history_screen.dart
class HistoryScreen extends StatefulWidget {
const HistoryScreen({Key? key}) : super(key: key);
#override
_HistoryScreenState createState() => _HistoryScreenState();
}
class _HistoryScreenState extends State<HistoryScreen> {
#override
Widget build(BuildContext context) {
//List<Meds> temp1 = context.watch<MedicinesNotifier>().history;
return Scaffold(
bottomNavigationBar: BottomBar(),
body: SafeArea(
child: Column(
children: <Widget>[
Container(
color: Theme.of(context).primaryColor,
child: Padding(
padding: EdgeInsets.fromLTRB(15.0, 0.0, 15.0, 30.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
TextButton(
onPressed: () {},
child: Text(
"Filter",
style: TextStyle(
color: Colors.white,
),
),
),
],
),
],
),
),
),
Expanded(
child: Container(
//color: Colors.white54,
decoration: BoxDecoration(
color: Colors.white30,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30.0),
topRight: Radius.circular(30.0),
),
),
child: ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30.0),
topRight: Radius.circular(30.0),
),
child: Consumer<SearchHistoryData?>(**//Using Consumer here**
builder: (context, searchHistoryData, child) {
//assert(child != null);
return ListView.builder(
itemCount: searchHistoryData!.sizeOfHistory(),
itemBuilder: (BuildContext context, int index) {
//final historyData =
return Container(
margin: EdgeInsets.only(...
Exception caught
The following ProviderNotFoundException was thrown building Consumer<SearchHistoryData?>(dirty):
Error: Could not find the correct Provider<SearchHistoryData?> above this Consumer<SearchHistoryData?> Widget
This happens because you used a BuildContext that does not include the provider
of your choice. There are a few common scenarios:
You added a new provider in your main.dart and performed a hot-reload.
To fix, perform a hot-restart.
The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then
other routes will not be able to access that provider.
You used a BuildContext that is an ancestor of the provider you are trying to read.
Make sure that Consumer<SearchHistoryData?> is under your MultiProvider/Provider<SearchHistoryData?>.
This usually happens when you are creating a provider and trying to read it immediately.
For example, instead of:
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// Will throw a ProviderNotFoundError, because `context` is associated
// to the widget that is the parent of `Provider<Example>`
child: Text(context.watch<Example>()),
),
}
consider using builder like so:
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// we use `builder` to obtain a new `BuildContext` that has access to the provider
builder: (context) {
// No longer throws
return Text(context.watch<Example>()),
}
),
}

A dialog has a delay to open, I get an error when navigating to another view before the dialog is generated. How can I make it not be generated?

I am new to flutter.
In my real problem, my client is in places where it is very frequent that the internet is very slow, so sometimes an attempt is made to make a web request and this may take time, so the user leaves the screen before the web request is completed. Sometimes my app after completing a web request generates a dialog. So here is where my problem lies, the user is trying to make a web request and while it is done, they leave the screen and then the dialog is generated.
I am trying to simulate this problem with a delay that later generates the dialog.
I am not thinking of any strategy to end the web request, what I want is to find a way that once I leave the screen, causes the dialog not to be generated something like a dispose
I made an example where I have 2 screens. On the second screen a dialog is generated with a delay of 5 seconds when the button is clicked. If I navigate to another screen before the dialog is opened I get an error. I assume this occurs because the view was destroyed and therefore the dialog cannot be opened.
What can I do to avoid the error when the dialog is generated after being in another view? if I am in another view I DO NOT WANT the dialog to be generated.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
print("main");
return MaterialApp(title: 'Provider Example', initialRoute: '/', routes: {
'/': (context) => Home(),
'home': (context) => Home(),
'dialogpage': (context) => Dialogpage(),
});
}
}
class Home extends StatelessWidget {
Home() {
print("home");
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: const Text('home'),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.add_alert),
tooltip: 'Show Snackbar',
onPressed: () {
Navigator.pushNamed(context, "dialogpage");
},
),
],
),
body: const Center(
child: Text(
'home',
style: TextStyle(fontSize: 24),
),
),
);
}
}
class Dialogpage extends StatelessWidget {
Dialogpage() {
print("dialogpage");
}
dialog(BuildContext context) {
Future.delayed(const Duration(seconds: 5), () {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0)),
title: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(19.0),
topRight: Radius.circular(19.0),
),
),
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 5),
child: Text(
'Error',
style: TextStyle(color: Colors.white),
),
),
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 20.0, bottom: 20.0),
child: Icon(
Icons.error,
size: 50,
),
),
Text("dialog"),
],
),
titlePadding: EdgeInsets.all(0),
actions: <Widget>[
FlatButton(
child: Text('Aceptar'),
onPressed: () {
return Navigator.of(context).pop();
}),
],
);
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('dialog'),
),
body: Center(
child: RaisedButton(
child: Text("show dialog"),
onPressed: () {
dialog(context);
}),
),
);
}
}
use Globalkey in scaffold in then check the context in dialog method is it != null
then run dialog otherwise don't...
GlobalKey _scafolldKey = GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scafolldKey,
appBar: AppBar(
title: const Text('dialog'),),
body: Center(
child: RaisedButton(
child: Text("show dialog"),
onPressed: () {
dialog(context);
}),
),
);
}
}
dialog(BuildContext context) {
Future.delayed(const Duration(seconds: 2), () {
if(_scafolldKey.currentContext !=null){
showDialog();
}
});
}
Instead of Future.delayed, you should use Timer, which can be cancelled in onDispose method.
Working solution:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
print("main");
return MaterialApp(
title: 'Provider Example',
initialRoute: '/',
routes: {
'/': (context) => Home(),
'home': (context) => Home(),
'dialogpage': (context) => Dialogpage(),
},
);
}
}
class Home extends StatelessWidget {
Home() {
print("home");
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('home'),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.add_alert),
tooltip: 'Show Snackbar',
onPressed: () {
Navigator.pushNamed(context, "dialogpage");
},
),
],
),
body: const Center(
child: Text(
'home',
style: TextStyle(fontSize: 24),
),
),
);
}
}
class Dialogpage extends StatefulWidget {
#override
_DialogpageState createState() => _DialogpageState();
}
class _DialogpageState extends State<Dialogpage> {
Timer _timer;
#override
void dispose() {
_timer?.cancel();
super.dispose();
}
dialog(BuildContext context) {
_timer = Timer(
const Duration(seconds: 3),
() {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
title: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(19.0),
topRight: Radius.circular(19.0),
),
),
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 5),
child: Text(
'Error',
style: TextStyle(color: Colors.white),
),
),
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 20.0, bottom: 20.0),
child: Icon(
Icons.error,
size: 50,
),
),
Text("dialog"),
],
),
titlePadding: EdgeInsets.all(0),
actions: <Widget>[
FlatButton(
child: Text('Aceptar'),
onPressed: () {
return Navigator.of(context).pop();
},
),
],
);
},
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('dialog'),
),
body: Center(
child: RaisedButton(
child: Text("show dialog"),
onPressed: () {
dialog(context);
},
),
),
);
}
}
Try this code
class Dialogpage extends StatelessWidget {
...
Timer t;
dialog(BuildContext context) {
t = Timer(Duration(seconds: 5), () {
showDialog(...);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('dialog'),
leading: IconButton(
icon: Icon(Icons.arrow_back, color: Colors.black),
onPressed: () {
t?.cancel();
Navigator.of(context).pop();
},
),
),
body: Center(
child: RaisedButton(
child: Text("show dialog"),
onPressed: () {
dialog(context);
}),
),
);
}
}
Hope it helps.

how to get custom circular progress indicator as shown in image with flutter?

I tried to get this circular progress indicator in alert dialog type. here's my code and output below.
code:
Future<void> loaderDialogNormal(BuildContext context) {
return showDialog<void>(
context: context,
builder: (BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(20.0)),
child:
Container(
width: 50, height: 50,
child: CircularProgressIndicator()),
);
});
}
my output:
expected output:
how to achieve the expected output?
You can copy paste run full code below
You can wrap with Center
Container(
width: 50,
height: 50,
child: Center(child: CircularProgressIndicator())),
);
working demo
full code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
Future<void> loaderDialogNormal(BuildContext context) {
return showDialog<void>(
context: context,
builder: (BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0)),
child: Container(
width: 50,
height: 50,
child: Center(child: CircularProgressIndicator())),
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: () {
loaderDialogNormal(context);
},
child: Text('click'),
),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Try this code, It works as you want
Future<void> loaderDialogNormal(BuildContext context) {
showDialog(
context: context,
barrierDismissible: false,
builder: (_) {
return Center(
child: Container(
width: 100.0,
height: 100.0,
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(10.0),
),
),
),
child: Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
Colors.grey),
),
),
));
});
}

Persistent bottom sheet form data

I have one form on a bottom sheet. It's opened on click of one button. It can be closed when the user clicks outside the form. I want to maintain the form data if the user reopens the form. I don't want to assign each form field value explicitly. Is there any other way of saving the form state and reusing it while creating the bottom sheet again?
void _modalBottomSheetMenu(BuildContext context, Widget form) async {
await showModalBottomSheet<dynamic>(
isDismissible: false,
isScrollControlled:true,
context: context,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20)),
),
backgroundColor: Colors.white,
builder: (BuildContext bc) {
return SingleChildScrollView(
child: Container(
padding:
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Padding(
padding: const EdgeInsets.fromLTRB(20.0, 20.0, 20.0, 0.0),
child: form) // From with TextField inside
));}
);
So I finally found one of the best approaches for maintaining the state using Provider. I also explored other ways such as BLOC but it was very verbose for it. We can use BLOC for other cases but Provider is a better solution in case of a bottom sheet.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Bottom Sheet',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ChangeNotifierProvider(
create: (context) => TitleDataNotifier(),
child: ButtomSheetScreen(),
),
);
}
}
class TitleDataNotifier with ChangeNotifier {
String _name;
String get name => _name;
set name(String name) {
_name = name;
notifyListeners();
}
}
class AddTaskScreen extends StatefulWidget {
final TitleDataNotifier valueProvider;
AddTaskScreen(this.valueProvider);
#override
_AddTaskScreenState createState() => _AddTaskScreenState();
}
class _AddTaskScreenState extends State<AddTaskScreen> {
final _controller = TextEditingController();
#override
void initState() {
super.initState();
_controller.text = widget.valueProvider.name;
}
void dispose() {
_controller.dispose();
super.dispose();
}
#override
void deactivate() {
widget.valueProvider.name = _controller.text;
super.deactivate();
}
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(20.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0),
topRight: Radius.circular(20.0),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
'Add Task',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 30.0,
color: Colors.lightBlueAccent,
fontWeight: FontWeight.w700),
),
TextField(
controller: _controller,
autofocus: false,
textAlign: TextAlign.center,
onChanged: (newText) {
widget.valueProvider._name = newText;
},
),
FlatButton(
child: Text(
'Add',
style: TextStyle(color: Colors.white),
),
color: Colors.lightBlueAccent,
onPressed: () {
Navigator.pop(context);
},
)
],
),
);
}
}
class ButtomSheetScreen extends StatelessWidget {
void openBottomSheet(context) {
var valueProvider = Provider.of<TitleDataNotifier>(context, listen: false);
showModalBottomSheet<dynamic>(
context: context,
builder: (BuildContext context) {
return ChangeNotifierProvider.value(
value: valueProvider,
child: StatefulBuilder(
builder: (BuildContext context, StateSetter state) {
return Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: Wrap(
children: <Widget>[
AddTaskScreen(valueProvider),
],
),
);
}),
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Bottom Sheet",
),
),
body: Center(
child: Container(
child: IconButton(
icon: const Icon(Icons.work),
onPressed: () {
openBottomSheet(context);
},
)),
));
}
}

Scaffold Drawer to showModalBottomSheet

At the homescreen of myApp() I have a stateless widget, it contains a MaterialApp and a Scaffold. Scaffold have a property of drawer and I passed I created a drawer, and one of the item in my drawer needs to open the showModalBottomSheet while closing the drawer. How can I achieve this? I've tried passing the context itself, and as globalKey.currentContext (after GlobalKey<ScaffoldState> globalKey = GlobalKey();) but the drawer sometimes closes, other time gives me a NoMethodFoundException (or something like that)
In short, how to have a Scaffold drawer that have one of the item, when tapped closes the drawer and showModalBottomSheet?
Current code:
class Timeline extends StatelessWidget {
#override
Widget build(BuildContext context) {
GlobalKey<ScaffoldState> homeScaffoldKey = GlobalKey();
return MaterialApp(
title: "Test",
theme: ThemeData(
appBarTheme: AppBarTheme(iconTheme: IconThemeData(color: Colors.black)),
),
home: Scaffold(
key: homeScaffoldKey,
drawer: showDrawer(homeScaffoldKey.currentContext),
backgroundColor: Colors.grey[100],
body: Stack(
children: <Widget>[
HomePageView(),
AppBar(
elevation: 0,
backgroundColor: Colors.transparent,
),
],
),
),
);
}
}
Drawer showDrawer(BuildContext context) {
void showCalendarsModalBottom() {
showModalBottomSheet(
context: context,
builder: (BuildContext builder) {
return ListView.builder(
itemCount: repo.calendars.length,
itemBuilder: (builder, index) {
return StatefulBuilder(
builder: (builder, StateSetter setState) => ListTile(
leading: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Checkbox(
value: repo.getIsEnabledCal(repo.getCal(index)),
onChanged: (value) {
setState(() {
repo.toggleCalendar(repo.getCal(index));
});
},
),
Container(
height: 14,
width: 14,
margin: EdgeInsets.only(left: 2, right: 6),
decoration: BoxDecoration(
color: Colors.redAccent,
shape: BoxShape.circle,
),
),
Text(
repo.getCal(index).name,
style: TextStyle(
fontSize: 16,
),
),
],
),
onTap: () {
setState(() {
repo.toggleCalendar(repo.getCal(index));
});
},
),
);
},
);
},
);
}
return Drawer(
child: ListView(
children: <Widget>[
DrawerHeader(
child: Align(
child: Text('Timeline', textScaleFactor: 2),
alignment: Alignment.bottomLeft,
),
),
ListTile(
title: Text('Dark Mode'),
onTap: () => Navigator.pop(context),
),
ListTile(
title: Text('Calenders'),
onTap: () {
Navigator.pop(context);
showCalendarsModalBottom();
},
)
],
),
);
}
Updated working code based on your code snippet:
You'll need to have statefulwidget that will help to pass the context from drawer to bottomsheet and pass the context as an argument in showCalendarModalBottomSheet() method.
void main() {
runApp(new MaterialApp(home: Timeline(), debugShowCheckedModeBanner: false));
}
class Timeline extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Test",
theme: ThemeData(
appBarTheme: AppBarTheme(iconTheme: IconThemeData(color: Colors.black)),
),
home: MyHomePage()
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: AppDrawer(),
backgroundColor: Colors.grey[100],
body: Stack(
children: <Widget>[
//HomePageView(),
AppBar(
elevation: 0,
backgroundColor: Colors.transparent,
)
],
)
);
}
Widget AppDrawer() {
return Drawer(
child: ListView(
children: <Widget>[
DrawerHeader(
child: Align(
child: Text('Timeline', textScaleFactor: 2),
alignment: Alignment.bottomLeft,
),
),
ListTile(
title: Text('Dark Mode'),
onTap: () => Navigator.pop(context),
),
ListTile(
title: Text('Calenders'),
onTap: () {
Navigator.of(context).pop();
showCalendarsModalBottom(context);
},
)
],
),
);
}
Future<Null> showCalendarsModalBottom(context) {
return showModalBottomSheet(context: context, builder: (context) => Container(
color: Colors.red,
// your code here
));
}
}
And the output is: When app drawer menu Calendar is tapped, it closes and opens the bottomsheet seamlessly. If you tap on app drawer again and repeat steps, you see smooth transition between drawer and bottomsheet. Hope this answers your question.