How to automatically deactivate the previous button when clicking on another button? - flutter

I have 3 buttons. When you press the button, it turns purple. Only one button can be selected (activated). But now when one button is selected and I want to select another button, I need to first click on the same button (the previous one) and then click on the other button (the new one). How can I make it so that when you click on another button, it toggles, and the previous button is deactivated automatically?
Padding(
padding: const EdgeInsets.symmetric(horizontal: 21),
child: Row(
children: [
GestureDetector(
onTap: () => setState(() {
isVoltageAC = !isVoltageAC;
}),
child: _buttonVoltage('AC', isVoltageAC),
),
const SizedBox(width: 16),
GestureDetector(
onTap: () => setState(() {
isVoltageDC = !isVoltageDC;
}),
child: _buttonVoltage('DC', isVoltageDC),
),
const SizedBox(width: 16),
GestureDetector(
onTap: () => setState(() {
isVoltageAll = !isVoltageAll;
}),
child: _buttonVoltage('All', isVoltageAll),
),
],
),
),
Widget _buttonVoltage(String nameButton, bool isActive) => Container(
padding: const EdgeInsets.symmetric(vertical: 11),
height: 40,
width: 87,
decoration: BoxDecoration(
color: isActive
? constants.Colors.purpleMain
: constants.Colors.white.withOpacity(0.15),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: isActive ? Colors.transparent : constants.Colors.greyDark,
),
boxShadow: [
BoxShadow(
color: isActive
? constants.Colors.purpleMain.withOpacity(0.34)
: Colors.transparent,
blurRadius: 10,
spreadRadius: 2,
offset: const Offset(0.0, 1.0)),
],
),
alignment: Alignment.center,
child:
Text(nameButton, style: constants.Styles.smallBoldTextStyleWhite),
);

I think it will be better to use enum here
enum VoltageMode {
ac,
dc,
all,
none, //not using
}
VoltageMode? selectedMode;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 21),
child: Row(
children: [
GestureDetector(
onTap: () => setState(() {
selectedMode = VoltageMode.ac;
}),
child: _buttonVoltage('AC', selectedMode == VoltageMode.ac),
),
const SizedBox(width: 16),
GestureDetector(
onTap: () => setState(() {
selectedMode = VoltageMode.dc;
}),
child: _buttonVoltage('DC', selectedMode == VoltageMode.dc),
),
const SizedBox(width: 16),
GestureDetector(
onTap: () => setState(() {
selectedMode = VoltageMode.all;
}),
child: _buttonVoltage('All', selectedMode == VoltageMode.all),
),
],
),
),
],
),
);
}
}

make int variable and call it selectedIndex and pass value when click on each button, for example when select 1 button set
GestureDetector(
onTap: () => setState(() {
isVoltageAC = !isVoltageAC;
selectedIndex = 1;
}),
child: _buttonVoltage('AC', isVoltageAC),
),
then pass this specific number to your _buttonVoltage widget and check it if it is equal to selectedIndex or not, if it is consider as active button, like this:
Widget _buttonVoltage(String nameButton, int index, int _selectedIndex) => Container(
padding: const EdgeInsets.symmetric(vertical: 11),
height: 40,
width: 87,
decoration: BoxDecoration(
color: _selectedIndex == index
? constants.Colors.purpleMain
: constants.Colors.white.withOpacity(0.15),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: isActive ? Colors.transparent : constants.Colors.greyDark,
),
boxShadow: [
BoxShadow(
color: _selectedIndex == index
? constants.Colors.purpleMain.withOpacity(0.34)
: Colors.transparent,
blurRadius: 10,
spreadRadius: 2,
offset: const Offset(0.0, 1.0)),
],
),
alignment: Alignment.center,
child:
Text(nameButton, style: constants.Styles.smallBoldTextStyleWhite),
);

Related

Flutter : How to select image from a list

I'm stuck on a small point, I'm developing a self-help service application and I take care of creating the form to create an announcement.
For that I thought it a great idea to set it up as a Cupertino Stepper, but when I want the user to choose an image from a list I give, I don't know how to make an image selectable. Here is a picture of what I have so far (left) and what I would like (right):
Comparison between what I have and what I want
And here is the code of the page I have:
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: [
Stepper(
currentStep: currentStep,
onStepTapped: (index) {
setState(() => currentStep = index);
},
onStepContinue: () {
if (currentStep != 3) {
setState (() => currentStep++);
}
},
onStepCancel: () {
if (currentStep != 0) {
setState (() => currentStep--);
}
},
steps: [
Step(
isActive: currentStep >= 0,
title: Text('Choisissez une catégorie'),
content: CupertinoPageScaffold(
child: DefaultTextStyle(
style: TextStyle(
color: CupertinoColors.label.resolveFrom(context),
fontSize: 22.0,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('Catégorie séléctionnée : ', style: TextStyle(fontSize: 14),),
CupertinoButton(
padding: EdgeInsets.zero,
// Display a CupertinoPicker with list of fruits.
onPressed: () => _showDialog(
CupertinoPicker(
magnification: 1.22,
squeeze: 1.2,
useMagnifier: true,
itemExtent: _kItemExtent,
onSelectedItemChanged: (int selectedItem) {
setState(() {
_selectedCategorie = selectedItem;
});
},
children:
List<Widget>.generate(_CategorieNames.length, (int index) {
return Center(
child: Text(
_CategorieNames[index],
),
);
}),
),
),
// This displays the selected fruit name.
child: Text(
_CategorieNames[_selectedCategorie],
),
),
],
),
),
)
),
Step(
isActive: currentStep >= 1,
title: Text("Choisir le titre de l'annonce"),
content: Padding(
padding: EdgeInsets.only(top: 20),
child: TextField(
maxLength: 20,
decoration: InputDecoration(
border: OutlineInputBorder(borderRadius: BorderRadius.circular(30)),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 1.5),
borderRadius: BorderRadius.circular(30)
),
labelText: "Titre de l'annonce"
),
),
)
),
Step(
isActive: currentStep >= 2,
title: Text('Ajouter une description'),
content: Padding(
padding: EdgeInsets.only(top: 20),
child: TextField(
keyboardType: TextInputType.multiline,
maxLines: null,
maxLength: 250,
decoration: InputDecoration(
border: OutlineInputBorder(borderRadius: BorderRadius.circular(30)),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 1.5),
borderRadius: BorderRadius.circular(30)
),
labelText: "Description de l'annonce"
),
),
)
),
Step(
isActive: currentStep >= 3,
title: Text('Ajouter une image'),
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child:
Image.asset("images/Jardinage.png"),
),
Expanded(
child:
Image.asset("images/baby sitting.png"),
),
Expanded(
child:
Image.asset("images/Coiffure.png"),
),
],
),
),
],
)
],
),
),
);
I thank you in advance for your help !
You can make the image selectable by adding GestureDetector and changing the _selectedImageIndex using setState on each tap.
Step(
isActive: currentStep >= 3,
title: const Text('Ajouter une image'),
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: GestureDetector(
onTap: () {
setState(() {
_selectedImage = 0;
});
},
child: Container(
decoration: BoxDecoration(
border: Border.all(
width: 3,
color: _selectedImage == 0
? Colors.green
: Colors.transparent)),
child: Image.asset(
"assets/images/provinces/1.png"))),
),
Expanded(
child: GestureDetector(
onTap: () {
setState(() {
_selectedImage = 1;
});
},
child: Container(
decoration: BoxDecoration(
border: Border.all(
width: 3,
color: _selectedImage == 1
? Colors.green
: Colors.transparent)),
child: Image.asset(
"assets/images/provinces/2.png"))),
),
Expanded(
child: GestureDetector(
onTap: () {
setState(() {
_selectedImage = 2;
});
},
child: Container(
decoration: BoxDecoration(
border: Border.all(
width: 3,
color: _selectedImage == 2
? Colors.green
: Colors.transparent)),
child: Image.asset(
"assets/images/provinces/3.png"))),
),
],
),
),
Alternate Solution : You can refactor the image selector widget as well
The variables will be
var _selectedImageIndex = 0;
final _images = [
"assets/images/provinces/1.png",
"assets/images/provinces/2.png",
"assets/images/provinces/3.png",
];
SelectabImage widget will be
class SelectableImage extends StatelessWidget {
const SelectableImage({
Key? key,
required this.isSelected,
required this.imageAsset,
required this.onTap,
}) : super(key: key);
final bool isSelected;
final String imageAsset;
final void Function(String imageAsset) onTap;
#override
Widget build(BuildContext context) {
return Expanded(
child: GestureDetector(
onTap: () => onTap(imageAsset),
child: Container(
decoration: BoxDecoration(
border: Border.all(
width: 3,
color: isSelected ? Colors.green : Colors.transparent)),
child: Image.asset(imageAsset),
),
),
);
}
}
Step function will be reduced to
Step(
isActive: currentStep >= 3,
title: const Text('Ajouter une image'),
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (int i = 0; i < _images.length; i++)
SelectableImage(
isSelected: _selectedImageIndex == i,
onTap: (selectedImageIndex) {
setState(() {
_selectedImageIndex = i;
});
},
imageAsset: _images[i],
),
],
),
),

persistent_bottom_nav_bar reclick on set screen icon, returns to that screen

I currently have a persistent tab view in my flutter application with the code below:
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xffffffff),
resizeToAvoidBottomInset: false,
body: PersistentTabView.custom(
context,
controller: _tabController,
screens: <Widget>[
const page1(),
const page2(),
const page3(),
ProfileScreen(
onLogoutButtonTap: () async {
Get.deleteAll();
await StorageClass.logout();
Navigator.pushNamedAndRemoveUntil(
context, LoginScreen.routeName, (r) => false);
// Navigator.pushReplacement(context, MaterialPageRoute(builder: (BuildContext context) => LoginScreen()));
},
),
// const ExploreScreen(),
],
// items: _navBarsItems(),
customWidget: _customNavBar(),
routeAndNavigatorSettings: const CutsomWidgetRouteAndNavigatorSettings(
onGenerateRoute: RouteManager.generateRoute),
itemCount: 4,
),
);
}
Widget **_customNavBar()** {
return Container(
decoration: const BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 5.0,
spreadRadius: -2,
offset: Offset(0, -5),
),
],
shape: BoxShape.rectangle,
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(25.0),
topRight: Radius.circular(25.0),
bottomLeft: Radius.zero,
bottomRight: Radius.zero,
),
),
child: SizedBox(
height: 80,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: InkWell(
onTap: () => onTabTapped(0),
child: currentTab == 0
? SvgPicture.asset("assets/icons/home_icon_fill.svg",
width: 24, height: 24)
: SvgPicture.asset("assets/icons/home_icon_nofill.svg",
width: 24, height: 24),
),
),
Expanded(
child: InkWell(
onTap: () => onTabTapped(1),
child: currentTab == 1
? SvgPicture.asset(
"assets/icons/notification_icon_fill.svg",
width: 24,
height: 24)
: SvgPicture.asset(
"assets/icons/notification_icon_nofill.svg",
width: 24,
height: 24),
),
),
Expanded(
child: InkWell(
onTap: () => onTabTapped(2),
child: currentTab == 2
? SvgPicture.asset("assets/icons/library_icon_fill.svg",
width: 24, height: 24)
: SvgPicture.asset("assets/icons/library_icon_nofill.svg",
width: 24, height: 24),
),
),
Expanded(
child: InkWell(
onTap: () => onTabTapped(3),
child: currentTab == 3
? SvgPicture.asset("assets/icons/user_icon_fill.svg",
width: 24, height: 24)
: SvgPicture.asset("assets/icons/user_icon_nofill.svg",
width: 24, height: 24),
),
),
],
),
),
);
}
void onTabTapped(int index) {
setState(() {
currentTab = index;
_tabController.index = index;
});
setState(() {});
}
All of this works, so whenever I click on any of these icons, or the bottom nav bar icons, it directs me to the proper pages: page1, page2, page3 and Profile screen AND the nav bar icons highlight properly.
What I need help is that when I am in page1() and I click on stuff and go explore page1(), going deeper and deeper in the application, when I reclick on the page1() icon, I want to be able to go back to the initial page1() screen. Right now the reclick does not work but if I click on other tabs I can go to the other tabs. I need to be able to once I reclick on the page1() icon, it builds the page1() screen again (or it just resets all of what I have explored).
Thanks

How to remove build side effects in this context, using Staggered Grid View Builder Flutter?

I am trying to create the build method without side effects (without blinking). I solved this problem using StatefulBuilder, but I read that I should rebuild 1000x times without any change or effect.
The Staggered Grid View Builder Widget are rebuilt when the keyboard is opening, or whenever I open again the page, or add/remove an item from it. That's good, normally, but with side effects like you see below. Maybe there is any solution to animate the remove/add functionality, or the infinite reloading and keep the rest of the items in cache. So I need to limit the builder recreate inside Grid View Builder?
On other applications I don't see this ugly "blinking". Where is the problem and how can I solve it? I used Animation Limiter but it's not working for me, neither PrecacheImage, somehow I need to rebuild without blink (first items).
My code:
class VisionBoard extends StatefulWidget {
const VisionBoard({Key? key}) : super(key: key);
#override
_VisionBoardState createState() => _VisionBoardState();
}
class _VisionBoardState extends State<VisionBoard> with SingleTickerProviderStateMixin {
ScreenshotController screenshotController = ScreenshotController();
String saveGoalsButtonText = "SAVE GOALS";
String wallpaperButtonText = "CREATE WALLPAPER";
String saveWallpaperButtonText = "SAVE";
bool createWallpaper = false;
bool isWallpaperCreated = false;
late File imageFile;
late String newImage;
late Uint8List imageRaw;
int noOfImages = 0;
late Uint8List wallpaperBytes;
String title = "My Vision Board";
String goals = "";
late List<String> visions = <String>[];
final TextEditingController _textFieldController = TextEditingController();
final TextEditingController _goalsController = TextEditingController();
static final _formKey = GlobalKey<FormState>();
#override
void initState() {
super.initState();
loadVisionBoardTitleAndImages();
}
void loadVisionBoardTitleAndImages() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
//await prefs.clear();
setState(() {
_textFieldController.text = prefs.getString('titlu') ?? title;
visions = prefs.getStringList('visions') ?? <String>[];
_goalsController.text = prefs.getString('goals') ?? goals;
});
}
void _removeVisions(int index) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
isButtonDisabled=true;
visions.removeAt(index);
prefs.setStringList('visions', visions);
createWallpaper = false;
wallpaperButtonText = "CREATE WALLPAPER";
isWallpaperCreated = false;
});
await CreateWallpaperLayouts().createWallpaper(visions).then((value) {
setState(() {
wallpaperBytes = value;
wallpaper = Image.memory(wallpaperBytes);
precacheImage(wallpaper.image, context);
isButtonDisabled=false;
});
});
}
#override
Widget build(BuildContext context) {
return Sizer(
builder: (context, orientation, deviceType) {
return GestureDetector(
onTap: () {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
},
child: Scaffold(
body: AnimationLimiter(
child: Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/background-marmura.jpeg"), fit: BoxFit.cover)),
child: SafeArea(
child: SingleChildScrollView(
child: Container(
margin: const EdgeInsets.fromLTRB(20, 0, 20, 0),
child: Column(
children: [
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Row(
children: [
Flexible(
child: Text(_textFieldController.text,
style: const TextStyle(
fontWeight: FontWeight.w700,
fontSize: 21,
color: Color(0xff393432),
),
),
),
IconButton(
icon: const Icon(
Icons.edit,
size: 21,
color: Color(0xff393432),
),
onPressed: () {
showAlertDialog(context, setState);
},
)
]);
}
),
const SizedBox(height: 5),
GridView.builder(
clipBehavior: Clip.none,
physics: const ScrollPhysics(),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: SizeConfig.screenWidth!/3,),
itemCount: (visions.length == 12) ? visions.length : visions.length + 1,
itemBuilder: (BuildContext ctx, index) {
if (index < visions.length) {
return AnimationConfiguration.staggeredGrid(
position: index,
duration: const Duration(milliseconds: 1000),
columnCount: 3,
child: ScaleAnimation(
child: FadeInAnimation(
child: OpacityAnimatedWidget.tween(
opacityEnabled: 1, //define start value
opacityDisabled: 0, //and end value
enabled: index < visions.length, //bind with the boolean
child: Stack(
alignment: Alignment.center,
children: [
Container(
width: 25.w,
height: 25.w,
decoration: BoxDecoration(
image: DecorationImage(
image: CleverCloset.imageFromBase64String(visions[index]).image,
fit: BoxFit.fill),
borderRadius: const BorderRadius.all(
Radius.circular(
5.0) // <--- border radius here
),
),
),
Positioned(
top:0,
right:0,
child: ClipOval(
child: InkWell(
onTap: () {
_removeVisions(index);
},
child: Container(
padding: const EdgeInsets.all(5),
color: Colors.white,
child:
const Icon(
Icons.delete,
size: 16,
color: Color(0xff393432)),
),
),
),
),
],
clipBehavior: Clip.none,
),
),
),
),
);
}
else if(index<12){
return InkWell(
onTap: () {
_openGallery(context);
},
child:
Stack(
alignment: Alignment.center,
children:[
Container(
width: 25.w,
height: 25.w,
decoration:
BoxDecoration(
border: Border.all(color: const Color(0xff393432)),
borderRadius: const BorderRadius.all(Radius.circular(5.0)),
),
child:
const Icon(
Icons.add,
size: 25,
color: Color(0xff393432),
)
),
],
),
);
}
else {
return Container(color: Colors.red);
}
}
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: const [
Text("You can add up to 12 pictures.",
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w400,
fontStyle: FontStyle.italic,
),),
],
),
const SizedBox(height: 10),
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Column (
children: [
if(visions.isNotEmpty && visions.length>1 && !isWallpaperCreated) Row(
children: [
SizedBox(
width: 50.w,
child: OpacityAnimatedWidget.tween(
opacityEnabled: 1, //define start value
opacityDisabled: 0, //and end value)
enabled: !isWallpaperCreated &&
visions.isNotEmpty && visions.length > 1,
child: OutlinedButton(
onPressed: visions.isNotEmpty &&
visions.length > 1 ? () async{
setState(() {
wallpaperButtonText = "CREATING...";
//_createWallpaper();
});
wallpaperBytes = await CreateWallpaperLayouts().createWallpaper(visions);
setState(() {
noOfImages = visions.length;
isWallpaperCreated = true;
createWallpaper = true;
});
//Navigator.pushReplacementNamed(context, '/masterclasses');
} : null,
style: OutlinedButton.styleFrom(
primary: const Color(0xffE4BCB4),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
18.0),
),
side: const BorderSide(
width: 3, color: Color(0xffE4BCB4)),
),
child: Text(
wallpaperButtonText,
style: const TextStyle(
color: Color(0xff393432),
fontSize: 14,
fontWeight: FontWeight.w700,
)
),
),
),
),
],
),
const SizedBox(height:40),
if(createWallpaper==true) OpacityAnimatedWidget.tween(
opacityEnabled: 1, //define start value
opacityDisabled: 0, //and end value
enabled: createWallpaper, //bind with the boolean
child: Row(
children: const [
Flexible(
child: Text("Wallpaper",
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 21,
color: Color(0xff393432),
),
),
),
],
),
),
if(createWallpaper==true) const SizedBox(height:15),
if(createWallpaper==true)
OpacityAnimatedWidget.tween(
opacityEnabled: 1, //define start value
opacityDisabled: 0, //and end value
enabled: createWallpaper,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children:[ Container(
width: 50.w,//double.infinity,
height: 50.h,//SizeConfig.screenHeight,
decoration: BoxDecoration(
image: DecorationImage(
image: Image.memory(wallpaperBytes).image,
fit: BoxFit.fill,
)
),
//child: CreateWallpaperLayouts().createWallpaper(visions),
),],
),
),
if(createWallpaper==true) const SizedBox(height:10),
if(createWallpaper==true) StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return OpacityAnimatedWidget.tween(
opacityEnabled: 1, //define start value
opacityDisabled: 0, //and end value
enabled: createWallpaper,
child: Row(
children: [
SizedBox(
width: 50.w,
child: OutlinedButton(
onPressed: () {
_saveWallpaper(wallpaperBytes);
setState(() {
saveWallpaperButtonText = "SAVED!";
});
Future.delayed(const Duration(milliseconds: 1300), () {
setState(() {
saveWallpaperButtonText = "SAVE";
});
});
//Navigator.pushReplacementNamed(context, '/masterclasses');
},
style: OutlinedButton.styleFrom(
primary: const Color(0xffE4BCB4),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
),
side: const BorderSide(width: 3, color: Color(0xffE4BCB4)),
),
child: Text(
saveWallpaperButtonText,
style: const TextStyle(
color: Color(0xff393432),
fontSize: 14,
fontWeight: FontWeight.w700,
)
),
),
),
const SizedBox(width: 10),
],
),
);
}
),
if(createWallpaper==true) const SizedBox(height:50),
Row(
children: const [
Flexible(
child: Text("Goals & Affirmations",
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 21,
color: Color(0xff393432),
),
),
),
],
),
],
);
}
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: const [
Text("Add goals and affirmations.",
style: TextStyle(
fontSize: 15,
),),
],
),
const SizedBox(height: 10),
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Column(
children: [
Card(
elevation: 0,
color: const Color(0xffEFEFEF),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: TextFormField(
key: _formKey,
controller: _goalsController,
maxLines: 8,
decoration: const InputDecoration(border: InputBorder.none),
),
)
),
const SizedBox(height: 10),
Row(
children: [
Container(
width: 50.w,
margin: const EdgeInsets.fromLTRB(0, 0, 0, 40),
child: OutlinedButton(
onPressed: () async{
setState(() {
saveGoalsButtonText = "SAVED!";
});
Future.delayed(const Duration(seconds: 1), () {
setState(() {
saveGoalsButtonText = "SAVE GOALS";
});
});
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
goals = _goalsController.text;
prefs.setString('goals', goals);
});
//Navigator.pushReplacementNamed(context, '/masterclasses');
},
style: OutlinedButton.styleFrom(
primary: const Color(0xffE4BCB4),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
),
side: const BorderSide(width: 3, color: Color(0xffE4BCB4)),
),
child: Text(
saveGoalsButtonText,
style: const TextStyle(
color: Color(0xff393432),
fontSize: 14,
fontWeight: FontWeight.w700,
)
),
),
),
],
),
],
);
}
),
],
),
),
),
),
),
),
),
);
}
);
}
}

How to return data from Flutter dialog?

I'm having a dialog which opens up when click of a button and having a Listtile which Radio as a child in order to make a selection. As the user makes the selection and accepts it, I'm trying to update the selected part in the UI but i'm getting a null response while the value is being returned.
This is my custom statefull dialog
class _CustomScannerSelectDialogState extends State<CustomScannerSelectDialog> {
int selectedRadio;
setSelectedRadio(int val) {
setState(() {
selectedRadio = val;
});
}
#override
Widget build(BuildContext context) {
return Expanded(
child: Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
elevation: 0,
backgroundColor: Colors.transparent,
child: Stack(
children: [
Container(
padding: EdgeInsets.only(left: 20, top: 40, right: 20, bottom: 20),
margin: EdgeInsets.only(top: 40),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(color: Colors.black, offset: Offset(0, 10), blurRadius: 10),
]),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: 20,
),
Text(
'Select the Scanner type',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w600),
),
SizedBox(
height: 15,
),
ListTile(
title: const Text('QR & Barcode scanner'),
leading: Radio(
activeColor: Color(0xFFCC0000),
value: 1,
groupValue: selectedRadio,
onChanged: (val) {
print("Radio $val");
setSelectedRadio(val);
},
),
),
ListTile(
title: const Text('Barras Scanner'),
leading: Radio(
activeColor: Color(0xFFCC0000),
value: 2,
groupValue: selectedRadio,
onChanged: (val) {
print("Radio $val");
setSelectedRadio(val);
},
),
),
SizedBox(
height: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
CustomButton(
buttonText: 'CANCEL',
buttonFunction: () {
Navigator.pop(context);
return;
},
),
RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10), side: BorderSide(color: Colors.blueGrey)),
textColor: Color(0xFFCD853F),
color: Colors.white,
onPressed: () {
print("Selected radio is " + selectedRadio.toString());
Navigator.pop(context);
return selectedRadio.toString();
},
child: new Text(
'ACCEPT',
),
)
],
),
],
),
),
],
),
));
}
}
And on click of a button I'm calling a function as _showScannerSelectionDialog
_showScannerSelectionDialog() {
showDialog(
context: context,
builder: (BuildContext context) {
return CustomScannerSelectDialog();
},
).then((value) => print("Value is "+value.toString()));
}
Here the value I'm getting printed is null but i need the value of the selectedRadio
I think, you only need to simply use the following when closing the dialog:
var result = selectedRadio.toString(); // or any value.
Navigator.pop(context, result);

Why is selected value not reflecting in showModalBottomSheet with flutter?

First of all, I created a designed bottom sheet, In which, there are two lists to show numbers(left side) and options(hour, day, week, month) with the help of CupertinoPicker widget,
Numbers will depend on what option I select, If I select the hour, numbers of the left side should be 1-24, and if I select week, numbers should be 1-4, I select the day, numbers should be 1-30 and last I select month number should be 1-12.
Code :
All Lists variable:
List<String> reminderDay = ['hour','day','week','month'];
List<String> reminderHoursVal =['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24'];
List<String> reminderDaysVal =['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31'];
List<String> reminderMonthsVal =['1','2','3','4','5','6','7','8','9','10','11','12'];
List<String> reminderWeeksVal =['1','2','3','4'];
String selectedReminderVal='1';
String selectedReminderDay ='hour';
Code of bottom sheet:
addReminder(){
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return AnimatedPadding(
padding: MediaQuery.of(context).viewInsets,
duration: const Duration(milliseconds: 100),
curve: Curves.decelerate,
child: Container(
padding: const EdgeInsets.only(top:8,right: 8, left:8,bottom: 8),
height: MediaQuery.of(context).size.height/2,
// color: Colors.transparent,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30)
)
),
child: Container(
child: Column(
// mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(height:10),
Text("Set a reminder",
style: TextStyle(
fontSize:18,
fontWeight:FontWeight.bold,
color: Colors.grey
),
),
SizedBox(height:20),
Container(
margin: const EdgeInsets.only(left: 10, right: 10),
height: MediaQuery.of(context).size.height/4,
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(10)
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: CupertinoPicker(
scrollController: new FixedExtentScrollController(
initialItem: 0,
),
itemExtent: 30,
backgroundColor: Colors.grey[100],
onSelectedItemChanged: (int val) {
setState(() {
if(selectedReminderDay=='day'){
selectedReminderVal = reminderDaysVal[val];
}else if(selectedReminderDay=='week'){
selectedReminderVal = reminderWeeksVal[val];
}else if(selectedReminderDay=='month'){
selectedReminderVal = reminderMonthsVal[val];
}else{
selectedReminderVal = reminderHoursVal[val];
}
print("selectedReminderVal:$selectedReminderVal");
});
},
children:selectedReminderDay=='day'?reminderDaysVal
:selectedReminderDay=='week'?reminderWeeksVal
:selectedReminderDay=='month'?reminderMonthsVal:reminderHoursVal// ['hour','day','week','month']; reminderHoursVal
.map(
(item) => Center(
child: Text(
item,
style: TextStyle(
fontSize: 16,
// fontWeight:FontWeight.bold,
),
),
),
)
.toList()),
),
Expanded(
child: CupertinoPicker(
scrollController: new FixedExtentScrollController(
initialItem: 0,
),
itemExtent: 30,
backgroundColor: Colors.grey[100],
onSelectedItemChanged: (int val) {
setState(() {
selectedReminderDay = reminderDay[val];
print("selectedReminderDay:$selectedReminderDay");
});
},
children: reminderDay
.map(
(item) => Center(
child: Text(
item,
style: TextStyle(
fontSize: 16,
// fontWeight:FontWeight.bold,
),
),
),
)
.toList()),
),
])
),
SizedBox(height:15),
// selectedVal!=null?Text(selectedVal.toString()):Container()
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("You'll get the reminder"),
Text('$selectedReminderVal $selectedReminderDay before the event')
],
),
SizedBox(height:25),
Padding(
padding: const EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: (){
Navigator.pop(context);
},
child: Text("Cancel",
style: TextStyle(
fontSize: 18,
color: Colors.blue,
fontWeight: FontWeight.bold
),
),
),
InkWell(
onTap: (){
Navigator.pop(context);
},
child: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(8)
),
width: MediaQuery.of(context).size.width/5,
height: MediaQuery.of(context).size.height/25 ,
child: Text("Save", style:TextStyle(
color: Colors.white,
fontSize: 19
)),
),
)
],
),
)
],
),
)
),
);
},
);
}
Screenshot:
This is because the state you are setting is different from the state in the modal bottom sheet.
Right now, when you are calling setState, you are actually rebuilding the stateful widget under the modal bottom sheet.
To fix this, just wrap your bottom sheet in a Stateful Builder.
StatefulBuilder(
builder: (context, setState) {
return AnimatedPadding(
padding: MediaQuery.of(context).viewInsets,
duration: const Duration(milliseconds: 100),
curve: Curves.decelerate,
child: Container(
padding: const EdgeInsets.only(top:8,right: 8, left:8,bottom: 8),
height: MediaQuery.of(context).size.height/2,
// color: Colors.transparent,
decoration: BoxDecoration(
color: Colors.white,
....
when we create new context widgets in existing state there states become different, bottomSheets are similar to dialogBox with new context and builder build a completely new widget out of the parent state, to create its own stateful state Wrap it with the stateful builder and user its own setState to change anything in this context not the parent one
eg:
StatefulBuilder(
builder: (context, setStateChild) {
return AnimatedPadding(...
Expanded(
child: CupertinoPicker(
scrollController: new FixedExtentScrollController(
initialItem: 0,
),
itemExtent: 30,
backgroundColor: Colors.grey[100],
onSelectedItemChanged: (int val) {
setStateChild(() {
selectedReminderDay = reminderDay[val];
print("selectedReminderDay:$selectedReminderDay");
});
},);
child: ... ),
}