I made two forms with pass fields on both, then made all the code to, when the user clicks in the eye Icon, the field show the password, clicking again it hide the password.
But now I put these forms inside an Alert Dialog widget and now it doesn't updating when I click in the icon, only updates if I close the dialog and open again (you open the dialog, click in the icon, it doesn't change. If you close and open again you see the icon changed)
After some search I tried Stateful Builder but it doesn't work too.
Dialog:
Future<void> _myDialog(child){
return showDialog<void>(
context: context,
builder: (BuildContext context) {
return SingleChildScrollView(
child: AlertDialog(
content: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Padding(
padding: EdgeInsets.all(20),
child: child,
);
},
),
insetPadding: EdgeInsets.only(left: 10, right: 10),
),
);
}
);
}
Toggle method referenced in my textFields:
void _toggle(int index) {
setState(() {
_toggleList[index] = !_toggleList[index];
});
}
How can I toggle it instantly when the user click in the icon as outside the alert?
Edit
Row _showButtons(){
return Row(
children: [
RaisedButton(
child: Text("Change email"),
onPressed: () {_myDialog(_showEmailFields());}
),
RaisedButton(
child: Text("Change pass"),
onPressed: () {_myDialog(_showPassFields());}
),
],
);
}
I have created a structure for you that you should use for achieving what you want here. Make your forms into a separate stateful widgets, so that they have their own State, and you call the right setState function. Right now the setState function you are calling does not belongs to the state of your alert dialog.
class Test extends StatefulWidget {
#override
State<StatefulWidget> createState() => _TestState();
}
class _TestState extends State<Test> {
void showForm() {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("Login"),
content: LoginWidget(),
);
},
);
}
#override
Widget build(BuildContext context) {
TextTheme textTheme = Theme.of(context).textTheme;
return Scaffold(
appBar: AppBar(
title: Text("Appbar"),
),
body: Center(
child: RaisedButton(
child: Text("Show Form"),
onPressed: showForm,
),
),
);
}
}
class LoginWidget extends StatefulWidget {
LoginWidget({Key key}) : super(key: key);
#override
LoginWidgetState createState() => LoginWidgetState();
}
class LoginWidgetState extends State<LoginWidget> {
GlobalKey<FormState> _formKey;
bool _passwordVisible;
#override
void initState() {
super.initState();
_formKey = GlobalKey<FormState>();
_passwordVisible = false;
}
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
decoration: InputDecoration(labelText: "Email"),
),
SizedBox(
height: 10,
),
TextFormField(
obscureText: !_passwordVisible,
decoration: InputDecoration(
labelText: "Password",
suffixIcon: IconButton(
onPressed: () {
setState(() {
_passwordVisible = !_passwordVisible;
});
},
icon: Icon(
_passwordVisible ? Icons.visibility : Icons.visibility_off),
),
),
),
],
),
);
}
}
Related
In ListView.builder I'm adding a new ListTile with the button Pressed.
Now when I press on ListTile I want to delete that widget.
I have tried to do that by wrapping the widget with InkWell but when I try to delete it deletes from the last ListTile.
How to delete that specific ListTile when I longPressed on that.
Below here is the code
import 'package:flutter/material.dart';
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
/*InkWell(
child: widgets[index],
onLongPress: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Delete?'),
actions: [
IconButton(
onPressed: () {
widgets.removeAt(index);
setState(() {
Navigator.pop(context);
});
},
icon: Icon(Icons.check))
],
));
},
);*/
class _HomeState extends State<Home> {
#override
List<Widget> widgets = [];
int inde = 0;
List<List> blogList = [];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Note'),
centerTitle: true,
),
body: Column(children: [
Expanded(
child: ListView.builder(
itemCount: widgets.length,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return InkWell(
child: widgets[index],
onLongPress: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Delete?'),
actions: [
IconButton(
onPressed: () {
widgets.removeAt(index);
setState(() {
Navigator.pop(context);
});
},
icon: Icon(Icons.check))
],
));
},
);
},
),
),
FloatingActionButton(
onPressed: () {
setState(() {
widgets.add(Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: 150,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
border: Border.all(width: 2),
color: Color.fromARGB(255, 76, 178, 204),
),
child: ListTile(
leading: Icon(Icons.circle),
title: TextField(),
)),
));
});
},
child: Icon(Icons.add),
),
]));
}
}
Actually your code works, it deletes the ListTile which you use long press on.
The problem is that you do not assign different controllers to the TextField widgets. So if you enter some text into them, and call setState when deleting one, the values in the TextFields will be wrong, and it looks like the last one is deleted.
So you need to add the following logic to your code:
Create another list like widgets for the controllers.
When adding a new item, create a new controller and assign it to the TextField.
When deleting an item, dispose the controller and remove it from the controllers' list.
Don't forget to dispose all remaining controllers when the widget is disposed.
Here is a sample code, check for the comments where I added to your code. You can run it on DartPad.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: Home(),
),
),
);
}
}
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
List<Widget> widgets = [];
// this is the list for the controllers
List<TextEditingController> controllers = [];
int inde = 0;
List<List> blogList = [];
// you need to add this in order to dispose
// the controllers when the widget is disposed
#override
void dispose() {
for (var controller in controllers) {
controller.dispose();
}
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Note'),
centerTitle: true,
),
body: Column(children: [
Expanded(
child: ListView.builder(
itemCount: widgets.length,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return InkWell(
child: widgets[index],
onLongPress: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Delete?'),
actions: [
IconButton(
onPressed: () {
setState(() {
widgets.removeAt(index);
// dispose the controller
controllers[index].dispose();
// remove the controller from list
controllers.removeAt(index);
});
Navigator.pop(context);
},
icon: const Icon(Icons.check))
],
));
},
);
},
),
),
FloatingActionButton(
onPressed: () {
setState(() {
// create a new controller and add it to the list
final newController = TextEditingController();
controllers.add(newController);
widgets.add(Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: 150,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
border: Border.all(width: 2),
color: const Color.fromARGB(255, 76, 178, 204),
),
child: ListTile(
leading: const Icon(Icons.circle),
// assign the controller to the field
title: TextField(controller: newController),
)),
));
});
},
child: const Icon(Icons.add),
),
]));
}
}
I suggest that following the convention, begin all private members of your state class with an underscore, so rename controllers to _controllers etc.
for many of you this is an obvious / stupid question, but I've come to a point where I don't have a clue anymore. I have real difficulties understanding State Management with Bloc / Cubit.
Expectation: I have a page with a ListView (recipe_list) of all recipes and an 'add' button. Whenever I click on a ListItem or the 'add' button I go to the next page (recipe_detail). On this page I can create a new recipe (if clicked the 'add' button before), update or delete the existing recipe (if clicked on ListItem before). When I click the 'save' or 'delete' button the Navigator pops and I go back to the previous page (recipe_list). I used Cubit to manage the state of the recipe list. My expectation is that the ListView updates automatically after I clicked 'save' or 'delete'. But I have to refresh the App to display the changes.
main.dart
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Recipe Demo',
home: BlocProvider<RecipeCubit>(
create: (context) => RecipeCubit(RecipeRepository())..getAllRecipes(),
child: const RecipeList(),
)
);
}
}
recipe_list.dart
class RecipeList extends StatefulWidget {
const RecipeList({Key? key}) : super(key: key);
#override
_RecipeListState createState() => _RecipeListState();
}
class _RecipeListState extends State<RecipeList> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 24.0
),
color: const Color(0xFFF6F6F6),
child: Stack(
children: [
Column(
children: [
Container(
margin: const EdgeInsets.only(
top: 32.0,
bottom: 32.0
),
child: const Center(
child: Text('Recipes'),
),
),
Expanded(
child: BlocBuilder<RecipeCubit, RecipeState>(
builder: (context, state) {
if (state is RecipeLoading) {
return const Center(
child: CircularProgressIndicator(),
);
} else if (state is RecipeError) {
return const Center(
child: Icon(Icons.close),
);
} else if (state is RecipeLoaded) {
final recipes = state.recipes;
return ListView.builder(
itemCount: recipes.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) {
return BlocProvider<RecipeCubit>(
create: (context) => RecipeCubit(RecipeRepository()),
child: RecipeDetail(recipe: recipes[index]),
);
}
));
},
child: RecipeCardWidget(
title: recipes[index].title,
description: recipes[index].description,
),
);
},
);
} else {
return const Text('Loading recipes error');
}
}
),
),
],
),
Positioned(
bottom: 24.0,
right: 0.0,
child: FloatingActionButton(
heroTag: 'addBtn',
onPressed: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) {
return BlocProvider<RecipeCubit>(
create: (context) => RecipeCubit(RecipeRepository()),
child: const RecipeDetail(recipe: null),
);
}
));
},
child: const Icon(Icons.add_rounded),
backgroundColor: Colors.teal,
),
)
],
),
),
),
);
}
}
recipe_detail.dart
class RecipeDetail extends StatefulWidget {
final Recipe? recipe;
const RecipeDetail({Key? key, required this.recipe}) : super(key: key);
#override
_RecipeDetailState createState() => _RecipeDetailState();
}
class _RecipeDetailState extends State<RecipeDetail> {
final RecipeRepository recipeRepository = RecipeRepository();
final int _recipeId = 0;
late String _recipeTitle = '';
late String _recipeDescription = '';
final recipeTitleController = TextEditingController();
final recipeDescriptionController = TextEditingController();
late FocusNode _titleFocus;
late FocusNode _descriptionFocus;
bool _buttonVisible = false;
#override
void initState() {
if (widget.recipe != null) {
_recipeTitle = widget.recipe!.title;
_recipeDescription = widget.recipe!.description;
_buttonVisible = true;
}
_titleFocus = FocusNode();
_descriptionFocus = FocusNode();
super.initState();
}
#override
void dispose() {
recipeTitleController.dispose();
recipeDescriptionController.dispose();
_titleFocus.dispose();
_descriptionFocus.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 24.0
),
color: const Color(0xFFF6F6F6),
child: Stack(
children: [
Column(
children: [
Align(
alignment: Alignment.topLeft,
child: InkWell(
child: IconButton(
highlightColor: Colors.transparent,
color: Colors.black54,
onPressed: () {
Navigator.pop(context);
},
icon: const Icon(Icons.arrow_back_ios_new_rounded),
),
),
),
TextField(
focusNode: _titleFocus,
controller: recipeTitleController..text = _recipeTitle,
decoration: const InputDecoration(
hintText: 'Enter recipe title',
border: InputBorder.none
),
style: const TextStyle(
fontSize: 26.0,
fontWeight: FontWeight.bold
),
onSubmitted: (value) => _descriptionFocus.requestFocus(),
),
TextField(
focusNode: _descriptionFocus,
controller: recipeDescriptionController..text = _recipeDescription,
decoration: const InputDecoration(
hintText: 'Enter recipe description',
border: InputBorder.none
),
),
],
),
Positioned(
bottom: 24.0,
left: 0.0,
child: FloatingActionButton(
heroTag: 'saveBtn',
onPressed: () {
if (widget.recipe == null) {
Recipe _newRecipe = Recipe(
_recipeId,
recipeTitleController.text,
recipeDescriptionController.text
);
context.read<RecipeCubit>().createRecipe(_newRecipe);
//recipeRepository.createRecipe(_newRecipe);
Navigator.pop(context);
} else {
Recipe _newRecipe = Recipe(
widget.recipe!.id,
recipeTitleController.text,
recipeDescriptionController.text
);
context.read<RecipeCubit>().updateRecipe(_newRecipe);
//recipeRepository.updateRecipe(_newRecipe);
Navigator.pop(context);
}
},
child: const Icon(Icons.save_outlined),
backgroundColor: Colors.amberAccent,
),
),
Positioned(
bottom: 24.0,
right: 0.0,
child: Visibility(
visible: _buttonVisible,
child: FloatingActionButton(
heroTag: 'deleteBtn',
onPressed: () {
context.read<RecipeCubit>().deleteRecipe(widget.recipe!.id!);
//recipeRepository.deleteRecipe(widget.recipe!.id!);
Navigator.pop(context);
},
child: const Icon(Icons.delete_outline_rounded),
backgroundColor: Colors.redAccent,
),
),
),
],
),
),
),
);
}
}
recipe_state.dart
part of 'recipe_cubit.dart';
abstract class RecipeState extends Equatable {
const RecipeState();
}
class RecipeInitial extends RecipeState {
#override
List<Object> get props => [];
}
class RecipeLoading extends RecipeState {
#override
List<Object> get props => [];
}
class RecipeLoaded extends RecipeState {
final List<Recipe> recipes;
const RecipeLoaded(this.recipes);
#override
List<Object> get props => [recipes];
}
class RecipeError extends RecipeState {
final String message;
const RecipeError(this.message);
#override
List<Object> get props => [message];
}
recipe_cubit.dart
part 'recipe_state.dart';
class RecipeCubit extends Cubit<RecipeState> {
final RecipeRepository recipeRepository;
RecipeCubit(this.recipeRepository) : super(RecipeInitial()) {
getAllRecipes();
}
void getAllRecipes() async {
try {
emit(RecipeLoading());
final recipes = await recipeRepository.getAllRecipes();
emit(RecipeLoaded(recipes));
} catch (e) {
emit(const RecipeError('Error'));
}
}
void createRecipe(Recipe recipe) async {
await recipeRepository.createRecipe(recipe);
final newRecipes = await recipeRepository.getAllRecipes();
emit(RecipeLoaded(newRecipes));
}
void updateRecipe(Recipe recipe) async {
await recipeRepository.updateRecipe(recipe);
final newRecipes = await recipeRepository.getAllRecipes();
emit(RecipeLoaded(newRecipes));
}
void deleteRecipe(int id) async {
await recipeRepository.deleteRecipe(id);
final newRecipes = await recipeRepository.getAllRecipes();
emit(RecipeLoaded(newRecipes));
}
}
It looks like you're creating another BlocProvider when you're navigating to RecipeDetail page. When you're pushing new MaterialPageRoute, this new page gets additionally wrapped in new RecipeCubit. Then, when you're calling context.read<RecipeCubit>(), you're referencing that provider (as this is closest BlocProvider in the widget tree). Your RecipeList can't react to those changes because it's BlocBuilder is looking for a BlocProvider declared above it in the widget tree (the one in MyApp).
Besides that, newly created provider gets removed from the widget tree anyway when you're closing RecipeDetail page as it is declared in the MaterialPageRoute which has just been pushed off the screen.
Try to remove the additional BlocProvider (the one in RecipeList, in OnTap function of RecipeCardWidget):
onTap: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) {
return BlocProvider<RecipeCubit>( // remove this BlocProvider
create: (context) => RecipeCubit(RecipeRepository()),
child: RecipeDetail(recipe: recipes[index]),
);
}
));
},
Here I am using provider package for state management.
I have a SpeedDial widget in the floatingActionButton. And whenever I add something in the list mainGoalList by using speedDial and do Navigator.pop(context); it does go back to the page but does not update the list.
SpeedDial
SpeedDialChild(
child: Icon(Icons.book),
backgroundColor: Colors.blue,
label: 'Add Notes',
labelStyle: TextStyle(
fontSize: 18.0,
color: Colors.black,
),
onTap: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) => SingleChildScrollView(
child: Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context)
.viewInsets
.bottom),
child: AddNotes(),
),
));
}),
AddNote
Center(
child: FlatButton(
onPressed: () {
if (_actcontroller.text == null) {
print("Cannot add null topic");
} else {
addingTheNotes();
Navigator.pop(context);
}
},
child: Text("Add"),
color: Colors.blue,
),
)
Adding the notes
addingTheNotes() {
theDataProvider.ourAllnotes.add(
TodaysNoteClass(
note: _actcontroller.text,
dateTime: theDataProvider.notesChoosenDate,
status: false,
),
);
theDataProvider.showingTheTodaysList();
}
Here is the change notifier
List<TodaysNoteClass> _ourAllnotes = [];
List<TodaysNoteClass> get ourAllnotes => _ourAllnotes;
set ourAllnotes(List<TodaysNoteClass> val) {
_ourAllnotes = val;
notifyListeners();
}
Consumer class, this is where I am showing the notes
class HomePage extends StatefulWidget {
static const String id = 'homePage';
final String todaysDate =
DateFormat('d MMMM').format(DateTime.now()).toLowerCase();
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
var theDataProvider;
Widget build(BuildContext context) {
theDataProvider = Provider.of<TheData>(context, listen: false);
return Consumer<TheData>(
builder: (context, value, child) => SingleChildScrollView(
child: Container(
child: Column(
children: [
Container(
height: 120,
child: TodaysNote(),
),
],
),
),
),
);
}
}
And this is the TodaysNote class
class TodaysNote extends StatefulWidget {
TodaysNote({Key key}) : super(key: key);
bool boxChecked = false;
#override
_TodaysNoteState createState() => _TodaysNoteState();
}
class _TodaysNoteState extends State<TodaysNote> {
var theDataProvider;
Widget build(BuildContext context) {
theDataProvider = Provider.of<TheData>(context, listen: false);
return Consumer<TheData>(
builder: (context, value, child) => Container(
child: theDataProvider.showingTheTodaysList(),
),
);
}
}
But now when I go to another screen and then come to the previous screen. I see the updated list.
I have wrapped the main file with provider, wrapped the files with consumer but did not work.
What might be the reason behind this?
try using this:
Navigator.push( context, MaterialPageRoute( builder: (context) => SecondPage()), ).then((value) => setState(() {}));
I have an app with two tabs. One for the "all items" list and second for the "favourite/saved items". The second tab has a FAB and text written "Add your favorite items here" inside the children of the Column widget. So when the FAB is clicked, Navigator.push() works and triggers a second screen for "selecting favorite items" by the use of CheckBox widget. I've made an empty list _saved (its actually a Set to avoid duplicates) to store the items that are to be selected. And in the 'select favorite items screen' there is also a FAB, which when clicked, Navigator.pop() works and SHOULD RETURN THE _saved LIST. And this is the only problem I'm facing. I'm just not able to implement it.
Also as I mentioned above some text is written in the "Saved Items" tab, I want to build something like
"If items selected, just show the items and not the (before mentioned) Text! If none selected anything, just return the Text."
You guys can check the entire code here.
The code where I'm facing issues:
class SecondPage extends StatefulWidget {
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Add Your Favorite Sites Here!❤',
style: TextStyle(color: Colors.white),
),
Container(
child: Icon(Icons.favorite, size: 150, color: Colors.blue[100]),
),
SizedBox(height: 250),
FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FavoriteList(),
),
);
},
child: Icon(Icons.add),
foregroundColor: Colors.blue,
),
],
);
}
}
//The Favorite List Code:
final Set _saved = Set();
class FavoriteList extends StatefulWidget {
#override
_FavoriteListState createState() => _FavoriteListState();
}
class _FavoriteListState extends State<FavoriteList> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add to Favorites!'),
centerTitle: true,
backgroundColor: Colors.red),
// backgroundColor: Colors.indigo,
body: SafeArea(
child: ListView.builder(
itemCount: 53,
itemBuilder: (context, index) {
return CheckboxListTile(
activeColor: Colors.red,
checkColor: Colors.white,
// value: _saved.contains(context), // changed
value: _saved.contains(index),
onChanged: (val) {
setState(() {
// isChecked = val; // changed
// if(val == true){ // changed
// _saved.add(context); // changed
// } else{ // changed
// _saved.remove(context); // changed
// } // changed
if (val == true) {
_saved.add(index);
} else {
_saved.remove(index);
}
});
},
title: Row(
children: <Widget>[
Image.asset('lib/images/${images[index]}'),
SizedBox(
width: 10,
),
Text(nameOfSite[index]),
],
),
);
},
),
),
floatingActionButton: FloatingActionButton(
foregroundColor: Colors.red,
child: Icon(Icons.check),
onPressed: () {
Navigator.pop(context, _saved);
},
),
);
}
}
this is demo code, you can make your customize code using below code
class checkModel{
String nameOfSite;
bool isCheck;
checkModel(this.nameOfSite, this.isCheck);
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<checkModel> _list = new List();
#override
void initState() {
// TODO: implement initState
super.initState();
_list.add(checkModel("title1", false));
_list.add(checkModel("title2", false));
}
#override
Widget build(BuildContext context) {
SizeConfig().init(context);
return Scaffold(
body: ListView.builder(
itemCount: _list.length,
itemBuilder: (context, index) {
return CheckboxListTile(
activeColor: Colors.red,
checkColor: Colors.white,
// value: _saved.contains(context), // changed
value: _list[index].isCheck,
onChanged: (val) {
print("object ${val}");
setState(() {
_list[index].isCheck = val;
});
},
title: Text(_list[index].nameOfSite),
);
},
),
);
}
}
Replace:
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FavoriteList(),
),
);
With:
Navigator.push<Set>(
context,
MaterialPageRoute(
builder: (context) => FavoriteList(),
),
).then((Set _saved){
print(_saved);
});
And see logs, you have the Set of saved items.
Is it possible to prevent ModalBottomSheet to hide from outside touch? Like in showDialog() we can use barrierDismissible property to prevent dialog from closing on outside touch
you can use isDismissible: false and enableDrag: false like this
showModalBottomSheet(
isDismissible: false,
enableDrag: false,
builder: (context) {
return Container(
height: 100.0
)
}
);
You need to use showBottomSheet() which doesn't include barrier instead of using showModalBottomSheet().
More info here
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _scaffoldKey = new GlobalKey<ScaffoldState>();
VoidCallback _showPersBottomSheetCallback;
#override
void initState() {
super.initState();
_showPersBottomSheetCallback = _showBottomSheet;
}
void _showBottomSheet() {
setState(() {
_showPersBottomSheetCallback = null;
});
_scaffoldKey.currentState
.showBottomSheet((context) {
return new Container(
color: Colors.greenAccent,
height: 300.0,
child: new Center(
child: new Text("Hi BottomSheet"),
),
);
})
.closed
.whenComplete(() {
if (mounted) {
setState(() {
_showPersBottomSheetCallback = _showBottomSheet;
});
}
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
key: _scaffoldKey,
appBar: new AppBar(
title: new Text("Flutter BottomSheet"),
),
body: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {},
child: Padding(
padding: const EdgeInsets.only(top: 10.0),
child: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new RaisedButton(
onPressed: _showPersBottomSheetCallback,
child: new Text("Persistent"),
),
],
),
),
),
),
);
}
}
Try setting isDismissible to false inside showModalBottomSheet
showModalBottomSheet(
isDismissible: false,
context: context,
builder: (BuildContext bc) {
return SheetWidget();
},
);
where SheetWidget is the widget you are trying to call as BottomSheet