Flutter popupmenubutton isn't closed until select any popup item - flutter

Everything works fine but popupmenubutton isn't closed until any popup item is selected. I do not understand why the popup menu does not close after clicking outside the popup menu and onCanceled does not called.
Please help me.
I have provided the source code below. Thanks.
//Call from Here
StatefulWidget> Scafold(
bottomNavigation,
TabBar
body: widgets>
//under tabBar
child: callAction(
tooltip: "Call Button",
child: Container(
height: double.infinity,
padding: EdgeInsets.all(20),
child: Icon(Icons.call)
),
),
)
//PopupMenuButton widget
enum CallActionType { DataCall, Sim1Call, Sim2Call}
class callAction extends StatelessWidget {
Widget child;
String tooltip;
callAction({#required this.child, #required this.tooltip});
PopupMenuButton<CallActionType>(
elevation: 8,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0)),
onSelected: (CallActionType value) {
setState(() {
print(value);
});
},
//onCanceled didn't call
onCanceled: () {
print('You have not chossed anything');
},
tooltip: widget.tooltip,
offset: Offset(0, 100),
child: widget.child,
itemBuilder: (BuildContext context) => <PopupMenuEntry<CallActionType>>[
new PopupMenuItem<CallActionType>(
value: CallActionType,
child: Text('Action 1'),
),
new PopupMenuItem<CallActionType>(
value: CallActionType,
child: Text('Action 2'),
),
new PopupMenuItem<CallActionType>(
value: CallActionType,
child: Text('Action 3'),
),
],
);

PopupMenu will close smoothly, please see below code for example:
Widget popupMenuButton(){
return PopupMenuButton<String>(
elevation: 50,
padding: EdgeInsets.fromLTRB(5, 0, 0, 0),
icon: Icon(Icons.keyboard_arrow_down, size: 30, color: Colors.black),
itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
PopupMenuItem<String>(
value: "One_Val",
child: Text("One_Val"),
),
PopupMenuItem<String>(
value: "Two_Val",
child: Text("Two_Val"),
),
PopupMenuItem<String>(
value: "Three_Val",
child: Text("Three_Val"),
)
],
onSelected: (String value) {
setState(() {
companyName = value;
});
},
);}
Please let me know for any query, Thanks.

Related

saving the State of the dropdownbuttons in flutter and exporting to excel

hello guys i'm a beginner on flutter and i'm having a little issue with keeping the changed values of dropdownbuttons alive ?? is there anyway to keep the result when the users changes the screen ?? as shown on the gif below .
and the code :
body: Column(
children: <Widget>[
Flexible(
child: ListView.builder(
itemCount: propositions.length,
itemExtent: 50.0,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text("${propositions[index]}"),
trailing: _dropdown(index),
);
},
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () => (null),
backgroundColor: Color.fromRGBO(82, 113, 255, 25),
child: const Icon(Icons.save),
),
bottomNavigationBar: BottomAppBar(
color: Color.fromRGBO(82, 113, 255, 25),
child: Container(height: 40.0),
shape: CircularNotchedRectangle(),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
Widget _dropdown(int index) {
return DropdownButton<String>(
isExpanded: false,
value: selectedItemValue[index].toString(),
items: _dropDownItem(),
onChanged: (value) {
setState(() {
selectedItemValue[index] = value as String;
});
},
);
}
List<DropdownMenuItem<String>> _dropDownItem() {
List<String> ddl = ["Not Done", "Partially Done", "Done"];
return ddl
.map((value) => DropdownMenuItem(
value: value,
child: Text(value),
))
.toList();
}
}
and i'm initiliazing the list with :
class _Check_listsState extends State<Check_lists> {
String dropdownValue = "Not Done";
List<String> selectedItemValue = <String>[];
#override
void initState() {
super.initState();
selectedItemValue.addAll(List.filled(propositions.length, "Not Done"));
}
and when i tried to use AutomaticKeepAliveClientMixin my app crashes , thnks

gestureEvent or PointerEvent on longPressed DialogBox

How to add tap or longpress (any gestureDector event) on longpressed DialogBox
clicking on icons on popop after showing that, can we swipe on icons and getting action like with click?
hear i'am, using "OverlyEntery" to show dialog, iam using overlyEntry because it will keep the gesture longpress event, it's work fine when longpress show the Dialog and disappear when longpressEnd event occurred, but the problem is after open a dialog Box,
in Dialog box i was not able to add any event, hear i want like instagram image preview.
Thank you.
import 'package:flutter/material.dart';
class Explore extends StatelessWidget {
Explore({Key? key}) : super(key: key);
OverlayEntry _popUp({required String url}) {
return OverlayEntry(
builder: (context) => Dialog(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: double.infinity,
padding: EdgeInsets.all(10.0),
color: Colors.black,
child: Center(
child: Text(
"Trip Body name",
style: TextStyle(color: Colors.white),
)),
),
Image.asset(url),
Container(
color: Colors.black,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
GestureDetector(
onLongPress: () {
print("press1");
},
onLongPressCancel: () {
print("press2");
},
onLongPressDown: (e) {
print("press3");
},
onLongPressMoveUpdate: (d) {
print("press4");
},
child: Container(
child: const Icon(
Icons.favorite,
color: Colors.white,
)),
),
Icon(Icons.save)
],
),
)
],
)));
}
OverlayEntry? _popUpDialog;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: StaggeredGridView.countBuilder(
// controller: _scrollController,
crossAxisCount: 2,
itemCount: 50,
itemBuilder: (BuildContext context, int index) => InkWell(
onTap: () {
},
child: GestureDetector(
// behavior: HitTestBehavior.deferToChild,
onLongPress: () {
_popUpDialog = _popUp(
url: 'assets/images/a' +
((index < 15)
? index.toString()
: index > 20
? '3'
: (index - 10).toString()) +
'.jpeg');
Overlay.of(context)!.insert(_popUpDialog!);
},
onLongPressEnd: (d) {
_popUpDialog!.remove();
},
child: Image.asset((
'assets/images/a' +
((index < 15)
? index.toString()
: index > 20
? '3'
: (index - 10).toString()) +
'.jpeg'
)),
),
staggeredTileBuilder: (int index) => StaggeredTile.count(
1, isForReel(index: index) ? 1.5 : 1), // 2,11,23,30,44
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
),
),
)
]
)
);
}
}
Try to add Gesture Detecter on body of DialogBox rather than SubViews.

Return variable from current screen to previous screen

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!

Passing value to previous widget

I have simple form , inside it have CircularAvatar when this is pressed show ModalBottomSheet to choose between take picture from gallery or camera. To make my widget more compact , i separated it to some file.
FormDosenScreen (It's main screen)
DosenImagePicker (It's only CircularAvatar)
ModalBottomSheetPickImage (It's to show ModalBottomSheet)
The problem is , i don't know how to passing value from ModalBottomSheetPickImage to FormDosenScreen. Because value from ModalBottomSheetPickImage i will use to insert operation.
I only success passing from third Widget to second Widget , but when i passing again from second Widget to first widget the value is null, and i think the problem is passing from Second widget to first widget.
How can i passing from third Widget to first Widget ?
First Widget
class FormDosenScreen extends StatefulWidget {
static const routeNamed = '/formdosen-screen';
#override
_FormDosenScreenState createState() => _FormDosenScreenState();
}
class _FormDosenScreenState extends State<FormDosenScreen> {
String selectedFile;
#override
Widget build(BuildContext context) {
final detectKeyboardOpen = MediaQuery.of(context).viewInsets.bottom;
print('trigger');
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Tambah Dosen'),
actions: <Widget>[
PopupMenuButton(
itemBuilder: (_) => [
PopupMenuItem(
child: Text('Tambah Pelajaran'),
value: 'add_pelajaran',
),
],
onSelected: (String value) {
switch (value) {
case 'add_pelajaran':
Navigator.of(context).pushNamed(FormPelajaranScreen.routeNamed);
break;
default:
}
},
)
],
),
body: Stack(
fit: StackFit.expand,
children: <Widget>[
SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
SizedBox(height: 20),
DosenImagePicker(onPickedImage: (file) => selectedFile = file),
SizedBox(height: 20),
Card(
margin: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
TextFormFieldCustom(
onSaved: (value) {},
labelText: 'Nama Dosen',
),
SizedBox(height: 20),
TextFormFieldCustom(
onSaved: (value) {},
prefixIcon: Icon(Icons.email),
labelText: 'Email Dosen',
keyboardType: TextInputType.emailAddress,
),
SizedBox(height: 20),
TextFormFieldCustom(
onSaved: (value) {},
keyboardType: TextInputType.number,
inputFormatter: [
// InputNumberFormat(),
WhitelistingTextInputFormatter.digitsOnly
],
prefixIcon: Icon(Icons.local_phone),
labelText: 'Telepon Dosen',
),
],
),
),
),
SizedBox(height: kToolbarHeight),
],
),
),
Positioned(
child: Visibility(
visible: detectKeyboardOpen > 0 ? false : true,
child: RaisedButton(
onPressed: () {
print(selectedFile);
},
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
color: colorPallete.primaryColor,
child: Text(
'SIMPAN',
style: TextStyle(fontWeight: FontWeight.bold, fontFamily: AppConfig.headerFont),
),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
textTheme: ButtonTextTheme.primary,
),
),
bottom: kToolbarHeight / 2,
left: sizes.width(context) / 15,
right: sizes.width(context) / 15,
)
],
),
);
}
}
Second Widget
class DosenImagePicker extends StatefulWidget {
final Function(String file) onPickedImage;
DosenImagePicker({#required this.onPickedImage});
#override
DosenImagePickerState createState() => DosenImagePickerState();
}
class DosenImagePickerState extends State<DosenImagePicker> {
String selectedImage;
#override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.center,
child: InkWell(
onTap: () async {
await showModalBottomSheet(
context: context,
builder: (context) => ModalBottomSheetPickImage(
onPickedImage: (file) {
setState(() {
selectedImage = file;
widget.onPickedImage(selectedImage);
print('Hellooo dosen image picker $selectedImage');
});
},
),
);
},
child: CircleAvatar(
foregroundColor: colorPallete.black,
backgroundImage: selectedImage == null ? null : MemoryImage(base64.decode(selectedImage)),
radius: sizes.width(context) / 6,
backgroundColor: colorPallete.accentColor,
child: selectedImage == null ? Text('Pilih Gambar') : SizedBox(),
),
),
);
}
}
Third Widget
class ModalBottomSheetPickImage extends StatelessWidget {
final Function(String file) onPickedImage;
ModalBottomSheetPickImage({#required this.onPickedImage});
#override
Widget build(BuildContext context) {
return SizedBox(
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Wrap(
alignment: WrapAlignment.spaceEvenly,
children: <Widget>[
InkWell(
onTap: () async {
final String resultBase64 =
await commonFunction.pickImage(quality: 80, returnFile: ReturnFile.BASE64);
onPickedImage(resultBase64);
},
child: CircleAvatar(
foregroundColor: colorPallete.white,
backgroundColor: colorPallete.green,
child: Icon(Icons.camera_alt),
),
),
InkWell(
onTap: () async {
final String resultBase64 =
await commonFunction.pickImage(returnFile: ReturnFile.BASE64, isCamera: false);
onPickedImage(resultBase64);
},
child: CircleAvatar(
foregroundColor: colorPallete.white,
backgroundColor: colorPallete.blue,
child: Icon(Icons.photo_library),
),
),
],
),
),
);
}
}
The cleanest and easiest way to do this is through Provider. It is one of the state management solutions you can use to pass values around the app as well as rebuild only the widgets that changed. (Ex: When the value of the Text widget changes). Here is how you can use Provider in your scenario:
This is how your model should look like:
class ImageModel extends ChangeNotifier {
String _base64Image;
get base64Image => _base64Image;
set base64Image(String base64Image) {
_base64Image = base64Image;
notifyListeners();
}
}
Don't forget to add getters and setters so that you can use notifyListeners() if you have any ui that depends on it.
Here is how you can access the values of ImageModel in your UI:
final model=Provider.of<ImageModel>(context,listen:false);
String image=model.base64Image; //get data
model.base64Image=resultBase64; //set your image data after you used ImagePicker
Here is how you can display your data in a Text Widget (Ideally, you should use Selector instead of Consumer so that the widget only rebuilds if the value its listening to changes):
#override
Widget build(BuildContext context) {
//other widgets
Selector<ImageModel, String>(
selector: (_, model) => model.base64Image,
builder: (_, image, __) {
return Text(image);
},
);
}
)
}
You could achieve this easily. If you are using Blocs.

How to toggle in Flutter Stateless widget

I want to be able to select one item from a list, and when the item is clicked on, the check signed be toggled to checked. I also want to make sure, a user can select just one item from the list at a time.
Here is my recipient card:
class RecipientCard extends StatelessWidget {
const RecipientCard({Key key, this.recipient}) : super(key: key);
final recipient;
#override
Widget build(BuildContext context) {
bool selected = false;
return Card(
child: Row(
children: <Widget>[
Container(
decoration: new BoxDecoration(
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(4.0),
bottomLeft: const Radius.circular(4.0),
),
),
width: 40.0,
height: 50.0,
// Should be able to toggle the icons here
child: selected ?
IconButton(
icon: Icon(Icons.check),
onPressed: () {
selected = false;
},
) :
IconButton(
icon: Icon(Icons.check_box_outline_blank) ,
onPressed: () {
selected = true;
print(selected);
},
),
),
Expanded(
child: Container(
padding: EdgeInsets.all(10.0),
child: Text.rich(
TextSpan(children: [
TextSpan(text: '${recipient.recipientName}:', style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold)),
TextSpan(text: ' ${recipient.recipientCity} ${recipient.recipientCountry}, Number: ${recipient.recipientPhone}, ${recipient.recipientBank} ${recipient.receiveVia} ',)
]),
style: TextStyle(
fontSize: 14.0,
),
),
),
),
],
),
);
}
}
I call that in a listbuilder as:
return ListView.builder(
shrinkWrap: true,
itemCount: recipients.length,
itemBuilder: (BuildContext context, int index) {
Recipient recipient = recipients[index];
return Dismissible(
key: Key(recipient.id),
onDismissed: (DismissDirection direction) {
removeRecipient(recipient, state);
},
child: RecipientCard(recipient: recipient),
background: Container(color: Colors.red),
);
},
);
How can I achieve this? Thank you
The parent must be responsible for selecting. The child must know whether it is selected or not, and notify the parent when it gets selected.
Try this:
class RecipientCard extends StatelessWidget {
const RecipientCard({
Key key,
this.recipient,
this.selected,
this.onSelect,
}) : super(key: key);
final Recipient recipient;
final bool selected;
final VoidCallback onSelect;
#override
Widget build(BuildContext context) {
return Card(
child: Row(
children: <Widget>[
Container(
...
child: selected
? IconButton(
icon: Icon(Icons.check),
onPressed: onSelect,
)
: IconButton(
icon: Icon(Icons.check_box_outline_blank),
onPressed: onSelect,
),
...
),
],
),
),
);
// this variable must be in your class' scope
int selectedIndex;
...
return ListView.builder(
shrinkWrap: true,
itemCount: recipients.length,
itemBuilder: (BuildContext context, int index) {
Recipient recipient = recipients[index];
return Dismissible(
key: Key(recipient.id),
onDismissed: (DismissDirection direction) {
removeRecipient(recipient, state);
},
child: RecipientCard(
recipient: recipient,
selected: selectedIndex == index,
onSelect: () {
setState(() => selectedIndex = index);
},
),
background: Container(color: Colors.red),
);
},
);