how can i controll the default pop property of bottom sheet.Like I want to asign a value to a variable when showModalBottomSheet is popped .I have tried to do with controllers
Why don't you just do :
showModalBottomSheet(
context: context,
builder: (context) {
var a = "desired value";
return Widget;
you can trigger when the bottom sheet is popped/dismissed with an AnimationController like this:
in your StatefulWidget's State:
late AnimationController _controller;
#override
void initState() {
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 300),
);
_controller.addListener(() {
if (_controller.isDismissed) {
print("dismissed");
}
});
super.initState();
}
#override
void dispose() {
_controller.dispose;
super.dispose();
}
in your showModalBottomSheet:
showModalBottomSheet(
context: context,
builder: (context) => Container(),
transitionAnimationController: _controller, // assign the controller
);
You can set isDismissible: false, and than add one button (Close button) on tap of button, you have to do your code and pop the bottomSheet.
showModalBottomSheet(
isScrollControlled: true,
isDismissible: false,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(15),
),
),
context: context,
builder: (context) {
return SizedBox(
height:
MediaQuery.of(context).size.height * (0.6),
child: Padding(
padding: const EdgeInsets.only(top: 15),
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: () {
// Add your code here. which you want to perform before closing bottomSheet
Navigator.pop(context);
},
child: const Icon(Icons.close)),
InkWell(
onTap: () {},
child: const Text(
"Reset",
)),
],
),
const SizedBox(height: 15),
//Other widgets of bottomSheet
Container(
height:
MediaQuery.of(context).size.height * (0.5),
color: Colors.amber,
)
],
),
),
);
});
thanks for your help
I solved the problem with WillPopScope
popfunction() {
SelectedValue = tempValue;
Navigator.pop(context, true);
}
onWillPop: () async {
return popfunction() ?? false;
},
Related
I'm new to Flutter and I'm trying to integrate an SVG loading spinner into a dialog box. I understand I would need the dialog box to be stateful but I can't seem to wrap my head around the logic.
Here's what I've tried so far
loadingDialog(BuildContext context) {
late final SvgController controller;
#override
void initState() {
// Initialize SvgController
controller = AnimatedSvgController();
super.initState(); //this throws an error
}
#override
void dispose() {
// Dispose SvgController
controller.dispose();
super.dispose(); //this throws an error
}
// set up the AlertDialog
AlertDialog alert = AlertDialog(
backgroundColor: Colors.transparent,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
content: Container(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
AnimatedSvg(
controller: controller,
duration: const Duration(milliseconds: 600),
onTap: (() {}),
size: 20,
clockwise: true,
children: [
SvgPicture.asset(
'assets/Spinner.svg',
color: Colors.white,
),
],
),
],
),
));
// show the dialog
showDialog(
context: context,
barrierColor: const Color.fromRGBO(2, 22, 52, 0.75),
builder: (BuildContext context) {
return alert;
},
);
}
I'm using the animated_svg: ^1.1.2 package
I have a DraggableScrollableSheet in which I have two buttons Yes and No. If the user clicked on yes then the spinner is shown and if the function sendEmail returns true then a new DraggableScrollableSheet will be shown.
My problem is when I do a setState for the variable isLoading while clicking on Yes the DraggableScrollableSheet is closed then if the email is send DraggableScrollableSheet will be shown. I don't know why the first one is closed. When I removed the set State the spinner is not shown at all!
InkWell(
onTap: () {
print(isLoading);
// setState(() {
//isLoading = true;
//});
sendEmail(widget.measure).then((sendEmail) {
if (sendEmail == true) {
// setState(() {
// isLoading = false;
// });
print(isLoading);
newTransmissionController.reset();
transmissionDoneController.animateTo(
0.95,
duration:
const Duration(milliseconds: 100),
curve: Curves.easeOutBack,
);
} else {
// setState(() {
isLoading = false;
// });
Navigator.of(context).pop();
Navigator.of(context).push(PageRouteBuilder(
pageBuilder: (context, animation1,
animation2) =>
const TransmissionErrorPage()));
}
});
},
child: AnimatedContainer(
duration:
const Duration(milliseconds: 400),
height:
MediaQuery.of(context).size.height *
0.08,
margin: const EdgeInsets.symmetric(
horizontal: 5),
width:
MediaQuery.of(context).size.height *
0.18,
decoration: BoxDecoration(
color: const Color(0xFF008DFF),
borderRadius: BorderRadius.circular(10),
),
child: Center(
child: !isLoading
? const Text(
"Yes",
style: TextStyle(
color: Colors.white,
fontSize: 17,
fontFamily: 'SFProRegular',
),
)
: const Center(
child:
CircularProgressIndicator(
backgroundColor: Colors.white,
valueColor:
AlwaysStoppedAnimation<
Color>(
Color(0xFF008DFF)),
),
),
)),
),
newTransmissionController and transmissionDoneController are DraggableScrollableController.
DraggableScrollableController newTransmissionController =
DraggableScrollableController();
DraggableScrollableController transmissionDoneController =
DraggableScrollableController();
Wrap DraggableScrollableSheet with StatefulBuilder and use its setstate to update the bottomSheet UI.
showModalBottomSheet(
context: context,
builder: (c) {
return StatefulBuilder(
builder: (context, setStateSB) => DraggableScrollableSheet(
builder: (context, scrollController) {
/// use `setStateSB` to update inside dialog
/// use `setState` to update state class UI
},
),
);
});
So I am implementing a 'settings' view in my Flutter app. The idea is all settings will appear in a ListView, and when the user will click on a ListTile, a showModalBottomSheet will pop where the user will be able to manipulate the setting. The only problem I am having is I am unable to migrate the showModalBottomSheet to a separate class as I cannot make the new function (outside the class) return the manipulated setting variable. This has lead to a messy code, all in a single class.
class Page extends StatefulWidget {
Page({Key key}) : super(key: key);
#override
_Page createState() => _Page();
}
class _Page extends State<Page> {
var value;
#override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
ListTile(
title: Text("Age"),
trailing: Text(value),
onTap: () {
setState(() {
value = _valueSelector(); // This doesn't work, but to give an idea what I want
});
},
),
],
);
}
}
int _valueSelector(context) { // Doesn't return
var age = 0;
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Wrap(
children: [
Column(
children: <Widget>[
Slider(
value: age.toDouble(),
min: 0,
max: 18,
divisions: 18,
onChanged: (value) {
setState(() {
age = value.toInt();
});
},
),
],
),
],
);
});
},
).whenComplete(() {
return age; // Not sure if return is supposed to be here
});
}
How can I implement showModalBottomSheet in a separate class and just make it return the variable representing the setting chosen by the user?
You can try the below code,
First, create a class custom_bottom_sheet.dart and add the below code. You can use it everywhere in the project. And also use this library modal_bottom_sheet: ^0.2.0+1 to get the showMaterialModalBottomSheet.
customBottomSheet(BuildContext context, {#required Widget widget}) async {
return await showMaterialModalBottomSheet(
context: context,
backgroundColor: AppColors.transparent_100,
barrierColor: AppColors.black_75,
isDismissible: false,
enableDrag: true,
builder: (_, ScrollController scrollController) {
return widget;
},
);
}
Sample example code:
Create another class called bottom_sheet_example.dart and add the below code.
class BottomSheetExample {
static Future getSheet(BuildContext _context,
{ValueChanged<bool> onChanged}) async {
await customBottomSheet(
_context,
widget: SafeArea(
child: Container(
padding: EdgeInsets.only(left: 40.0, right: 40.0),
height: 170.0,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(27.0),
topRight: Radius.circular(27.0))),
child: Container(
padding: EdgeInsets.only(top: 32),
child: Column(
children: [
Text("Were you at Queen Victoria Building?"),
SizedBox(height: 48),
Row(
children: [
Expanded(
child: RaisedButton(
child: Text("No"),
onPressed: () {
Navigator.of(_context).pop();
onChanged(false);
},
),
),
SizedBox(width: 18),
Expanded(
child: RaisedButton(
child: Text("Yes"),
onPressed: () {
Navigator.of(_context).pop();
onChanged(true);
},
),
),
],
),
SizedBox(height: 24),
],
),
),
)),
);
}
}
Button click to show the bottom sheet
#override
Widget build(BuildContext context) {
return Scaffold(
body: yourBodyWidget(),
bottomNavigationBar: Container(
height: 40,
width: double.infinity,
child: FlatButton(
onPressed: () {
/// call BottomSheetExample class
BottomSheetExample.getSheet(
context,
onChanged: (bool result) async {
///
/// add your code
},
);
},
child: Text("show bottom sheet")),
),
);
}
In onChanged callback you can return your value(obj/String/num/bool/list).
Thank you!
I am working on a card game and it includes 2 parts.
InputPage
GamePage
In InputPage() user picks cards and it has a new game button. When user click on it, page must be reload. I did this with Navigator.of method. But when user go to GamePage(), i got an error like this:
Unhandled Exception: setState() called after dispose(): _GamePageState#d4518(lifecycle state: defunct, not mounted)
This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
This is my code:
ayarAlert2(BuildContext context) async {
AlertDialog alert = AlertDialog(
title: Center(
child: Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(
Radius.circular(15.0),
),
),
child: Text(
" Settings ",
style: TextStyle(color: Colors.white, fontWeight: FontWeight.w300),
),
),
),
backgroundColor: Color(0xFF1F010B),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15)),
content: Container(
child: Wrap(
runSpacing: 5,
spacing: 10,
children: <Widget>[
FlatButton(
onPressed: () {
Navigator.pop(context);
Navigator.of(context).push(new MaterialPageRoute(
builder: (BuildContext context) => InputPage()));
},
child: Center(
child: Row(
children: [Container(
child: Text(
" New Game ",
style: TextStyle(
color: Colors.white,
fontSize: 32,
fontWeight: FontWeight.w300),
)),
],
),
)),
],
),
),
);
// show the dialog
showDialog(
barrierColor: Colors.black.withOpacity(0.01),
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
When user picks all the cards ,he/she press the " START " button. Heres the start button :
Navigator.of(context).pushReplacement(
new MaterialPageRoute(
builder: (BuildContext context) =>
new GamePage(
mycards: cardBrain.mycards,
yourcards:
cardBrain.rakipcards,
annen: true,
)));
This is InputPage() 's initState
#override
void initState() {
super.initState();
Future.delayed(Duration.zero, () {
showDialog(
context: context,
builder: (context) => AlertDialog(
content: Container(
child: Wrap(
alignment: WrapAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FlatButton(
onPressed: () {
setState(() {
cardBrain.kartbol();
Navigator.pop(context);
});
},
child: Center(
child: Row(
children: [
Center(
child: Container(
child: Text(
" START ",
)),
),
],
),
)),
],
),
],
),
),
));
});
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
// animationController.dispose() instead of your controller.dispose
}
And this is GamePage() 's initState :
#override
void initState() {
super.initState();
WidgetsBinding.instance
.addPostFrameCallback((_) => showAlertDialog(context));
_now = DateTime.now().second.toString();
// defines a timer
_everySecond = Timer.periodic(Duration(seconds: 1), (Timer t) {
if (annenn == true) {
setState(() {
_now = DateTime.now().second.toString();
if (cardBrain.bitir == 13) {
if (ihale <= cardBrain.bizimskor) {
mesaj = "WIN";
} else {
mesaj = "LOSE";
}
sonucAlert(context, cardBrain.bizimskor, mesaj, cardBrain.onunskor,
widget.annen);
}
});
}
});
}
A solution would be to use the popAndPushNamed method:
await Navigator.popAndPushNamed(context, '/gameScreen');
Remember to define the route in the MaterialApp class, usually in the main.dart file:
MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => InputPage(),
'/gameScreen': (context) => GamePage(),
},
);
what is the proper way to call Dialog function from another class.
I have been searching this topic for a while but seems none of them are my answer.
my Dialog has a little complicated logic for server communicating and some paginations
so this code is going to be long for just one dart file. so I want to separate them.
and I need the some dialog animations so I picked the showGeneralDialog()
I also saw the example dialog implementaion using StatefulBuilder() which can use setState,
but this problem is it is not able to use initState()
for now, what I did is below
dart1 file
import 'package:aaa/bbb/some_dialog_file.dart'
as someDialog;
GestureDetector(
onTap: () async{
var result =
await someDialog.displayDialogOKCallBack(
context,
);
},
child: Container(
width: 60,
height: 60,
child: Icon(
Icons.comment,
size: 38,
),
),
)
dart2 file
Future<dynamic> displayDialogOKCallBack(BuildContext context) async {
return await showGeneralDialog(
barrierLabel: "Label",
barrierDismissible: true,
// barrierColor: ,
transitionDuration: Duration(milliseconds: 400),
context: context,
pageBuilder: (context, anim1, anim2) {
return StatefulBuilder(builder: (context, setState) {
return Scaffold(
body: SafeArea(
),
);
});
},
transitionBuilder: (context, anim1, anim2, child) {
return SlideTransition(
position:
Tween(begin: Offset(0, 1), end: Offset(0, -0.02)).animate(anim1),
child: child,
);
},
);
}
so my question is I want to build very clean animation dialog
which is logically separated from base class file and it has to have initState(), and setState()
how could I acheive this ? thanks
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Container(
child: RaisedButton(
onPressed: () {
someDialog(context);
},
child: Text("click"),
),
);
}
Future<dynamic> someDialog(BuildContext context) async {
return await showGeneralDialog(
barrierLabel: "Label",
barrierDismissible: true,
context: context,
pageBuilder: (context, anim1, anim2) {
return Scaffold(
backgroundColor: Colors.transparent,
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
// List
AnotherClassDialog(),
],
),
],
),
),
),
);
});
}
}
class AnotherClassDialog extends StatefulWidget {
#override
_AnotherClassDialogState createState() => _AnotherClassDialogState();
}
class _AnotherClassDialogState extends State<AnotherClassDialog> {
Color color;
#override
void initState() {
// TODO: implement initState
super.initState();
color = Colors.black;
}
#override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
RaisedButton(
onPressed: () {
setState(() {
color = Colors.red;
});
},
),
Container(
width: 100,
height: 100,
color: color,
),
RaisedButton(
onPressed: () {
setState(() {
color = Colors.green;
});
},
)
],
),
);
}
}
I use a custom dialog in my app in some classes and had the same problem.
You should define a dialog and pass context and other variables to it and call it everywhere you want.
You can define a dialog like this :
showCustomDialog(BuildContext context, String title, String description) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
title,
textAlign: TextAlign.right,
),
content: SingleChildScrollView(
child: Text(
description,
style: Theme.of(context).textTheme.bodyText1,
textAlign: TextAlign.right,
),
),
actions: [
FlatButton(
child: Text(
'ok',
style: Theme.of(context).textTheme.bodyText2.copyWith(
color: Theme.of(context).accentColor,
),
),
onPressed: () => Navigator.of(context).pop(),
),
],
actionsPadding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
);
});
}
and use it everywhere you want like this :
InkWell(
child: Icon(
Icons.error_outline,
size: 17,
),
onTap: () => showCustomDialog(context,"text1" , "text2") ,
),
I hope my answer will help you.