How do i return a container based on a list of item selected in flutter? - flutter

I have a list of items
List<String> items = [
"All",
"Jobs",
"Messages",
"Customers",
];
int current = 0;
And this list is directly responsible for my tab bar:
When i tap an item in the Tab bar i want to return a different container on each of them?
How do i go about this in flutter?
I tried returning an if statement just before the container but it seems i don't get the statement correctly.
this is the container i want to return if the item user select is All, and then put conditions in place for the rest items.
this is how i put the condition but it gives me this error
My return statement and code -
current = 0 ??
Container(
margin: const EdgeInsets.only(top: 30),
height: MediaQuery.of(context).size.height * 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(
height: 10,
),
Text(
items[current],
style: GoogleFonts.laila(
fontWeight: FontWeight.w500,
fontSize: 30,
color: Colors.deepPurple),
),
],
),
),
current = 1 ?? Text('hello')
FULL WIDGET ADDED
class NotificationsView extends StatefulWidget {
#override
State<NotificationsView> createState() => _NotificationsViewState();
}
class _NotificationsViewState extends State<NotificationsView> {
final controller = Get.put(NotificationsController());
List<String> items = [
"All",
"Jobs",
"Messages",
"Customers",
];
int current = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Notifications".tr,
style: GoogleFonts.poppins(
color: Color(0xff000000),
fontSize: 16,
fontWeight: FontWeight.w600),
),
centerTitle: false,
backgroundColor: Colors.transparent,
elevation: 0,
automaticallyImplyLeading: false,
leadingWidth: 15,
leading: new IconButton(
icon: new Icon(Icons.arrow_back_ios, color: Color(0xff3498DB)),
onPressed: () => {Get.back()},
),
),
body: RefreshIndicator(
onRefresh: () async {
},
child: ListView(
primary: true,
children: <Widget>[
filter(),
],
),
),
);
}
Widget notificationsList() {
return Obx(() {
if (!controller.notifications.isNotEmpty) {
return CircularLoadingWidget(
height: 300,
onCompleteText: "Notification List is Empty".tr,
);
} else {
var _notifications = controller.notifications;
return ListView.separated(
itemCount: _notifications.length,
separatorBuilder: (context, index) {
return SizedBox(height: 7);
},
shrinkWrap: true,
primary: false,
itemBuilder: (context, index) {
var _notification = controller.notifications.elementAt(index);
if (_notification.data['message_id'] != null) {
return MessageNotificationItemWidget(
notification: _notification);
} else if (_notification.data['booking_id'] != null) {
return BookingNotificationItemWidget(
notification: _notification);
} else {
return NotificationItemWidget(
notification: _notification,
onDismissed: (notification) {
controller.removeNotification(notification);
},
onTap: (notification) async {
await controller.markAsReadNotification(notification);
},
);
}
});
}
});
}
Widget filter() {
return Container(
width: double.infinity,
margin: const EdgeInsets.all(5),
child: Column(
children: [
/// CUSTOM TABBAR
SizedBox(
width: double.infinity,
height: 60,
child: ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: items.length,
scrollDirection: Axis.horizontal,
itemBuilder: (ctx, index) {
return Column(
children: [
GestureDetector(
onTap: () {
setState(() {
current = index;
});
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
margin: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: current == index
? Color(0xff34495E)
: Color(0xffF5F5F5),
borderRadius: BorderRadius.circular(11),
),
child: Center(
child: Padding(
padding: const EdgeInsets.only(
left: 10.0, right: 10.0, top: 5, bottom: 5),
child: Text(
items[index],
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: current == index
? Colors.white
: Colors.grey),
),
),
),
),
),
],
);
}),
),
/// MAIN BODY
current = 0 ??
Container(
margin: const EdgeInsets.only(top: 30),
height: MediaQuery.of(context).size.height * 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(
height: 10,
),
Text(
items[current],
style: GoogleFonts.laila(
fontWeight: FontWeight.w500,
fontSize: 30,
color: Colors.deepPurple),
),
Padding(
padding: const EdgeInsets.only(
left: 20.0, right: 20.0, top: 20.0, bottom: 20),
child: Column(
children: [
Stack(
children: [
Row(
children: [
Container(
decoration: BoxDecoration(
color: Color(0xffEFFAFF),
borderRadius:
BorderRadius.circular(20)),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Image.asset(
'assets/icon/suitcase.png'),
),
),
SizedBox(
width: 15,
),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
'New Job started ',
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xff151515)),
),
Text(
'Tailoring for John Cletus ',
style: GoogleFonts.poppins(
fontSize: 10,
fontWeight: FontWeight.w400,
color: Color(0xff151515)),
),
],
),
Spacer(),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(2),
color: Color(0xffFFE8E8),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Urgent',
style: GoogleFonts.poppins(
color: Color(0xffC95E5E)),
),
),
),
],
),
],
),
Divider(
height: 5,
color: Color(0xffEFFAFF),
),
],
),
),
],
),
),
current = 1 ?? Text('hello')
],
),
);
}
}

You can use Builder if want to display different type of widget based on the current index.
Builder(
builder: (context) {
switch (current) {
case 0:
return Container(
margin: const EdgeInsets.only(top: 30),
height: MediaQuery.of(context).size.height * 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(
height: 10,
),
Text(
items[current],
style: GoogleFonts.laila(
fontWeight: FontWeight.w500,
fontSize: 30,
color: Colors.deepPurple),
),
],
),
);
case 1:
return Text('Hello');
default:
return SizedBox.shrink();
}
},
);

Below approach will solve your problem. If you need further assistance, please feel free to comment.
int _currentIndex = 0;
var _containers = <Widget>[
AllContainer(),
JobsContainer(),
MessagesContainer(),
CustomerContainer(),
];
Widget _bottomTab() {
return BottomNavigationBar(
currentIndex: _currentIndex,
onTap: _onItemTapped, //
type: BottomNavigationBarType.fixed,
selectedLabelStyle: const TextStyle(color: Colors.blue),
selectedItemColor: WAPrimaryColor,
unselectedLabelStyle: const TextStyle(color: Colors.blue),
unselectedItemColor: Colors.grey,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(label: 'All'),
BottomNavigationBarItem(
label: 'Jobs'),
BottomNavigationBarItem(
label: 'Messages'),
BottomNavigationBarItem( label: 'Customer'),
],
);
}
void _onItemTapped(int index) async {
print('bottom index::: $index');
setState(() {
_currentIndex = index;
});
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
bottomNavigationBar: _bottomTab(),
body: Center(child: _containers.elementAt(_currentIndex)),
),
);
}

You can use conditional if on widget level,
like
/// MAIN BODY
if (current == 0)
Container(
margin: const EdgeInsets.only(top: 30),
height: MediaQuery.of(context).size.height * 1,
...
),
if (current == 1) Text('hello')
Also can be use else if
if (current == 0)
Container(
margin: const EdgeInsets.only(top: 30),
height: MediaQuery.of(context).size.height * 1,
) //you shouldnt put coma
else if (current == 1) Text('hello')
],
),
);
But creating a separate method will be better instead of putting it here
Widget getWidget(int index) {
/// MAIN BODY
if (current == 0) // or switch case
return Container(
margin: const EdgeInsets.only(top: 30),
height: MediaQuery.of(context).size.height * 1,
); //you shouldnt put coma
else if (current == 1) return Text('hello');
return Text("default");
}
And call the method getWidget(current).
Also there are some widget like PageView, IndexedStack will help to organize the code structure

Related

Flutter : Get Value of Selected Container

I am trying to get the Text value of the selected container in a GridView. I am able to print the selected index. But I need to get the text value of the selected container so that I can save it in sharedPreferences and later get it and store it on the server.
Please assist. Your help will be gladly appreciated.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class QuickSurveyScreen extends StatefulWidget {
const QuickSurveyScreen({super.key});
#override
State<QuickSurveyScreen> createState() => _QuickSurveyScreenState();
}
class _QuickSurveyScreenState extends State<QuickSurveyScreen> {
int selectedIndex = -1;
List<String> gasUsage = [
"Heater",
"Stove",
"Geyser",
"Fireplace",
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return [
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.only(top: 120.0, right: 30, left: 30),
child: Column(
children: const [
Text(
"What do you use \nLP Gas for?",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold),
),
Padding(
padding: EdgeInsets.symmetric(
vertical: 16.0,
horizontal: 20,
),
child: Text(
"Please choose one or more options from below",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
),
),
),
],
),
))
];
},
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
children: [
Expanded(
child: GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1.0,
crossAxisSpacing: 20.0,
mainAxisSpacing: 20.0,
),
physics: const NeverScrollableScrollPhysics(),
itemCount: gasUsage.length,
itemBuilder: (BuildContext context, int index) {
return gasUsageContainer("", gasUsage[index], index);
}),
),
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
minimumSize: const Size(double.infinity, 65.0),
backgroundColor: const Color(0xFFF0A202),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(26.0),
),
textStyle: const TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
child: const Text('Continue'),
),
],
),
),
),
),
);
}
gasUsageContainer(String image, String name, int index) {
return GestureDetector(
onTap: () {
setState(() {
if (selectedIndex == index) {
selectedIndex = -1;
} else {
selectedIndex = index;
}
if (kDebugMode) {
print(selectedIndex);
}
});
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
padding: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: selectedIndex == index
? Colors.amber.shade50
: Colors.grey.shade200,
border: Border.all(
color: selectedIndex == index
? Colors.amber
: Colors.blue.withOpacity(0),
width: 2.0,
),
borderRadius: BorderRadius.circular(22.0),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
name,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: selectedIndex == index
? Colors.amber.shade800
: Colors.black,
),
)
]),
),
);
}
}
You can get it from List with index. Checkout the change I did for onTap in gasUsageContainer
onTap: () {
setState(() {
String? selected;
if (selectedIndex == index) {
selectedIndex = -1;
selected = null;
} else {
selectedIndex = index;
selected = gasUsage[index];//Here you get the value
}
if (kDebugMode) {
print('selectedIndex:$selectedIndex, selected: $selected');
}
});
}
Edited
class _QuickSurveyScreenState extends State<QuickSurveyScreen> {
final selectedItems = <String>[];
...
gasUsageContainer(String image, String name, int index) {
String item = gasUsage[index];
return GestureDetector(
onTap: () {
setState(() {
if (selectedItems.contains(item)) {
selectedItems.remove(item);
} else {
selectedItems.add(item);
}
if (kDebugMode) {
print('selectedItems: $selectedItems');
}
});
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
padding: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: selectedItems.contains(item)
? Colors.amber.shade50
: Colors.grey.shade200,
border: Border.all(
color: selectedItems.contains(item)
? Colors.amber
: Colors.blue.withOpacity(0),
width: 2.0,
),
borderRadius: BorderRadius.circular(22.0),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
name,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: selectedItems.contains(item)
? Colors.amber.shade800
: Colors.black,
),
)
]),
),
);
}
}

How do i correctly initialise a variable from a stateful widget to another widget in flutter and maintain state?

I have two widgets
JobsHeaderWidget
JobsView
JobsHeaderWidget is a stateful widget where i code all the logic and initialise int current = 0; in the state. In this same file, i have another class named CategoriesBuilder where i use switch cases to make sure at each switch case a different container is returned. ( a switch case for each tab )
This switch cases is now responsible for switching containers depending on the tab bar selected as seen in this image:
I will also drop the code snippet of the JobsHeaderWidget for better clarifications.
The problem is - when i use this CategoriesBuilder in same widget as the 'JobsHeaderWidget' it works.
But i don't want to use it in same widget cos of the logic of my design. I want to be able to use this builder in JobsView widget which is another dart file and it doesn't work maybe because of wrong approach.
I tried converting the JobsView to a stateful widget and initialising 'int current = 0;' but it doesn't work.
I also tried making int current = 0; global var, it worked but the state doesn't change when i select individual tab bars. ( I mean my switch cases don't seem to work ).
I have gone round stackoverflow for answers before asking this but can't find a solution.
Snippets of each widgets below.
JobsHeaderWidget
class JobsHeaderWidget extends StatefulWidget {
const JobsHeaderWidget({
Key key,
}) : super(key: key);
#override
State<JobsHeaderWidget> createState() => _JobsHeaderWidgetState();
}
class _JobsHeaderWidgetState extends State<JobsHeaderWidget> {
List<String> items = [
"All",
"Critical",
"Open",
"Closed",
"Overdue",
];
ValueChanged<int> onChange;
int current = 0;
List<DropdownMenuItem<String>> get dropdownItems {
List<DropdownMenuItem<String>> menuItems = [
DropdownMenuItem(
child: Text(
"Today",
),
value: "Today"),
];
return menuItems;
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 15.0, right: 15.0),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Jobs',
style: GoogleFonts.poppins(
color: Colors.black,
fontSize: 18,
fontWeight: FontWeight.w600),
),
Row(
children: [
Text(
'View Insights ',
style: GoogleFonts.poppins(
color: Color(0xff3498DB),
fontSize: 12,
fontWeight: FontWeight.w500),
),
Icon(
Icons.arrow_forward_ios,
color: Color(0xff3498DB),
size: 12,
),
],
),
SizedBox(
height: 10,
),
filterJobs(),
],
),
),
);
}
Widget filterJobs() {
String selectedValue = "Today";
return Column(
children: [
Container(
constraints: const BoxConstraints(maxWidth: 600, maxHeight: 100),
width: double.infinity,
child: IntrinsicWidth(
child: FittedBox(
fit: BoxFit.fitWidth,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (int i = 0; i < items.length; i++) ...[
GestureDetector(
onTap: () {
setState(() {
current = i;
});
},
child: AnimatedContainer(
height: 40,
duration: const Duration(milliseconds: 300),
margin: const EdgeInsets.all(5),
padding: const EdgeInsets.only(
left: 14.0, right: 14.0, top: 4, bottom: 4),
decoration: BoxDecoration(
color: current == i
? const Color(0xff34495E)
: const Color(0xffF5F5F5),
borderRadius: BorderRadius.circular(50),
),
child: Center(
child: Text(
items[i],
style: GoogleFonts.poppins(
fontSize: 15,
fontWeight: FontWeight.w500,
color:
current == i ? Colors.white : Colors.grey),
),
),
),
),
]
],
),
),
),
),
Divider(
color: Color(0xff34495E).withOpacity(0.2),
),
Row(
children: [
Text(
'All Jobs',
style:
GoogleFonts.poppins(fontSize: 9, fontWeight: FontWeight.w400),
),
SizedBox(
width: 5,
),
Text(
' * This Week',
style:
GoogleFonts.poppins(fontSize: 9, fontWeight: FontWeight.w400),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'25',
style: GoogleFonts.poppins(
fontSize: 20, fontWeight: FontWeight.w600),
),
Container(
height: 30,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(2),
color: Color(0xffF4F4F4)),
child: Padding(
padding: const EdgeInsets.only(
left: 8.0,
),
child: Row(
children: [
Container(
decoration: BoxDecoration(
color: Color(0xff34495E),
borderRadius: BorderRadius.circular(2)),
child: Icon(
Icons.tune,
size: 15,
color: Colors.white,
),
),
SizedBox(
width: 5,
),
DropdownMenuItem(
child: DropdownButtonHideUnderline(
child: Container(
child: DropdownButton(
isDense: true,
style: GoogleFonts.poppins(
fontSize: 10,
fontWeight: FontWeight.w500,
color: Color(0xff34495E),
),
onChanged: (value) {},
items: dropdownItems,
value: selectedValue,
),
),
),
),
],
),
),
),
],
),
//If i uncomment this line and use the category builder here, it works fine! CategoriesBuilder(current: current)
],
);
}
}
class CategoriesBuilder extends StatelessWidget {
const CategoriesBuilder({
Key key,
#required this.current,
}) : super(key: key);
final int current;
#override
Widget build(BuildContext context) {
return Builder(
builder: (context) {
switch (current) {
case 0:
return AllJobsListView();
case 1:
return CriticalJobsListView();
case 2:
return OpenJobsListView();
case 3:
return ClosedJobsListView();
case 4:
return OverdueJobsListView();
default:
return SizedBox.shrink();
}
},
);
}
}
JobsView
class JobsView extends StatefulWidget {
const JobsView({
Key key,
}) : super(key: key);
#override
State<JobsView> createState() => _JobsViewState();
}
class _JobsViewState extends State<JobsView> {
int current = 0;
#override
Widget build(BuildContext context) {
final controller = Get.put(EServicesController());
return Scaffold(
// floatingActionButton: new FloatingActionButton(
// child: new Icon(Icons.add, size: 32, color: Get.theme.primaryColor),
// onPressed: () => {Get.offAndToNamed(Routes.E_SERVICE_FORM)},
// backgroundColor: Get.theme.colorScheme.secondary,
// ),
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
body: RefreshIndicator(
onRefresh: () async {
Get.find<LaravelApiClient>().forceRefresh();
controller.refreshEServices(showMessage: true);
Get.find<LaravelApiClient>().unForceRefresh();
},
child: CustomScrollView(
controller: controller.scrollController,
physics: AlwaysScrollableScrollPhysics(),
shrinkWrap: false,
slivers: <Widget>[
SliverAppBar(
backgroundColor: Color(0xffFFFFFF),
expandedHeight: MediaQuery.of(context).size.height * 0.4,
elevation: 0.5,
primary: true,
pinned: false,
floating: false,
//iconTheme: IconThemeData(color: Get.theme.primaryColor),
// title: Text(
// "Jobs".tr,
// style: Get.textTheme.headline6
// .merge(TextStyle(color: Get.theme.primaryColor)),
// ),
centerTitle: false,
automaticallyImplyLeading: false,
// leading: new IconButton(
// icon: new Icon(Icons.arrow_back_ios,
// color: Get.theme.primaryColor),
// onPressed: () => {Get.back()},
// ),
actions: [
SearchButtonWidget(),
],
//bottom: HomeSearchBarWidget(),
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.parallax,
title: JobsHeaderWidget(),
)),
SliverToBoxAdapter(
child: Wrap(
children: [
//ServicesListWidget(),
// The state doesnt change here for some reasosns CategoriesBuilder(current: current)
],
),
),
],
),
),
);
}
}
Try this: keep your 'int current' within _JobsViewState as you have in your code.
When you call your JobsHeaderWidget , pass the function it will use to update the the value of the current variable; and rebuild the state from here.
Something like this:
class JobsHeaderWidget extends StatefulWidget {
final Function changeCurrentValue(int newValue);
const JobsHeaderWidget({
this.changeCurrentValue,
Key key,
}) : super(key: key);
#override
State<JobsHeaderWidget> createState() => _JobsHeaderWidgetState();
}
class _JobsHeaderWidgetState extends State<JobsHeaderWidget> {
#override
Widget build(BuildContext context) {
// Somewhere inside build, instead calling setState()
// call the function you passed to the widget
GestureDetector(
onTap: () {
changeCurrentValue(i);
},
)
}
}
class _JobsViewState extends State<JobsView> {
int current = 0;
void changeCurrentValue(int newValue) {
setState(() {
current = newValue;
});
}
#override
Widget build(BuildContext context) {
//somewhere inside build
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.parallax,
title: JobsHeaderWidget(changeCurrentValue: changeCurrentValue),
)),
}
}
After hours and even sleeping overnight on this question i came up with a work-around that works. (minor refactoring)
This was my approach :
Convert my JobsView widget to a stateful widget and put all my controllers in place.
Copied all my variables from JobHeaderWidget and put it in the state of my JobsView widget.
Instead of returning a widget in the title of my sliver app as thus :
flexibleSpace: FlexibleSpaceBar( collapseMode: CollapseMode.parallax, title: JobsHeaderWidget(), )),
I copied all of my code from the widget tree from JobsHeaderWidget and put converted to a method and replaced it in my title.
My builder CategoryBuilder was put in a separate then imported as i used it in my SliverAppAdapter .
Of cos i got rid of the unnecessary dart file JobsHeaderWidget.
FULL CODE BELOW
class JobsView extends StatefulWidget {
#override
State<JobsView> createState() => _JobsViewState();
}
class _JobsViewState extends State<JobsView> {
List<String> items = [
"All",
"Critical",
"Open",
"Closed",
"Overdue",
];
int current = 0;
List<DropdownMenuItem<String>> get dropdownItems {
List<DropdownMenuItem<String>> menuItems = [
DropdownMenuItem(
child: Text(
"Today",
),
value: "Today"),
];
return menuItems;
}
#override
Widget build(BuildContext context) {
final controller = Get.put(EServicesController());
return Scaffold(
// floatingActionButton: new FloatingActionButton(
// child: new Icon(Icons.add, size: 32, color: Get.theme.primaryColor),
// onPressed: () => {Get.offAndToNamed(Routes.E_SERVICE_FORM)},
// backgroundColor: Get.theme.colorScheme.secondary,
// ),
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
body: RefreshIndicator(
onRefresh: () async {
Get.find<LaravelApiClient>().forceRefresh();
controller.refreshEServices(showMessage: true);
Get.find<LaravelApiClient>().unForceRefresh();
},
child: CustomScrollView(
controller: controller.scrollController,
physics: AlwaysScrollableScrollPhysics(),
shrinkWrap: false,
slivers: <Widget>[
SliverAppBar(
backgroundColor: Color(0xffFFFFFF),
expandedHeight: MediaQuery.of(context).size.height * 0.4,
elevation: 0.5,
primary: true,
pinned: false,
floating: false,
//iconTheme: IconThemeData(color: Get.theme.primaryColor),
// title: Text(
// "Jobs".tr,
// style: Get.textTheme.headline6
// .merge(TextStyle(color: Get.theme.primaryColor)),
// ),
centerTitle: false,
automaticallyImplyLeading: false,
// leading: new IconButton(
// icon: new Icon(Icons.arrow_back_ios,
// color: Get.theme.primaryColor),
// onPressed: () => {Get.back()},
// ),
actions: [
SearchButtonWidget(),
],
//bottom: HomeSearchBarWidget(),
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.parallax,
title: mainHeader(),
)),
SliverToBoxAdapter(
child: Wrap(
children: [
//ServicesListWidget(),
CategoriesBuilder(current: current)
],
),
),
],
),
),
);
}
Padding mainHeader() {
return Padding(
padding: const EdgeInsets.only(left: 15.0, right: 15.0),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Jobs',
style: GoogleFonts.poppins(
color: Colors.black,
fontSize: 18,
fontWeight: FontWeight.w600),
),
Row(
children: [
Text(
'View Insights ',
style: GoogleFonts.poppins(
color: Color(0xff3498DB),
fontSize: 12,
fontWeight: FontWeight.w500),
),
Icon(
Icons.arrow_forward_ios,
color: Color(0xff3498DB),
size: 12,
),
],
),
SizedBox(
height: 10,
),
() {
String selectedValue = "Today";
return Column(
children: [
Container(
constraints:
const BoxConstraints(maxWidth: 600, maxHeight: 100),
width: double.infinity,
child: IntrinsicWidth(
child: FittedBox(
fit: BoxFit.fitWidth,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (int i = 0; i < items.length; i++) ...[
GestureDetector(
onTap: () {
setState(() {
current = i;
});
},
child: AnimatedContainer(
height: 40,
duration: const Duration(milliseconds: 300),
margin: const EdgeInsets.all(5),
padding: const EdgeInsets.only(
left: 14.0,
right: 14.0,
top: 4,
bottom: 4),
decoration: BoxDecoration(
color: current == i
? const Color(0xff34495E)
: const Color(0xffF5F5F5),
borderRadius: BorderRadius.circular(50),
),
child: Center(
child: Text(
items[i],
style: GoogleFonts.poppins(
fontSize: 15,
fontWeight: FontWeight.w500,
color: current == i
? Colors.white
: Colors.grey),
),
),
),
),
]
],
),
),
),
),
Divider(
color: Color(0xff34495E).withOpacity(0.2),
),
Row(
children: [
Text(
'All Jobs',
style: GoogleFonts.poppins(
fontSize: 9, fontWeight: FontWeight.w400),
),
SizedBox(
width: 5,
),
Text(
' * This Week',
style: GoogleFonts.poppins(
fontSize: 9, fontWeight: FontWeight.w400),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'25',
style: GoogleFonts.poppins(
fontSize: 20, fontWeight: FontWeight.w600),
),
Container(
height: 30,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(2),
color: Color(0xffF4F4F4)),
child: Padding(
padding: const EdgeInsets.only(
left: 8.0,
),
child: Row(
children: [
Container(
decoration: BoxDecoration(
color: Color(0xff34495E),
borderRadius: BorderRadius.circular(2)),
child: Icon(
Icons.tune,
size: 15,
color: Colors.white,
),
),
SizedBox(
width: 5,
),
DropdownMenuItem(
child: DropdownButtonHideUnderline(
child: Container(
child: DropdownButton(
isDense: true,
style: GoogleFonts.poppins(
fontSize: 10,
fontWeight: FontWeight.w500,
color: Color(0xff34495E),
),
onChanged: (value) {},
items: dropdownItems,
value: selectedValue,
),
),
),
),
],
),
),
),
],
),
//CategoriesBuilder(current: current)
],
);
}(),
],
),
),
);
}
}

Flutter - codes not working correctly after putting inside setState

I am trying to add to an array list after user clicked. I am using InkWell, onTap function.
child: InkWell(
onTap: () {
if (tempArray.contains(entries[index].toString())) {
tempArray.remove(entries[index].toString());
print(tempArray.toList().toString());
} else {
tempArray.add(entries[index].toString());
print(tempArray.toList().toString());
}
My debug console is printing these out,
enter image description here
However, when i put the codes inside a setState, this is what got printed
child: InkWell(
onTap: () {
setState(() {
if (tempArray.contains(entries[index].toString())) {
tempArray.remove(entries[index].toString());
print(tempArray.toList().toString());
} else {
tempArray.add(entries[index].toString());
print(tempArray.toList().toString());
}
});
enter image description here
What i am trying to do, is to show/hide the 'trailing' icon in the ListTile, based on whatever user had selected the particular item.
My full codes (without the setState) are as follows,
import 'package:flutter/material.dart';
class Personal1Redo extends StatefulWidget {
#override
_Personal1RedoState createState() => _Personal1RedoState();
}
class _Personal1RedoState extends State<Personal1Redo> {
#override
Widget build(BuildContext context) {
final mediaQuery = MediaQuery.of(context);
List<String> entries = [
'Residental Loan',
'Personal Loan',
'Car Loan',
'Renovation Loan',
'Savings Account',
];
List<String> tempArray = [];
final heading = Column(
children: [
const SizedBox(
height: 40,
),
SizedBox(
height: (mediaQuery.size.height - mediaQuery.padding.top) * 0.1,
child: const Align(
alignment: Alignment.center,
child: Text(
'What do you aspire to do?',
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
),
),
const SizedBox(
height: 20,
),
],
);
return Scaffold(
appBar: AppBar(
title: Text('Borrower redo'),
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
heading,
ListView.builder(
shrinkWrap: true,
itemCount: entries.length,
itemBuilder: (ctx, index) {
return Container(
width: mediaQuery.size.width * 0.9,
padding: const EdgeInsets.all(15),
child: InkWell(
onTap: () {
if (tempArray.contains(entries[index].toString())) {
tempArray.remove(entries[index].toString());
print(tempArray.toList().toString());
} else {
tempArray.add(entries[index].toString());
print(tempArray.toList().toString());
}
},
child: Card(
margin: const EdgeInsets.all(10),
elevation: 8,
child: ListTile(
title: Text(
entries[index],
style: TextStyle(
color: Colors.grey.shade800,
fontSize: 20,
),
textAlign: TextAlign.center,
),
trailing: tempArray.contains(entries[index])
? Icon(Icons.check_box_outline_blank_outlined)
: null,
),
),
),
);
})
],
),
),
);
}
}
Any helps and guidance is very much appreciated, thanks!!
Define variables and functions outside build method.
As setState method, calls build, every time it is called.
Like this :
import 'package:flutter/material.dart';
class Personal1Redo extends StatefulWidget {
#override
_Personal1RedoState createState() => _Personal1RedoState();
}
class _Personal1RedoState extends State<Personal1Redo> {
List<String> entries = [
'Residental Loan',
'Personal Loan',
'Car Loan',
'Renovation Loan',
'Savings Account',
];
List<String> tempArray = [];
getHeadingWidget(BuildContext context) {
return Column(
children: [
const SizedBox(
height: 40,
),
SizedBox(
height: (mediaQuery!.size.height - mediaQuery.padding.top) * 0.1,
child: const Align(
alignment: Alignment.center,
child: Text(
'What do you aspire to do?',
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
),
),
const SizedBox(
height: 20,
),
],
);
}
#override
Widget build(BuildContext context) {
final mediaQuery = MediaQuery.of(context);
return Scaffold(
appBar: AppBar(
title: Text('Borrower redo'),
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
getHeadingWidget(context),
ListView.builder(
shrinkWrap: true,
itemCount: entries.length,
itemBuilder: (ctx, index) {
return Container(
width: mediaQuery.size.width * 0.9,
padding: const EdgeInsets.all(15),
child: InkWell(
onTap: () {
if (tempArray.contains(entries[index].toString())) {
tempArray.remove(entries[index].toString());
print(tempArray.toList().toString());
} else {
tempArray.add(entries[index].toString());
print(tempArray.toList().toString());
}
},
child: Card(
margin: const EdgeInsets.all(10),
elevation: 8,
child: ListTile(
title: Text(
entries[index],
style: TextStyle(
color: Colors.grey.shade800,
fontSize: 20,
),
textAlign: TextAlign.center,
),
trailing: tempArray.contains(entries[index])
? Icon(Icons.check_box_outline_blank_outlined)
: null,
),
),
),
);
})
],
),
),
);
}
}

Change a value from a List item and mantain it when changing pages on a page view

I get the length of the data in a page view.builder and then generate a List with all values being false (quantity = data length):
List<bool> isLockedList = List.generate(dataLength, (index) => false);
On a GestureDetector, on onTap, I have:
onTap: () {
setState(() {
isLockedList[index] = true;
});
print(isLockedList);
}
When I click on the item index 0 from the page view builder, which the length is 2, the List changes to [true, false]. When I go index 1, it changes to [false, true]. Why isn't it changing to [true, true]. Wasn't the value supposed to be overwritten?
When I change pages, without using the onTap, the List is reset to default values [false,false]
Entire code:
late final Stream<QuerySnapshot> _usersStream = FirebaseFirestore.instance
.collection('systems')
.doc(widget.systemRef)
.collection('matters')
.doc(widget.matterRef)
.collection('questions')
.snapshots();
#override
Widget build(BuildContext context) {
mediaQuery = MediaQuery.of(context).size;
int totalQuestion = 0;
int currentQuestion = 0;
return StreamBuilder<QuerySnapshot>(
stream: _usersStream,
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Scaffold(
body: Center(
child: Text('Algo deu errado'),
),
);
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
int dataLength = snapshot.data!.docs.length;
List<bool> isLockedList = List.generate(dataLength, (index) => false);
return Scaffold(
appBar: AppBar(
shadowColor: Colors.transparent,
backgroundColor: Colors.white,
leading: Navigator.canPop(context)
? IconButton(
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
icon: const Icon(
Icons.chevron_left,
color: Colors.black,
size: 40,
),
onPressed: () => Navigator.of(context).pop(),
)
: null,
actions: [
Padding(
padding: EdgeInsets.only(right: mediaQuery.width *0.05),
child: Center(child: Text('$currentQuestion /$totalQuestion', style: const TextStyle(color: Colors.black),)),
),
],
),
body: SafeArea(
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.white,
child: PageView.builder(
onPageChanged: (value) {
print(isLockedList);
},
controller: _pageController,
itemCount: dataLength,
itemBuilder: (ctx, index) {
refIndex = index;
currentQuestion = index + 1;
totalQuestion = dataLength;
QueryDocumentSnapshot<Object?> output =
snapshot.data!.docs[index];
List<String> alternativesCode = List<String>.generate(
output['numberOfOptions'],
(int index) => 'option${index + 1}.code');
List<String> alternativesText = List<String>.generate(
output['numberOfOptions'],
(int index) => 'option${index + 1}.text');
imageUrl = output['image'];
return Container(
width: double.infinity,
padding: EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width * 0.05),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// SizedBox(
// width: mediaQuery.width,
// child: Text(
// '${index + 1}/$dataLength',
// textAlign: TextAlign.end,
// style: const TextStyle(
// fontSize: 18, fontWeight: FontWeight.w600),
// ),
// ),
Container(
constraints: BoxConstraints(
maxHeight: mediaQuery.height * 0.16,
minHeight: mediaQuery.height * 0.05),
child: AutoSizeText(
output['title'],
maxLines: 4,
textAlign: TextAlign.start,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 23),
),
),
SizedBox(height: mediaQuery.height * 0.03),
const SizedBox(
height: 10,
),
imageUrl.isNotEmpty
? Container(
alignment: Alignment.center,
child: Image.network(imageUrl,
height: mediaQuery.height * 0.2))
: SizedBox(
height: mediaQuery.height * 0.05,
),
SizedBox(
height: mediaQuery.height * 0.03,
),
Container(
constraints:
BoxConstraints(maxHeight: mediaQuery.height * 0.1, maxWidth: mediaQuery.width * 0.9),
child: AutoSizeText(
output['subtitle'],
maxLines: 2,
textAlign: TextAlign.start,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
fontStyle: FontStyle.italic),
),
),
SizedBox(
height: mediaQuery.height * 0.02,
),
Container(
height: mediaQuery.height * 0.37,
width: double.infinity,
color: Colors.white,
child: ListView.builder(
physics: const NeverScrollableScrollPhysics(),
itemCount: alternativesText.length,
itemBuilder: (ctx, i) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 5),
child: GestureDetector(
child: Container(
height: mediaQuery.height * 0.07,
decoration: BoxDecoration(
boxShadow: const [
BoxShadow(
color: Colors.grey,
blurRadius: 3,
// Shadow position
),
],
border: Border.all(
width: 3, color: Colors.grey),
borderRadius: BorderRadius.circular(10),
color: Colors.grey[100],
),
// width: mediaQuery.width,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10),
child: Row(
children: [
Text(
'${output[alternativesCode[i]]}',
style: TextStyle(
color: Colors.grey.shade800,
fontWeight: FontWeight.w600,
fontSize: 20),
),
Container(
padding:
const EdgeInsets.only(left: 5),
alignment: Alignment.centerLeft,
width: mediaQuery.width * 0.78,
height: mediaQuery.height * 0.07,
// constraints: BoxConstraints(maxWidth: mediaQuery.width * 0.8, minWidth: mediaQuery),
child: AutoSizeText(
output[alternativesText[i]],
maxLines: 2,
textAlign: TextAlign.justify,
style: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.w500,
fontSize: 15),
),
),
],
),
),
),
onTap: () {
setState(() {
isLockedList[index] = true;
});
print(isLockedList);
}),
);
},
),
),
],
),
);
},
),
),
),
);
},
);
}
}
Follow these steps.
Declare isLockedList outside the build method as a nullable.
Set isLockedList only when the value is null in the StreamBuilder.
Code:
List<bool>? isLockedList;
#override
Widget build(BuildContext context){
....
return StreamBuilder<QuerySnapshot>(
builder: (context, snapshot) {
...
if (snapshot.hasError) {
return const Scaffold(
body: Center(
child: Text('Algo deu errado'),
),
);
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
int dataLength = snapshot.data!.docs.length;
if(isLockedList == null){
isLockedList = List.generate(dataLength, (index) => false);
}
....
}
);
....
}

flutter Problem same quantity show on products

when I am increasing my quantity of one product then all product's quantity is increasing, so how to solve it.
This is my product page:
import 'dart:convert';
import 'package:carousel_pro/carousel_pro.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:hospital/Authentication/LoginLogoutScreenPage/login.dart';
import 'package:hospital/CartPage/Cart_Api/cart_api.dart';
import 'package:hospital/CartPage/pages/cartPage.dart';
import 'package:hospital/Drawer/dropdown_menu.dart';
import 'package:hospital/ProductDetailsPage/product_detailPage.dart';
import 'package:hospital/ProductDetailsPage/related_product_page.dart';
import 'package:hospital/SecondSection/Medicine/medicine_page.dart';
import 'package:hospital/constant.dart';
import 'package:hospital/customApiVariable.dart';
import 'package:http/http.dart' as http;
import 'package:line_icons/line_icons.dart';
import 'package:provider/provider.dart';
class DetailPage extends StatefulWidget {
final plistId;
const DetailPage({Key key, this.plistId}) : super(key: key);
#override
_DetailPageState createState() => _DetailPageState();
}
class _DetailPageState extends State<DetailPage> {
final GlobalKey<FormState> _formKey = GlobalKey();
int quantity = 1;
var response;
var detailPageApi;
#override
void initState() {
super.initState();
fetchData(widget.plistId);
}
fetchData(var consultWithDoctor) async {
var api = Uri.parse(
'$ecommerceBaseUrl/productListApi.php?a2rTokenKey=$a2rTokenKey&plistId=${widget.plistId}');
response = await http.get(
api,
);
print("detailPageApi " + api.toString());
print("detailPageBody " + response.body);
detailPageApi = jsonDecode(response.body);
print("detailPagelist " + detailPageApi.toString());
setState(() {});
}
Future _submit() async {
var errorMessage = 'Authentication Failed';
if (successfully_add_cart_status.toString() == 'false') {
errorMessage = 'Please try again later';
print(errorMessage);
_showerrorDialog(errorMessage);
} else {
errorMessage = 'Product Succesfully Added to Cart';
print(errorMessage);
_showerrorDialog(errorMessage);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: kGreen,
title: Text(
"Details",
style: TextStyle(fontStyle: FontStyle.italic),
),
actions: [
IconButton(
icon: Icon(Icons.shopping_cart),
// onPressed: () => print("open cart"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Cartpage()),
);
},
),
DropDownMenu(),
],
),
body: Container(
child: response != null
? ListView.builder(
itemCount: detailPageApi.length.clamp(0, 1),
scrollDirection: Axis.vertical,
physics: ScrollPhysics(),
shrinkWrap: true,
itemBuilder: (context, index) {
var details = detailPageApi[index];
if (details['num'] == 0) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.white,
),
);
}
return Column(
children: <Widget>[
Hero(
tag: "1",
child: SizedBox(
height: 300.0,
width: 300.0,
child: Carousel(
boxFit: BoxFit.cover,
autoplay: false,
animationCurve: Curves.fastOutSlowIn,
animationDuration: Duration(milliseconds: 800),
dotSize: 6.0,
dotIncreasedColor: Colors.black,
dotBgColor: Colors.transparent,
// dotPosition: DotPosition.topRight,
dotVerticalPadding: 10.0,
showIndicator: true,
indicatorBgPadding: 7.0,
images: [
NetworkImage(details['pImgImg']),
],
),
),
),
SizedBox(
height: 50,
),
Padding(
padding: const EdgeInsets.only(left: 20, right: 20),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Name :",
style: TextStyle(
fontSize: 18,
height: 1.5,
fontWeight: FontWeight.w500),
),
SizedBox(
width: 20,
),
Flexible(
child: Text(
// widget.details,
details['productName'],
style: TextStyle(
fontSize: 17,
height: 1.5,
fontWeight: FontWeight.w500),
),
),
],
),
),
SizedBox(
height: 20,
),
Padding(
padding: const EdgeInsets.only(left: 20, right: 20),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Details :",
style: TextStyle(
fontSize: 18,
height: 1.5,
fontWeight: FontWeight.w500),
),
SizedBox(
width: 20,
),
Flexible(
child: Text(
// widget.details,
details['productDescription'],
style: TextStyle(
fontSize: 17,
height: 1.5,
fontWeight: FontWeight.w500),
),
),
],
),
),
SizedBox(
height: 20,
),
Padding(
padding: const EdgeInsets.only(left: 20, right: 20),
child: Row(
children: <Widget>[
Text(
"Price :",
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.w500),
),
SizedBox(
width: 20,
),
Row(
children: <Widget>[
Text(
// "Rs " + widget.pPromotionPrice,
"Rs 55.0",
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w500),
),
SizedBox(
width: 20,
),
Text(
// "Rs " + widget.pPrice,
"Rs 100",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
// color: warning,
decoration: TextDecoration.lineThrough),
)
],
)
],
),
),
SizedBox(
height: 25,
),
Padding(
padding: const EdgeInsets.only(right: 20, left: 20),
child: Row(
children: <Widget>[
Text(
"Qty :",
style: TextStyle(
fontSize: 17, fontWeight: FontWeight.w500),
),
SizedBox(
width: 20,
),
Row(
children: <Widget>[
InkWell(
onTap: () {
if (quantity > 1) {
setState(() {
quantity = --quantity;
});
}
// minus here
},
child: Container(
width: 25,
height: 25,
decoration: BoxDecoration(
// border: Border.all(color: primary),
shape: BoxShape.circle),
child: Icon(
LineIcons.minus,
size: 15,
),
),
),
SizedBox(
width: 15,
),
Text(
quantity.toString(),
style: TextStyle(fontSize: 16),
),
SizedBox(
width: 15,
),
InkWell(
onTap: () {
setState(() {
quantity = ++quantity;
});
// minus here
},
child: Container(
width: 25,
height: 25,
decoration: BoxDecoration(
// border: Border.all(color: primary),
shape: BoxShape.circle),
child: Icon(
LineIcons.plus,
size: 15,
),
),
),
],
)
],
),
),
SizedBox(
height: 50,
),
InkWell(
onTap: () async {
if (var_uid.toString() != null.toString()) {
_submit();
var qty = quantity.toString();
var plistId = widget.plistId;
print('pplistid' + plistId);
var uid = var_uid.toString();
var sid = var_sid.toString();
print('uuid' + uid);
print('ssid' + sid);
var response =
await add_to_cart_fn(qty, plistId, uid, sid);
print("rsp: " + response['msg']);
} else {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => LoginPage()));
}
},
// },
child: Padding(
padding: EdgeInsets.only(left: 20, right: 20),
child: Container(
height: 45,
width: double.infinity,
decoration: BoxDecoration(
color: kGreen,
borderRadius: BorderRadius.circular(30)),
child: Center(
child: Text(
"ADD TO CART",
style: TextStyle(
color: kWhite,
fontSize: 20,
),
),
),
),
),
),
SizedBox(height: 20.0),
RelatedProductPage(plistId: widget.plistId)
],
);
})
: Center(
child: CircularProgressIndicator(
backgroundColor: Colors.white,
),
),
),
);
}
void _showerrorDialog(String message) {
Fluttertoast.showToast(
msg: message,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
timeInSecForIosWeb: 1,
backgroundColor: Colors.grey[350],
textColor: Colors.black,
fontSize: 16.0);
}
}
You are using the same quantity variable against each item. Changing the value on one of those will cause all items to update. What you need to do is to keep a Map of quantity selected against product id. That way whenever you increase or decrease the quantity, you update the quantity against that specific product id and the rest will remain unchanged.
EDIT: Here is how this will work
//Create a new map to save the product id and product selected quantity
var productQuantity = new Map<int, int>()
//When setting the values of the product, set the quantity from the map
Padding(
padding: const EdgeInsets.only(right: 20, left: 20),
child: Row(
children: <Widget>[
Text(
"Qty :",
style: TextStyle(
fontSize: 17, fontWeight: FontWeight.w500),
),
SizedBox(width: 20,),
Row(
children: <Widget>[
InkWell(
onTap: () {
if (quantity > 1) {
setState(() {
//Changed here
productQuantity['$productId'] -= 1;
});
}
},
child: Container(
width: 25,
height: 25,
decoration: BoxDecoration(shape: BoxShape.circle),
child: Icon(
LineIcons.minus,
size: 15,
),
),
),
SizedBox(width: 15,),
Text(
//Changed here
productQuantity['$productId'].toString(),
style: TextStyle(fontSize: 16),
),
SizedBox(width: 15,),
InkWell(
onTap: () {
setState(() {
//Changed here
productQuantity['$productId'] += 1;
});
},
child: Container(
width: 25,
height: 25,
decoration: BoxDecoration(
shape: BoxShape.circle),
),
child: Icon(
LineIcons.plus,
size: 15,
),
),
),
],
)
],
),
)
you set the common quantity in all products so change it and set it inside the class.
Please Check the example
import 'package:flutter/material.dart';
class QuantityUpdatePage extends StatefulWidget {
#override
_QuantityUpdatePageState createState() => _QuantityUpdatePageState();
}
class _QuantityUpdatePageState extends State<QuantityUpdatePage> {
List<Product> productArray = [];
#override
void initState() {
super.initState();
WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
productArray.clear();
productArray.add(Product(1, "Product 1", 1));
productArray.add(Product(2, "Product 2", 1));
productArray.add(Product(3, "Product 3", 1));
productArray.add(Product(4, "Product 4", 1));
productArray.add(Product(5, "Product 5", 1));
setState(() {});
});
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("QUANTITY UPDATE")),
body: ListView.builder(
shrinkWrap: true,
itemCount: productArray.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(right: 20, left: 20),
child: ListView(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
children: [
SizedBox(
height: 20,
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(productArray[index].productName),
SizedBox(
width: 40,
),
Row(
children: <Widget>[
Text(
"Qty :",
style: TextStyle(fontSize: 17, fontWeight: FontWeight.w500),
),
SizedBox(
width: 20,
),
Row(
children: <Widget>[
InkWell(
onTap: () {
if (productArray[index].quantity > 1) {
setState(() {
productArray[index].quantity = --productArray[index].quantity;
});
}
// minus here
},
child: Container(
width: 25,
height: 25,
decoration: BoxDecoration(
// border: Border.all(color: primary),
shape: BoxShape.circle),
child: Icon(
Icons.minimize,
size: 15,
),
),
),
SizedBox(
width: 15,
),
Text(
productArray[index].quantity.toString(),
style: TextStyle(fontSize: 16),
),
SizedBox(
width: 15,
),
InkWell(
onTap: () {
setState(() {
productArray[index].quantity = ++productArray[index].quantity;
});
// minus here
},
child: Container(
width: 25,
height: 25,
decoration: BoxDecoration(
// border: Border.all(color: primary),
shape: BoxShape.circle),
child: Icon(
Icons.add,
size: 15,
),
),
),
],
)
],
),
],
),
SizedBox(
height: 20,
),
],
),
);
}));
}
}
class Product {
String productName;
int quantity;
int id;
Product(this.id, this.productName, this.quantity);
}
#Deepak if you don't understand Maps use list instead, with a value being updated for each index. Initialize a list of int types with some large values like 1000000, and update the value for each index. use ListView builder to fetch the data from the API for each index.
List<int> quantity = List.empty(growable: true);
OR
List<int> quantity = List.filled(10000, []);
For updating quantity:-
quantity[index] += 1;
For Grid view thing, refer this example from CodeGrepper:-
GridView.count(
// Create a grid with 2 columns. If you change the scrollDirection to
// horizontal, this produces 2 rows.
crossAxisCount: 2,
// Generate 100 widgets that display their index in the List.
children: List.generate(100, (index) {
return Center(
child: Text(
'Item $index',
style: Theme.of(context).textTheme.headline5,
),
);
}),
);