DropDownButton doesn't work after using Navigator.push - flutter

I'm trying to create a clothes E-Shop app and I need the cart items to show the list of different colors for each product, I'm using a DropDownButton to show them, the slidable library to create the cart items, and a custom bottom bar. If I tap in the cart item to go to the product view and then go back to the cart pressing the back button, the dropdownbutton stops working, but if I go to the product through the bottom bar everything works.
class CartPage extends StatefulWidget {
const CartPage({Key? key}) : super(key: key);
#override
CartPageState createState() => CartPageState();
}
class CartPageState extends State<CartPage> {
List<String> dropDownValues = cart.itemList.keys.toList();
late String dropDownValueShow;
final textController = TextEditingController();
#override
Widget build(BuildContext context) {
debugPrint('$dropDownValues');
dropDownValueShow = cart.itemList.isEmpty ? '' : dropDownValues[0];
return Scaffold(
bottomNavigationBar: CustomBottomNavBar(
1,
key: ValueKey(cart.itemList),
),
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.black,
title: const Text(
"Shopping Cart",
style: TextStyle(color: Colors.white, fontSize: 17),
),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: cart.empty
? [
SizedBox(
width: 200,
height: 200,
child: Center(
child: Text(
"Empty Cart",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.grey.shade500,
fontSize: 20,
),
),
))
]
: [
Expanded(
child: AnimatedList(
scrollDirection: Axis.vertical,
initialItemCount: cart.itemList.length,
itemBuilder: (context, index, animation) {
return Slidable(
key: UniqueKey(),
actionPane: const SlidableDrawerActionPane(),
actionExtentRatio: 0.25,
actions: const [],
secondaryActions: const [],
child: _cartItem(),
);
},
),
),
],
),
);
}
Widget _cartItem() {
return GestureDetector(
key: UniqueKey(),
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation1, animation2) =>
const ProductView(),
transitionDuration: Duration.zero,
reverseTransitionDuration: Duration.zero,
),
).then((_) => setState(() {}));
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey.shade200,
offset: const Offset(0, 2),
blurRadius: 6,
),
],
),
child: Row(
children: <Widget>[
Text('prod1'),
const SizedBox(
width: 10,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const SizedBox(height: 10),
DropdownButton(
isExpanded: true,
value: dropDownValueShow,
icon: const Icon(Icons.arrow_drop_down),
items: cart.itemList.keys.toList().map((String value) {
return DropdownMenuItem(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (String? newValue) {
setState(() {
dropDownValueShow = newValue!;
});
},
),
const SizedBox(height: 10),
],
),
),
const SizedBox(
width: 10,
),
],
),
),
);
}
}
The whole code is in https://github.com/nicoacevedor/minimal.git

Related

Flutter multiple ExpansionTile's collapses simultaneously

I have a custom Card widget with ExpansionTile as a child which displays multiple Dropdownbuttons according to data fetched from an API.
But when I use ListView.builder to build N amount of said custom widgets they all behave simultaneously, for example when I collapse the ExpansionTile all open ExpansionTiles collapse simultaneously and reset the data inside Dropdownbuttons (resetting the data expected outcome when ExpansionTile collapsed but only the collapsed ExpansionTile should reset its children Dropdownbuttons, not all open ExpansionTiles children).
Here is my builder.
var items = ["Apartment 1", "Apartment 2", "Apartment 3", "Apartment 4"];
class MapPage extends StatelessWidget {
const MapPage({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
key: ValueKey(items),
scrollDirection: Axis.vertical,
itemCount: items.length,
padding: const EdgeInsets.only(top: 8),
itemBuilder: (context, index) {
return MapCard(
building: items[index],
floor: 4,
key: Key(items[index].toString()),
);
}),
);
}
}
and my CustomCard
class MapCard extends StatefulWidget {
final String building;
final int floor;
const MapCard({super.key, required this.building, required this.floor});
#override
State<MapCard> createState() => _MapCardState();
}
class _MapCardState extends State<MapCard> {
#override
Widget build(BuildContext context) {
PageStorageKey key = PageStorageKey('${widget.key}');
return Center(
child: Consumer<MapCardViewModel>(
builder: (context, mapCardViewModel, child) => Card(
color: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.9,
child: Padding(
padding: const EdgeInsets.only(bottom: 12),
child: ExpansionTile(
key: key,
onExpansionChanged: (changed) {
if (!changed) {
mapCardViewModel.setAreaVisibility(false);
mapCardViewModel.setButtonVisibility(false);
mapCardViewModel.setIsFloorChosen(false);
mapCardViewModel.setAreaVisibility(false);
mapCardViewModel.area = mapCardViewModel.areas[0];
mapCardViewModel.floorNumber = mapCardViewModel.floors[0];
}
},
title: Row(
children: [
Container(
padding:
const EdgeInsets.only(top: 8, bottom: 8, right: 8),
child: Image.asset(
"assets/images/example.png",
height: 80,
width: 80,
)),
Flexible(
child: Container(
padding: const EdgeInsets.fromLTRB(0, 8, 8, 8),
child: Column(
children: [
Text("${widget.building} Apartment \n"
"Floor Count ${widget.floor} ")
],
),
),
)
],
),
children: [
const Text("Choose Floor"),
Padding(
padding: const EdgeInsets.only(right: 24, left: 24),
child: DropdownButton(
isExpanded: true,
value: mapCardViewModel.isFloorChosen == false
? mapCardViewModel.floors[0]
: mapCardViewModel.floorNumber,
items: mapCardViewModel.floors
.map<DropdownMenuItem<int>>((int i) {
return DropdownMenuItem<int>(
value: i,
child: Text(i.toString()),
);
}).toList(),
onChanged: (int? value) {
mapCardViewModel.setFloorNumber(value!);
mapCardViewModel.setIsFloorChosen(true);
mapCardViewModel.setAreaVisibility(true);
}),
),
Visibility(
visible: mapCardViewModel.isAreaVisible,
child: Column(
children: [
const Text("Choose an Area to map"),
Padding(
padding: const EdgeInsets.only(right: 24, left: 24),
child: DropdownButton(
isExpanded: true,
value: mapCardViewModel.isAreaChosen == false
? mapCardViewModel.areas[0]
: mapCardViewModel.area,
items: mapCardViewModel.areas
.map<DropdownMenuItem<String>>(
(String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (String? value) {
mapCardViewModel.setArea(value!);
mapCardViewModel.setIsAreaChosen(true);
mapCardViewModel.setButtonVisibility(true);
}),
),
],
),
),
Visibility(
visible: mapCardViewModel.isButtonsVisible,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
TextButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return CustomDialog(
title: "Mapping Status",
content:
"This area hasn't been mapped yet",
page: Container(),
buttonColor: MainColors().mainBlue);
});
},
child: const Text("Show Area Map")),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const MappedPage(),
),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: MainColors().mainBlue),
child: const Text(
"Map The Area",
style: TextStyle(color: Colors.white),
))
],
),
)
],
),
)),
),
));
}
}
I tried to assign keys to each ExpansionTile and custom MapCard widgets with StatefulWidget but I couldn't solve my problem.
I remove all 3rd dependency and try to adjust your solution of MapCard widget.
var items = ["Apartment 1", "Apartment 2", "Apartment 3", "Apartment 4"];
final floors = ['Floor 1', 'Floor 2'];
final areas = ['Area 1', 'Area 2'];
class MapCard extends StatefulWidget {
final String building;
final int floor;
const MapCard({Key? key, required this.building, required this.floor}): super(key: key);
#override
State<MapCard> createState() => _MapCardState();
}
class _MapCardState extends State<MapCard> {
#override
Widget build(BuildContext context) {
PageStorageKey key = PageStorageKey('${widget.key}');
return Center(
child: Card(
color: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.9,
child: Padding(
padding: const EdgeInsets.only(bottom: 12),
child: ExpansionTile(
key: key,
onExpansionChanged: (changed) {
if (!changed) {
print('!changed');
}
},
title: Row(
children: [
Container(
padding:
const EdgeInsets.only(top: 8, bottom: 8, right: 8),
child: Placeholder(
fallbackHeight: 80,
fallbackWidth: 80,
)),
Flexible(
child: Container(
padding: const EdgeInsets.fromLTRB(0, 8, 8, 8),
child: Column(
children: [
Text("${widget.building} Apartment \n"
"Floor Count ${widget.floor} ")
],
),
),
)
],
),
children: [
const Text("Choose Floor"),
Padding(
padding: const EdgeInsets.only(right: 24, left: 24),
child: DropdownButton(
isExpanded: true,
value: floors.first,
items: floors
.map<DropdownMenuItem<String>>((String i) {
return DropdownMenuItem<String>(
value: i,
child: Text(i.toString()),
);
}).toList(),
onChanged: (String? value) {
// code here
}),
),
Visibility(
visible: true,
child: Column(
children: [
const Text("Choose an Area to map"),
Padding(
padding: const EdgeInsets.only(right: 24, left: 24),
child: DropdownButton(
isExpanded: true,
value: areas.first,
items: areas
.map<DropdownMenuItem<String>>(
(String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (String? value) {
// code here
}),
),
],
),
),
Visibility(
visible: true,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
TextButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(content: Text('Alert dialog'),);
});
},
child: const Text("Show Area Map")),
ElevatedButton(
onPressed: () {
print('navigate');
},
child: const Text(
"Map The Area",
style: TextStyle(color: Colors.white),
))
],
),
)
],
),
)),
),
);
}
}
But I suppose your problem in the line
Consumer<MapCardViewModel>
because every time when you change it this will apply to each created card. So you have to put it above your ListView.builder and pass it as a parameter to your cards

How can I fix the renderflex overflow of a card in Flutter?

How can I fix the RenderFlex overflowed pixel in my card Flutter? I cant seem to find a tutorial regarding this kind of problem. All of the tutorials in StackOverflow teach you to use the listview and SingleChildScrollView but that is not the case for me. The error shows in the card itself and I don't want the card to be using a singlechildscrollview.
I already tried fixing it by lowering the height and width but I will still need a proper tutorial that can help me fix this kind of issues.
This is the card.dart for the application
import 'package:flutter/material.dart';
class ListViewCard extends StatelessWidget {
final String title;
final void Function()? onTap;
final String imageOfPlant; //Change to String
const ListViewCard({
super.key,
required this.title,
required this.onTap,
required this.imageOfPlant,
});
#override
Widget build(BuildContext context) {
return Card(
color: const Color.fromARGB(255, 75, 175, 78),
elevation: 1,
margin: const EdgeInsets.all(8),
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: InkWell(
splashColor: Colors.lightGreenAccent.withAlpha(30),
onTap: onTap,
//sizedBox of the card
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
//image of the card
Image.asset(
imageOfPlant,
height: 200,
width: 150,
fit: BoxFit.cover,
),
SizedBox(
height: 50,
width: 150,
child: Center(
child: Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 19,
fontFamily: 'RobotoMedium',
color: Color(0xffeeeeee)), // textstyle
),
),
), //text //SizedBox
], // <widget>[]
), // column
), //inkwell
); // card
}
}
This is the home.dart where the card will be called.
import 'package:flutter/material.dart';
import 'package:picleaf/nav_pages/plant.dart';
import '../widgets/card.dart';
class homePage extends StatefulWidget {
const homePage({super.key});
#override
State<homePage> createState() => _HomePageState();
}
List<String> plants = [
"Bell Pepper",
"Cassava",
"Grape",
"Potato",
"Strawberry",
"Tomato",
];
class CustomSearchDelegate extends SearchDelegate {
// Demo list to show querying
CustomSearchDelegate({String hinttext = "Search plants here"})
: super(searchFieldLabel: hinttext);
// first overwrite to
// clear the search text
#override
List<Widget>? buildActions(BuildContext context) {
return [
IconButton(
onPressed: () {
query = '';
},
icon: const Icon(Icons.clear),
),
];
}
// second overwrite to pop out of search menu
#override
Widget? buildLeading(BuildContext context) {
return IconButton(
onPressed: () {
close(context, null);
},
icon: const Icon(Icons.arrow_back),
);
}
// third overwrite to show query result
#override
Widget buildResults(BuildContext context) {
List<String> matchQuery = [];
for (var fruit in plants) {
if (fruit.toLowerCase().contains(query.toLowerCase())) {
matchQuery.add(fruit);
}
}
return ListView.builder(
itemCount: matchQuery.length,
itemBuilder: (context, index) {
var result = matchQuery[index];
return ListTile(
title: Text(
result,
style: const TextStyle(fontFamily: 'RobotoMedium'),
),
);
},
);
}
// last overwrite to show the
// querying process at the runtime
#override
Widget buildSuggestions(BuildContext context) {
List<String> matchQuery = [];
for (var fruit in plants) {
if (fruit.toLowerCase().contains(query.toLowerCase())) {
matchQuery.add(fruit);
}
}
return ListView.builder(
itemCount: matchQuery.length,
itemBuilder: (context, index) {
var result = matchQuery[index];
return ListTile(
title: Text(
result,
style: const TextStyle(fontFamily: 'RobotoMedium'),
),
);
},
);
}
}
class _HomePageState extends State<homePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: const Text(
"PicLeaf",
style: TextStyle(
color: Color.fromRGBO(102, 204, 102, 1.0),
fontWeight: FontWeight.bold),
),
backgroundColor: Colors.white,
shadowColor: const Color.fromARGB(255, 95, 94, 94),
actions: [
IconButton(
onPressed: () {
// method to show the search bar
showSearch(
context: context,
// delegate to customize the search bar
delegate: CustomSearchDelegate());
},
icon: const Icon(Icons.search, color: Colors.black),
)
],
),
backgroundColor: const Color(0xffeeeeee),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const SizedBox(
height: 10,
),
Container(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 10),
child: const Text(
'Take a pic!',
style: TextStyle(
fontSize: 35,
fontFamily: 'RobotoBold',
color: Colors.black),
textAlign: TextAlign.left,
),
),
Container(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 10),
child: const Text('Find out what is wrong with your plant!',
style: TextStyle(
fontSize: 18,
fontFamily: 'RobotoMedium',
color: Color.fromRGBO(102, 124, 138, 1.0)),
textAlign: TextAlign.left),
),
const SizedBox(
height: 10,
),
Container(
color: const Color.fromRGBO(102, 204, 102, 1.0),
child: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.fromLTRB(20, 10, 20, 0),
margin: const EdgeInsets.symmetric(horizontal: 0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: const <Widget>[
Expanded(
child: Text('List of Plants',
style: TextStyle(
fontSize: 30,
fontFamily: 'RobotoBold',
color: Color(0xffeeeeee)),
textAlign: TextAlign.center),
),
],
),
),
GridView.count(
physics: const ScrollPhysics(),
shrinkWrap: true,
crossAxisSpacing: 20,
mainAxisSpacing: 20,
crossAxisCount: 2,
children: <Widget>[
ListViewCard(
title: "Bell Pepper",
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) =>
const SecondPage(plantname: 'Bell Pepper')));
},
imageOfPlant:
"assets/Images_of_Plant/BellPeper_Image.jpg",
),
ListViewCard(
title: "Bell Pepper",
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) =>
const SecondPage(plantname: 'Bell Pepper')));
},
imageOfPlant:
"assets/Images_of_Plant/BellPeper_Image.jpg",
),
ListViewCard(
title: "Bell Pepper",
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) =>
const SecondPage(plantname: 'Bell Pepper')));
},
imageOfPlant:
"assets/Images_of_Plant/BellPeper_Image.jpg",
),
ListViewCard(
title: "Bell Pepper",
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) =>
const SecondPage(plantname: 'Bell Pepper')));
},
imageOfPlant:
"assets/Images_of_Plant/BellPeper_Image.jpg",
),
ListViewCard(
title: "Bell Pepper",
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) =>
const SecondPage(plantname: 'Bell Pepper')));
},
imageOfPlant:
"assets/Images_of_Plant/BellPeper_Image.jpg",
),
ListViewCard(
title: "Bell Pepper",
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) =>
const SecondPage(plantname: 'Bell Pepper')));
},
imageOfPlant:
"assets/Images_of_Plant/BellPeper_Image.jpg",
),
],
),
],
),
),
],
),
),
);
}
}
Your gridView's Item give you a specific size but you are setting more than that for your container and text, I suggest you try this:
child: Stack(
children: <Widget>[
//image of the card
Image.asset(
imageOfPlant,
height: double.infinity,
width: 150,
fit: BoxFit.cover,
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: SizedBox(
height: 50,
width: 150,
child: Center(
child: Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 19,
fontFamily: 'RobotoMedium',
color: Color(0xffeeeeee)), // textstyle
),
),
),
), //text //SizedBox
], // <widget>[]
),
and If you want to change item's AspectRatio you can do this:
GridView.count(
physics: const ScrollPhysics(),
shrinkWrap: true,
crossAxisSpacing: 20,
mainAxisSpacing: 20,
crossAxisCount: 2,
childAspectRatio: 2 / 3, <--- add this
children: <Widget>[
...
]
)

Flutter || Checkbox on hover doesn't give on tap cursor permission

I am working on dropdownmenu items where in the drop-down menu item there are several checkboxes but any of the checkboxes on hover don't give on tap cursor permission.
This is a very strange thing I found out as I have already used the checkbox before but this type of error I didn't receive.
I think maybe the problem is in dropdownmenu.
I have also included the video for better understanding of my problem.
my code :-
Container(
width: 160,
//margin: const EdgeInsets.only(top: 10.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5), color: Colors.white),
child: ListTileTheme(
contentPadding: EdgeInsets.all(0),
dense: true,
horizontalTitleGap: 0.0,
minLeadingWidth: 0,
child: ExpansionTile(
iconColor: primaryBackgroundLightGrey,
title: Text(
listOFSelectedItem.isEmpty
? "Project type"
: listOFSelectedItem[0],
style: t5O40),
children: <Widget>[
Container(
height: 10,
color: primaryBackgroundLightGrey,
),
ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: widget.listOFStrings.length,
itemBuilder: (BuildContext context, int index) {
return Column(
children: [
Container(
height: 10,
),
Container(
margin: const EdgeInsets.only(bottom: 8.0),
child: _ViewItem(
item: widget.listOFStrings[index],
selected: (val) {
selectedText = val;
if (listOFSelectedItem.contains(val)) {
listOFSelectedItem.remove(val);
} else {
listOFSelectedItem.add(val);
}
widget.selectedList(listOFSelectedItem);
setState(() {});
},
itemSelected: listOFSelectedItem
.contains(widget.listOFStrings[index])),
),
],
);
},
),
],
),
),
),
class _ViewItem extends StatelessWidget {
String item;
bool itemSelected;
final Function(String) selected;
_ViewItem(
{required this.item, required this.itemSelected, required this.selected});
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
return Padding(
padding: EdgeInsets.only(
left: size.width * .015,
),
child: Row(
children: [
SizedBox(
height: 2,
width: 2,
child: Checkbox(
value: itemSelected,
onChanged: (val) {
selected(item);
},
hoverColor: Colors.transparent,
checkColor: Colors.white,
activeColor: Colors.grey),
),
SizedBox(
width: size.width * .010,
),
Text(item, style: t3O60),
],
),
);
}
}
You can adapt the example to your own code
dropdownBuilder: _customDropDownExample,
popupItemBuilder: _customPopupItemBuilderExample,
Widget _customDropDownExample(
BuildContext context, UserModel? item, String itemDesignation) {
if (item == null) {
return Container();
}
return Container(
child: (item.avatar == null)
? ListTile(
contentPadding: EdgeInsets.all(0),
leading: CircleAvatar(),
title: Text("No item selected"),
)
: ListTile(
contentPadding: EdgeInsets.all(0),
leading: CircleAvatar(
// this does not work - throws 404 error
// backgroundImage: NetworkImage(item.avatar ?? ''),
),
title: Text(item.name),
subtitle: Text(
item.createdAt.toString(),
),
),
);
After that
Widget _customPopupItemBuilderExample(
BuildContext context, UserModel item, bool isSelected) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 8),
decoration: !isSelected
? null
: BoxDecoration(
border: Border.all(color: Theme.of(context).primaryColor),
borderRadius: BorderRadius.circular(5),
color: Colors.white,
),
child: ListTile(
selected: isSelected,
title: Text(item.name),
subtitle: Text(item.createdAt.toString()),
leading: CircleAvatar(
// this does not work - throws 404 error
// backgroundImage: NetworkImage(item.avatar ?? ''),
),
),
);
I am using this package https://pub.dev/packages/dropdown_button2
Multiselect Dropdown with Checkboxes
final List<String> items = [
'Item1',
'Item2',
'Item3',
'Item4',
];
List<String> selectedItems = [];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: DropdownButtonHideUnderline(
child: DropdownButton2(
isExpanded: true,
hint: Align(
alignment: AlignmentDirectional.center,
child: Text(
'Select Items',
style: TextStyle(
fontSize: 14,
color: Theme.of(context).hintColor,
),
),
),
items: items.map((item) {
return DropdownMenuItem<String>(
value: item,
//disable default onTap to avoid closing menu when selecting an item
enabled: false,
child: StatefulBuilder(
builder: (context, menuSetState) {
final _isSelected = selectedItems.contains(item);
return InkWell(
onTap: () {
_isSelected
? selectedItems.remove(item)
: selectedItems.add(item);
//This rebuilds the StatefulWidget to update the button's text
setState(() {});
//This rebuilds the dropdownMenu Widget to update the check mark
menuSetState(() {});
},
child: Container(
height: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row(
children: [
_isSelected
? const Icon(Icons.check_box_outlined)
: const Icon(Icons.check_box_outline_blank),
const SizedBox(width: 16),
Text(
item,
style: const TextStyle(
fontSize: 14,
),
),
],
),
),
);
},
),
);
}).toList(),
//Use last selected item as the current value so if we've limited menu height, it scroll to last item.
value: selectedItems.isEmpty ? null : selectedItems.last,
onChanged: (value) {},
buttonHeight: 40,
buttonWidth: 140,
itemHeight: 40,
itemPadding: EdgeInsets.zero,
selectedItemBuilder: (context) {
return items.map(
(item) {
return Container(
alignment: AlignmentDirectional.center,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Text(
selectedItems.join(', '),
style: const TextStyle(
fontSize: 14,
overflow: TextOverflow.ellipsis,
),
maxLines: 1,
),
);
},
).toList();
},
),
),
),
);
}

How to customize the checkboxes, radio buttons and Rang slider in Flutters?

Hello Folks I am playing with Buttons to customize the buttons but I didn't find a way to approach the expected design. basically, I wanted to customize the 1) Multiple checkBoxes with Grey color Background and Black color Check Mark 2) RangeSlider Inactive bar with Grey color 3) Radio Button with black Color and I Want to unselect all, I understand that in Radio Button user cant Unselect all the buttons but I Want to unselect all with or without Radio Buttons(any different way is also fine) 4) same as 3rd point but Square Box with Grey color Background and Black color Check Mark. I attached a screenshot of my code and Expected design and also pasted my code. Please guys Help me to approach Expected Design, Please feel free to do comments for any clarity. Thanks in Advance.
What I want My expected Design My Result UI
Main File
import 'package:filters2/filter_screen.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Dialog',
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Filters'),
),
body: Center(
child: FlatButton(
onPressed: (){
filterBottomSheet();
},
color: Colors.green,
padding: EdgeInsets.fromLTRB(15, 15, 15, 15),
child: Text('Filters',style: TextStyle(fontSize: 16,color: Colors.white),),
),
),
);
}
filterBottomSheet(){
showModalBottomSheet(
context: context,
isScrollControlled: true,
isDismissible: true,
builder: (BuildContext context){
return Filters();
}
);
}
}
Filters Class
import 'package:filters2/Fruit_model_class.dart';
import 'package:filters2/color_model_class.dart';
import 'package:filters2/mobile_model_class.dart';
import 'package:flutter/material.dart';
class Filters extends StatefulWidget {
#override
_FiltersState createState() => _FiltersState();
}
class _FiltersState extends State<Filters> {
/*--------MobileList-multiple checklist-------*/
List<MobileList> availableMobiles = [
MobileList(id: 0, companyName: "Google", model: "Pixel 6 Pro"),
MobileList(id: 1, companyName: "Apple", model: "Iphone Xr"),
MobileList(id: 2, companyName: "Xiaomi", model: "Redmi note 10"),
];
List<MobileList> selectedMobiles = [];
/*------Price Range Selection-RangeSlider--------*/
RangeValues _currentRangeValues = const RangeValues(40000, 80000);
int minPrice = 20000;
int maxPrice = 90000;
/*Radio--fruits*/
String radioItem1 = 'Mango';
int fruitGroupId = 0;
List<FruitsList> fList = [
FruitsList(id: 0, name: "Mango",),
FruitsList(id: 1, name: "Apple",),
FruitsList(id: 2, name: "Banana",),
FruitsList(id: 3, name: "Cherry",),
];
/*Radio--Colors*/
String radioItem2 = 'Red';
int? colorGroupId ;
List<ColorsList> cList = [
ColorsList(id: 0, name: "Blue",),
ColorsList(id: 1, name: "Red",),
ColorsList(id: 2, name: "Green",),
ColorsList(id: 3, name: "Yellow",),
];
#override
Widget build(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height * 0.85,
decoration: BoxDecoration(
//olor: Colors.red,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
)
),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 10,),
Container(
child: Text("Filter",style: TextStyle(color: Colors.black, fontSize: 24,fontWeight: FontWeight.bold),),
),
SizedBox(height: 10,),
Container(
child: Text("Select Mobiles (multiple selection)",style: TextStyle(color: Colors.black, fontSize: 20,fontWeight: FontWeight.bold),),
),
SizedBox(height: 10,),
Divider(),
Container(
child: Row(
children: [
Expanded(
flex:1,
child: ListView.builder(
shrinkWrap: true,
itemCount: availableMobiles.length,
itemBuilder: (context, playerIndex){
return CheckboxListTile(
controlAffinity: ListTileControlAffinity.trailing,
contentPadding: EdgeInsets.zero,
title:Text(availableMobiles[playerIndex].companyName),
value: selectedMobiles.contains(availableMobiles[playerIndex]),
onChanged: (value) {
if(selectedMobiles.contains(availableMobiles[playerIndex])){
selectedMobiles.remove(availableMobiles[playerIndex]);
}
else {
selectedMobiles.add(availableMobiles[playerIndex]);
}
setState(() {
});
}
);
}
),
),
Expanded(
flex: 2,
child:Container(
color: Colors.white ,
) ),
],
),
),
SizedBox(height: 10,),
Divider(),
Container(
child: Text("Year",style: TextStyle(color: Colors.black, fontSize: 20,fontWeight: FontWeight.bold),),
),
SizedBox(height: 10,),
Divider(),
Container(
child: Row(
//mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Align(
alignment: Alignment.centerLeft,
child: Text("20000",style: TextStyle(color: Colors.black, fontSize: 20)),
),
Expanded(
child: RangeSlider(
values: _currentRangeValues,
min: minPrice.toDouble(),
max: maxPrice.toDouble(),
divisions: maxPrice - minPrice,
activeColor: Colors.black,
labels: RangeLabels(
_currentRangeValues.start.round().toString(),
_currentRangeValues.end.round().toString(),
),
onChanged: (RangeValues values) {
setState(() {
_currentRangeValues = values;
});
},
),
),
Align(
alignment: Alignment.centerRight,
child: Text("90000",style: TextStyle(color: Colors.black, fontSize: 20))),
],
),
),
SizedBox(height: 10,),
Divider(),
Container(
child: Row(
children: [
Expanded(
flex: 1,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Fruits (Single selection)",style: TextStyle(color: Colors.black, fontSize: 20,fontWeight: FontWeight.bold),),
Column(
children:
fList.map((data) => RadioListTile(
controlAffinity: ListTileControlAffinity.trailing,
contentPadding: EdgeInsets.zero,
title: Text("${data.name}"),
groupValue: fruitGroupId,
value: data.id,
onChanged: (val){
setState(() {
radioItem1 = data.name;
fruitGroupId = data.id;
});
},
)).toList(),
),
],
),
),
Expanded(
flex:2,
child: Container(
//width: 5,
color: Colors.white,
)),
],
),
),
Container(
child: Row(
children: [
Expanded(
flex: 1,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Colors",style: TextStyle(color: Colors.black, fontSize: 20,fontWeight: FontWeight.bold),),
Column(
children:
cList.map((colorData) => RadioListTile(
controlAffinity: ListTileControlAffinity.trailing,
contentPadding: EdgeInsets.zero,
title: Text("${colorData.name}"),
groupValue: colorGroupId,
value: colorData.id,
onChanged: (val){
setState(() {
radioItem2 = colorData.name;
colorGroupId = colorData.id;
});
},
)).toList(),
),
],
),
),
Expanded(
flex: 2,
child:Container(
color: Colors.white ,
) ),
],
),
),
],
),
),
);
}
}
mobile_model_class
class MobileList {
int id;
String companyName;
String model;
MobileList({required this.id, required this.companyName, required this.model});
}
Fruit_model_class
class MobileList {
int id;
String companyName;
String model;
MobileList({required this.id, required this.companyName, required this.model});
}
color_model_class
class ColorsList {
String name;
int id;
ColorsList({required this.name,required this.id});
}
To change color of checkbox you can use
Checkbox(
value: isCheck,
checkColor: Colors.black, // change color here
activeColor: Colors.grey,
onChanged: (bool value) {
setState(() {
isCheck = value;
});
}),
To change color of radio button try
Theme(
data: ThemeData(
//Change color here
unselectedWidgetColor: Colors.grey,
),
child: Row(
children: <Widget>[
Radio(),
Radio()
],
),
);
You can use SliderTheme class to customize your slider color and shape.
SliderTheme(
data: SliderThemeData(
this.trackHeight,
activeTrackColor:Colors.grey,
inactiveTrackColor:Colors.black,
thumbColor: Colors.green,
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 20)),
child: Slider(
value: _value,
onChanged: (val) {
_value = val;
setState(() {});
},
),
),
Hope the answer is helpful to you.

Problem with List builder repeat the data when adding or removing item in the list

I'm new to flutter. When I adding new item or removing an item from the list, the list builder does update the list, but the problem is that the list builder also displaying the previous item list and showing new updated item list. So what I want to do is keeping the new updated item list instead of old item list.
class AlarmPage extends StatefulWidget {
final String title;
AlarmPage({Key key, this.title}) : super(key: key);
#override
_AlarmPageState createState() => _AlarmPageState();
}
class _AlarmPageState extends State<AlarmPage> {
String alarmName;
// Test Function
void _addAlarm() {
setState(() {
Navigator.push(
context, MaterialPageRoute(builder: (context) => AddAlarm()));
});
}
#override
void initState() {
super.initState();
Provider.of<AlarmsProvider>(context, listen: false).getLocalStorage();
}
#override
Widget build(BuildContext context) {
List<Widget> allWidgetsAlarms = List<Widget>();
return Consumer<AlarmsProvider>(builder: (context, alarmProviderItem, _) {
List<String> localAlarms = alarmProviderItem.alarms;
if (localAlarms != null) {
localAlarms.forEach((item) {
allWidgetsAlarms.add(
Stack(
children: <Widget>[
InkWell(
child: Container(
color: Color(0xff212121),
padding: EdgeInsets.all(10),
child: Column(
children: <Widget>[
// Alarm Name & Title
Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(width: 2),
)),
child: Row(
children: <Widget>[
Icon(Icons.alarm, color: Colors.yellow),
SizedBox(width: 20.0),
Text('$item',
style: TextStyle(
color: Color(0xffC1C1C1),
fontSize: 15.0,
fontWeight: FontWeight.w900)),
SizedBox(height: 5),
],
),
),
SizedBox(height: 10),
// Alarm Time & Toggle Switch
Container(
child: Row(
children: <Widget>[
Text(
'Time',
style: TextStyle(
fontSize: 30, color: Colors.white),
),
SizedBox(width: 20),
Text(
'AM / PM',
style: TextStyle(
fontSize: 20, color: Color(0xffB5B5B5)),
),
SizedBox(width: 150),
Icon(Icons.switch_camera, color: Colors.yellow),
],
),
),
// Alarm Repeat
Container(
child: Row(children: <Widget>[
Text(
'Repeat',
style: TextStyle(
fontSize: 11, color: Color(0xff616161)),
),
Container(
child: DaySelector(
value: null,
onChange: (value) {},
color: Colors.yellow[400],
mode: DaySelector.modeFull,
),
),
]),
),
],
),
),
onLongPress: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddAlarm(item: item)));
},
),
SizedBox(height: 180),
],
),
);
print(item);
});
}
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.black,
title: Text('Azy Alarm'),
centerTitle: true,
),
body: Container(
decoration: BoxDecoration(
image: const DecorationImage(
fit: BoxFit.cover,
image: AssetImage('assets/images/background_image(dark).png')),
),
// child: ListView(children: allWidgetsAlarms),
child: ListView.builder(
itemCount: allWidgetsAlarms.length,
padding: const EdgeInsets.all(8.0),
itemBuilder: (BuildContext context, int index) {
return allWidgetsAlarms[index];
}),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
backgroundColor: Colors.blue,
elevation: 0,
onPressed: _addAlarm,
),
);
});
}
}
So I think your issue is this line: allWidgetsAlarms.add( You construct allWidgetsAlarms in your builder, but the builder is not called again every time you Consumer rebuilds, hence it is just appending the new contents to the end of the list. To fix this, keep your original initialization of allWidgetsAlarms just at the top of the builder in your Consumer, add the following line:
allWidgetsAlarms = List<Widget>();
This will resert allWidgetsAlarms. Hope it helps!