How to Remove cart item using provider - flutter

I am using provider class in which i can add items to the cart list using product id but when i use the similar function to remove the item from cart it seems the function dont find the product id.or im doing somting wrong.
The function i used to add items in provider class:
//---------------------------------------add item
void addItem({
required CartItem product,
}) {
if (items.containsKey(product.id)) {
final double value = product.price;
items.update(
product.id,
(cartitem) => cartitem.copy(
qnt: cartitem.qnt + 1,
price: cartitem.price + value,
),
);
} else {
items.putIfAbsent(
product.id,
() => product.copy(
id: DateTime.now().toString(),
qnt: 1,
));
}
//---------------------------------------------------------total price
_totalPrice = _totalPrice + product.price;
notifyListeners();
}
and the list in my cart:
//----------- items cart list
Widget buildCarditems(BuildContext context) {
final porvidr = Provider.of<ShopProvider>(context, listen: false);
if (porvidr.items.isEmpty) {
return Center(
child: Text(
'Cart is Empty',
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
),
);
} else {
return ListView(
children: porvidr.items.values.map(buildcarditem).toList(),
);
}
}
//------------cart listtile
Widget buildcarditem(CartItem c_item) {
return ListTile(
leading: CircleAvatar(backgroundImage: AssetImage(c_item.imgUrl)),
title: Row(
children: [
Text(
'${c_item.qnt}x',
style: TextStyle(color: Colors.white),
),
SizedBox(
width: 10,
),
Expanded(
child: Text(
c_item.title,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
],
),
trailing: Text(
'\$${c_item.price}',
style: TextStyle(
color: Colors.white,
),
));
}
where can i use the ontap function to get the product id?

You can add it anywhere you want on each list item, you can wrap the list tile in n gesture detector widget. it depends on the requirement.

Related

How to control text overflow in Flutter when using a Text widget in a dropdown?

I'm using the next piece of code to render an item for a Dropdown component on Flutter web:
Widget _getColorInfo(int color, String text) {
return Row(
children: [
Container(
width: 10,
height: 10,
decoration: BoxDecoration(
color: Color(color),
borderRadius: BorderRadius.circular(5),
),
),
const SizedBox(width: 5),
Text(
text,
style: TextStyle(
color: Color(color),
overflow: TextOverflow.ellipsis,
fontWeight: FontWeight.bold,
fontSize: ShapeStyle.standardFontSize,
),
),
],
);
}
Now the problem is that if I do not use Flex or Expanded the widget will not recognize the overflow:
Overflow on the selected item
And the dropdown is working:
Overflow on the expanded panel
Now, if I use Flex the overflow is now recognized by the main component:
Flexible(
child: Text(
text,
style: TextStyle(
color: Color(color),
overflow: TextOverflow.ellipsis,
fontWeight: FontWeight.bold,
fontSize: ShapeStyle.standardFontSize,
),
),
),
Overlfow handled on the selected item
But now the dropdown is not working since it detects the 'size' property is missing:
Error on the expanded panel of the dropdown
Here is the error:
Image of the error
The problem is that I can't use the size of the screen to determine how much width is the component taking since all the other components are being expanded dynamically, so it will break the UI symmetry. Is there any other way to control the overflow when using a dropdown or something to differentiate the dropdown panel from the selected item so I can apply different strategies like using a fixed width for the dropdown panel and a Flex for the selected item?
I have already tried using Flex, Expanded, try catch, Wrap, ConstrainedBox, third party packages... nothing solved the problem.
UPDATE
Here is the full code of the component:
class ColorSelect extends StatelessWidget {
final AgendaFormHandler form;
final int? defaultColor;
final Map<String, int> elements = {
'Team 1': 0xFFFEC000,
'Team 2': 0xFF65CBFD,
'Team 3': 0xFFF2B085,
'Team 4': 0xFF01AE51,
'USO': 0xFF3764F7,
'Financiero': 0xFF702FA0,
'Procesos': 0xFF8FA9DA,
'TI': 0xFFFF7C81,
'Alterno 1': 0xFF00F3EB,
'Alterno 2': 0xFFA46B6B,
'Alterno 3': 0xFFDCBFB2,
'Alterno 4': 0xFFEF5CEF,
'Alterno 5': 0xFFC5D89F,
};
ColorSelect({super.key, required this.form, this.defaultColor});
#override
Widget build(BuildContext context) {
return SimpleSelectInput<int>(
title: 'Color',
customEmptyText: 'Ninguno',
activeOption: form.getValue<int?>('color') ?? defaultColor ?? 0xFFFEC000,
onOptionSelected: (color) {
form.updateValue('color', color);
},
options: List<SelectItem<int>>.from(elements.keys.map((key) {
return SelectItem(
value: elements[key]!,
text: key,
label: _getColorInfo(elements[key]!, key),
);
})),
);
}
Widget _getColorInfo(int color, String text) {
return Row(
children: [
Container(
width: 10,
height: 10,
decoration: BoxDecoration(
color: Color(color),
borderRadius: BorderRadius.circular(5),
),
),
const SizedBox(width: 5),
Flexible(
fit: FlexFit.tight,
child: Text(
text,
style: TextStyle(
color: Color(color),
overflow: TextOverflow.ellipsis,
fontWeight: FontWeight.bold,
fontSize: ShapeStyle.standardFontSize,
),
),
),
],
);
}
}
Here is the code of the SimpleSelectInput
/// A class to create a new option entry for the [SimpleSelectInput]
/// component with [T] as the datatype for the [value] and [String]
/// for the [label]
class SelectItem<T> {
final T value;
/// This text is only to be able to compare the content of the
/// input when applying any search filter. So, the same you put
/// in the [label] property is the one you should put here.
final String text;
final Widget? label;
SelectItem({
required this.value,
this.label,
required this.text,
});
}
/// A simple input with a bottom border and a blue title with
/// [T] as the value datatype for the dropdown options.
class SimpleSelectInput<T> extends StatelessWidget {
/// The options to be displayed in the dropdown
final List<SelectItem<T>> options;
/// The item to be rendered as the default selected item
final T activeOption;
final Function(T?) onOptionSelected;
final String Function(T?)? validator;
final String? title;
/// The text to be displayed when no option is selected
final String? customEmptyText;
final bool shouldDecorate;
final bool showSearchBox;
const SimpleSelectInput({
super.key,
required this.options,
required this.activeOption,
required this.onOptionSelected,
this.title,
this.validator,
this.shouldDecorate = true,
this.showSearchBox = false,
this.customEmptyText = '-- Seleccione --',
});
#override
Widget build(BuildContext context) {
if (activeOption == null) {
return Container();
}
// If there is no opportunity it will raise an error.
// TODO: check if it's ok to raise the BadState error.
late final SelectItem<T>? selectedOption;
try {
selectedOption = options.firstWhere(
(SelectItem<T> option) {
return option.value == activeOption;
},
);
} catch (_) {
selectedOption = null;
}
return DropdownSearch<SelectItem<T>>(
items: options,
selectedItem: selectedOption,
filterFn: (item, filter) {
return item.text.toLowerCase().contains(filter.toLowerCase());
},
dropdownButtonProps: const DropdownButtonProps(
focusColor: ColorStyle.blue,
color: Colors.black,
icon: Icon(
Icons.arrow_drop_down,
size: ShapeStyle.standardFontSize * 1.4,
),
),
dropdownBuilder: (context, value) {
return value?.label ??
Text(
customEmptyText ?? '-- Seleccione --',
style: const TextStyle(
fontSize: ShapeStyle.standardFontSize,
),
);
},
validator: (selected) {
return validator?.call(selected?.value);
},
popupProps: PopupPropsMultiSelection.menu(
showSearchBox: showSearchBox,
showSelectedItems: true,
itemBuilder: _itemBuilder,
searchFieldProps: const TextFieldProps(
scrollPadding: EdgeInsets.zero,
style: TextStyle(
color: ColorStyle.blue,
fontSize: ShapeStyle.standardFontSize,
),
),
),
compareFn: (i, s) => i.value == s.value,
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
fillColor: Colors.black,
border: shouldDecorate ? null : InputBorder.none,
labelStyle: const TextStyle(
color: ColorStyle.blue,
fontSize: ShapeStyle.standardFontSize * 1.4,
),
label: title != null
? Text(
title!,
style: const TextStyle(
fontSize: ShapeStyle.standardFontSize * 1.4,
),
)
: null,
),
),
onChanged: (x) => onOptionSelected(x?.value),
);
}
Widget _itemBuilder(
BuildContext context, SelectItem<T> item, bool isSelected) {
final size = MediaQuery.of(context).size;
return Container(
width: size.width,
padding: const EdgeInsets.symmetric(
vertical: 12.0,
horizontal: 8.0,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
item.label ??
Text(
item.text,
style: const TextStyle(
color: Colors.black,
fontSize: ShapeStyle.standardFontSize,
),
),
// a check icon
if (isSelected)
const Icon(
Icons.check,
color: Colors.green,
size: ShapeStyle.standardIconSize,
),
],
),
);
}
}

Flutter bloc doesn't showing UI as per proper state

Am using flutter bloc library and maintaining simple process for as per library for state management. In this code I am getting the state as expected but UI is not updating, its always returning the default state ui which a circle spinner . I have checked the state using bloc observer too. So need to know that what I have done wrong and need suggestions on that.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: buildAppBar(context),
body: Column(
children: [
BlocBuilder<UserManageCubit, UserManageState>(
builder: (context, state) {
if (state is Initial) {
return const Center(
child: CircularProgressIndicator(
color: Colors.red,
));
}
if (state is UserDetailFetched) {
Column(
children: <Widget>[
Text(
'${state.userDetails.name}'.toUpperCase(),
style: kNavyTextStyleNormal.copyWith(
fontSize: 18.sp,
),
),
Text(
'${state.userDetails.email}',
style: kNavyTextStyleNormal.copyWith(
fontWeight: FontWeight.bold,
),
),
Text(
'${state.userDetails.phone}',
style: kNavyTextStyleNormal.copyWith(
fontWeight: FontWeight.bold,
),
),
Text(
'Registered on ${DateTimeUtils.format(
DateTimeUtils.fromString(state.userDetails.createdAt!),
).split("-")[0]}',
style: kNavyTextStyleNormal.copyWith(
fontWeight: FontWeight.bold,
color: go11ThemeGreen,
),
),
SizedBox(height: 16.h),
],
);
}
return CircularProgressIndicator(
color: Colors.amber,
);
},
),
],
),
);
}
Calling bloc
#override
void initState() {
BlocProvider.of<UserManageCubit>(context).getUserDetails(widget.userId);
super.initState();
}
#Cubit impl
UserManageCubit({required this.userManageRepo})
: super(const UserManageState.initial());
void getUserDetails(int userId) async {
final FormData formData = FormData.fromMap({
'user_id': userId,
});
final Either<Failure, UserDetailsResponse> userDetailResponseEither =
await userManageRepo.getUserDetails(formData);
emit(const UserManageState.loading());
userDetailResponseEither.fold(
(failure) => {
emit(
UserManageState.loadFailure(
errorObject:
ErrorObject.mapFailureToErrorObject(failure: failure),
),
)
},
(response) => {
emit(
UserManageState.userDetailFetched(
userDetails: response.userDetails),
),
});
}
#state class
#freezed
class UserManageState with _$UserManageState {
const factory UserManageState.initial() = Initial;
const factory UserManageState.loading() = Loading;
const factory UserManageState.loadFailure({
required ErrorObject errorObject,
}) = LoadFailure;
const factory UserManageState.userDetailFetched(
{required UserDetails userDetails,
#Default(0) int currentDocType,
#Default(0) int currentCancelType}) = UserDetailFetched;
}
You are not returning your column
if (state is UserDetailFetched) {
return Column(
children: <Widget>[
Text(
'${state.userDetails.name}'.toUpperCase(),
style: kNavyTextStyleNormal.copyWith(
fontSize: 18.sp,
),
),
Text(
'${state.userDetails.email}',
style: kNavyTextStyleNormal.copyWith(
fontWeight: FontWeight.bold,
),
),
Text(
'${state.userDetails.phone}',
style: kNavyTextStyleNormal.copyWith(
fontWeight: FontWeight.bold,
),
),
Text(
'Registered on ${DateTimeUtils.format(
DateTimeUtils.fromString(state.userDetails.createdAt!),
).split("-")[0]}',
style: kNavyTextStyleNormal.copyWith(
fontWeight: FontWeight.bold,
color: go11ThemeGreen,
),
),
SizedBox(height: 16.h),
],
);
}

How to change button color and move to home when selecting 5 checklists or selecting all in flutter

I'm making a contract terms page. Checkbox ListTile is being used.
There is a total agreement and six items.
If the first to fifth items of the Terms and Conditions are selected, the Elevated Button color should be changed to blue and made available to move home (screenshot).
If all items are selected, the Elevated Button color should be changed to blue and made to be able to move home (screenshot).
Other than that, Elevated Button is grey and cannot be moved home (screenshot).
I tried it, but it's so hard to set the index for each item
Is there a solution?
Thank you to those who answered.
class TermsAgreementPage extends StatelessWidget {
TermsAgreementPage({
Key? key,
}) : super(key: key);
CheckBoxState checkBoxState = Get.put(CheckBoxState(''));
final alltermsAndConditions = CheckBoxState('');
final termsAndConditions = [
CheckBoxState('이용 약관 동의(필수)', '니어엑스 서비스 이용 통합 약관입니다.'),
CheckBoxState('개인정보 처리방침 동의(필수)', '개인정보보호 포털 법률에 의거한 제공동의로 필수 사항입니다.'),
CheckBoxState('개인정보 제3자 제공동의(필수)', '개인정보보호 포털 법률에 의거한 제공 동의로 필수 사항입니다.'),
CheckBoxState('위치기반 서비스 이용약관(필수)', '주변 가게들 검색에 사용됩니다.'),
CheckBoxState('전자금융거래 이용약관(필수)', '구매 또는 결제 사항이 있을 경우 제공 동의로 필수 사항입니다.'),
CheckBoxState('니어엑스 혜택 알림 동의(선택)','미선택 시 주변가게 할인 및 만기 다가오는 쿠폰 알림 사용 불가.')
];
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Scaffold(
body: Padding(
padding: const EdgeInsets.only(top: 80.0, bottom: 40),
child: Column(
children: [
Expanded(
flex: 1,
child: SizedBox(
width: size.width,
child: Column(
children: const [
Text(
'이용 약관 동의',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
Text(
'아래의 약관에 동의 하신 후 서비스를 이용해 주시기 바랍니다.',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.grey),
),
],
),
),
),
Expanded(
flex: 6,
child: Obx(
() => ListView(
children: [
buildGroupCheckbox(CheckBoxState('전체동의')),
Divider(color: Colors.grey[300], height: 5, thickness: 3),
...termsAndConditions.map(buildCheckbox).toList(),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: termsAndConditions.every((termsAndConditions) => termsAndConditions.isChecked.value)
? Colors.blue
: Colors.grey[400],
padding: const EdgeInsets.symmetric(
horizontal: 150, vertical: 10),
textStyle: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w400,
color: Colors.white)),
onPressed: () {
if(alltermsAndConditions.isChecked.value) {
Get.toNamed('/home');
}
},
child: const Text('시작하기'),
),
],
),
),
),
],
),
),
);
}
Widget buildGroupCheckbox(CheckBoxState checkBoxState) {
return CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
secondary: TextButton(
onPressed: () {
Get.toNamed('/home');
},
child: const Text(
'전문보기',
style: TextStyle(fontSize: 10.0, color: Colors.blue),
)),
title: Text(
checkBoxState.title,
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
),
onChanged: toggleCheckBox,
value: alltermsAndConditions.isChecked.value,
);
}
void toggleCheckBox(bool? value) {
if (value == null) return;
alltermsAndConditions.isChecked.value = value;
for (var termsAndConditions in termsAndConditions) {
termsAndConditions.isChecked.value = value;
}
}
Widget buildCheckbox(CheckBoxState checkBoxState) {
return CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
title: Text(
checkBoxState.title,
style: const TextStyle(fontSize: 12),
),
subtitle: Text(
checkBoxState.subTitle,
style: const TextStyle(fontSize: 9, color: Colors.grey),
),
onChanged: (value) {
checkBoxState.isChecked.value = value!;
alltermsAndConditions.isChecked.value = termsAndConditions.every(
(termsAndConditions) => termsAndConditions.isChecked.value);
},
value: checkBoxState.isChecked.value // checkBoxState 의 isChecked 값임 (false 상태)
);
}
}
class CheckBoxState extends GetxController {
RxBool isChecked = false.obs;
String title;
String subTitle;
CheckBoxState(this.title,[this.subTitle = '']);
}
It's possible to use getRange to get the items from 0 to 4 (The first 5 items). And then, just iterate over every one of them to check if all of them are checked.
Just change the line:
primary: termsAndConditions.every((termsAndConditions) => termsAndConditions.isChecked.value)
? Colors.blue
: Colors.grey[400],
To the following:
primary: termsAndConditions.getRange(0, 4).every(
(termsAndConditions) => termsAndConditions.isChecked.value)
? Colors.blue
: Colors.grey[400],
And also the onPressed property from:
onPressed: () {
if (allTermsAndConditions.isChecked.value) {
Get.toNamed('/home');
}
},
To the following:
onPressed: () {
if (termsAndConditions.getRange(0, 4).every(
(termsAndConditions) =>
termsAndConditions.isChecked.value)) {
Get.toNamed('/home');
}
},

how to Activate Elevatedbutton when checkboxListTile is selected

I'm making a page of terms and conditions.
I'm using checkboxListTile.
enter image description here
However, the Elevated Button shall be activated when selecting all items from the first to fifth items of the termsAndConditions.
The last sixth item may or may not be selected.
Otherwise, Elevated Button is disabled and the color should appear gray.
enter image description here
and Elevated Button must be activated when alltermsAndConditions are selected.
enter image description here
It’s too difficult.
Is there a solution?
I'd like to thank the people who answer.
class TermsAgreementPage extends StatelessWidget {
TermsAgreementPage({
Key? key,
}) : super(key: key);
CheckBoxState checkBoxState = Get.put(CheckBoxState(title: '', subTitle: ''));
final alltermsAndConditions = CheckBoxState(title: '전체동의', subTitle: '');
final termsAndConditions = [
CheckBoxState(
title: '이용 약관 동의(필수)',
subTitle: '니어엑스 서비스 이용 통합 약관입니다.'),
CheckBoxState(
title: '개인정보 처리방침 동의(필수)',
subTitle: '개인정보보호 포털 법률에 의거한 제공동의로 필수 사항입니다.'),
CheckBoxState(
title: '개인정보 제3자 제공동의(필수)',
subTitle: '개인정보보호 포털 법률에 의거한 제공 동의로 필수 사항입니다.'),
CheckBoxState(
title: '위치기반 서비스 이용약관(필수)',
subTitle: '주변 가게들 검색에 사용됩니다.'),
CheckBoxState(
title: '전자금융거래 이용약관(필수)',
subTitle: '구매 또는 결제 사항이 있을 경우 제공 동의로 필수 사항입니다.'),
CheckBoxState(
title: '니어엑스 혜택 알림 동의(선택)',
subTitle: '미선택 시 주변가게 할인 및 만기 다가오는 쿠폰 알림 사용 불가.'),
];
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Scaffold(
body: Padding(
padding: const EdgeInsets.only(top: 80.0, bottom: 40),
child: Column(
children: [
Expanded(
flex: 1,
child: Container(
width: size.width,
child: Column(
children: const [
Text(
'이용 약관 동의',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
Text(
'아래의 약관에 동의 하신 후 서비스를 이용해 주시기 바랍니다.',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.grey),
),
],
),
),
),
Expanded(
flex: 6,
child: Obx(
() => ListView(
children: [
buildGroupCheckbox(
CheckBoxState(title: '전체동의', subTitle: '')),
const Divider(color: Colors.grey, height: 2),
...termsAndConditions.map(buildCheckbox).toList()
],
),
),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
padding:
const EdgeInsets.symmetric(horizontal: 50, vertical: 10),
textStyle: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w400,
color: Colors.white)),
onPressed: () {
},
child: const Text('시작하기'),
),
],
),
),
);
}
Widget buildGroupCheckbox(CheckBoxState checkBoxState) {
return CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
secondary: TextButton(
onPressed: () {
Get.toNamed('/home');
},
child: const Text(
'전문보기',
style: TextStyle(fontSize: 10.0, color: Colors.blue),
)),
title: Text(
checkBoxState.title,
style: const TextStyle(fontSize: 12),
),
subtitle: Text(
checkBoxState.subTitle,
style: const TextStyle(fontSize: 9, color: Colors.grey),
),
onChanged: toggleCheckBox,
value: alltermsAndConditions.isChecked.value,
);
}
void toggleCheckBox(bool? value) {
if (value == null) return;
alltermsAndConditions.isChecked.value = value;
for (var termsAndConditions in termsAndConditions) {
termsAndConditions.isChecked.value = value;
}
}
Widget buildCheckbox(CheckBoxState checkBoxState) {
return CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
// 왼쪽에 네모 박스 위치
title: Text(
checkBoxState.title,
style: const TextStyle(fontSize: 12),
),
subtitle: Text(
checkBoxState.subTitle,
style: const TextStyle(fontSize: 9, color: Colors.grey),
),
onChanged: (value) {
checkBoxState.isChecked.value = value!;
alltermsAndConditions.isChecked.value = termsAndConditions.every(
(termsAndConditions) => termsAndConditions.isChecked.value);
},
value: checkBoxState
.isChecked.value
);
}
}
********** controller **********
class CheckBoxState extends GetxController {
RxBool isChecked = false.obs;
final String title; // CheckBoxListTile 의 타이틀 제목
final String subTitle; // CheckBoxListTile 의 서브타이틀 내용
CheckBoxState({
required this.title,
required this.subTitle,
});
}
enter code here
First of all you should change this line :
termsAndConditions.map(buildCheckbox).toList()
to this :
termsAndConditions.asMap().forEach((key, value) {
buildCheckbox(value,key);
});
//or if this does work you add .toList()
termsAndConditions.asMap().forEach((key, value) {
buildCheckbox(value,key);
}).toList();
then you change the buildCheckbox function to include the key/index of the list you are passing
Widget buildGroupCheckbox(CheckBoxState checkBoxState,int index) {
...
}
then you need to add an array of booleans to keep track of the checkboxes
final checkBoxState= [false,false,false,false,false,false];
now you are implementing something like this :
onChanged: (value) {
//new code
if (value){checkBoxState[index]=true;}
else {checkBoxState[index]=false;}
checkBoxState.isChecked.value = value!;
alltermsAndConditions.isChecked.value = termsAndConditions.every(
(termsAndConditions) => termsAndConditions.isChecked.value);
},
Finally you can each individual checkbox statue and can do anything you want :
onPressed: (!sumof5first())?null:() {
//does something
},
}
bool sumof5first(){
int sum = 0;
for (int i=0;i<5;i++){
if (checkBoxState[i]==true){sum++}
}
if (sum>4){return true}
else {return false}
}

Expanded tile with modal

i'm trying to make custom expanded tile that show and image as a modal if a child is clicked.
For example, if i click Hat no 1 i want that appear an image, if i click Hat no 2 i want that appear a different image, etc
How can i do it?
This is my list
_buildExpandableContent(Clothes clothes) {
List<Widget> columnContent = [];
for (String content in clothes.contents)
columnContent.add(
new ListTile(
title: new Text(content, style: new TextStyle(fontSize: 20.0),),
trailing: new Icon (Icons.keyboard_arrow_right, color: Colors.black, size: 30),
),
);
return columnContent;
}
}
class Clothes {
final String title;
List<String> contents = [];
Clothes(this.title, this.contents);
}
List<Clothes> clothes = <Clothes>[
new Clothes(
'Hats',
['Hat no. 1', 'Hat no. 2', 'Hat no. 3', 'Hat no. 4'],
),
];
This is body
body: ListView.builder(
itemCount: clothes.length,
itemBuilder: (context, i) {
return Container(
margin: EdgeInsets.only(top: 15),
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(17.0),
child: Column(
children: <Widget>[
Container(
color: Colors.blueAccent,
child: new ExpansionTile(
trailing: Icon (Icons.keyboard_arrow_down_rounded, color: Colors.white, size: 30),
title: new Text(clothes[i].title, style: new TextStyle(color: Colors.white, fontSize: 20,),),
children: <Widget>[
Container(
color:white,
child: new Column(
children: _buildExpandableContent(clothes[i]),
),
),
],
),
)
]
),
),
),
);
},
),
You can create a new class for the clothes items which has a content and an image source:
class Clothes {
final String title;
List<ClothesItem> items = [];
Clothes(this.title, this.items);
}
class ClothesItem {
String content;
String imgSrc;
ClothesItem(this.content, this.imgSrc);
}
List<Clothes> clothes = <Clothes>[
new Clothes(
'Hats',
[
ClothesItem('Hat no. 1', 'https://picsum.photos/200'),
ClothesItem('Hat no. 2', 'https://picsum.photos/200'),
ClothesItem('Hat no. 3', 'https://picsum.photos/200'),
ClothesItem('Hat no. 4', 'https://picsum.photos/200')
],
),
];
Then you need to modify the ListTiles and set showDialog as its onTap method, this dialog shows the corresponding image:
_buildExpandableContent(Clothes clothes) {
List<Widget> columnContent = [];
for (ClothesItem item in clothes.items)
columnContent.add(
new ListTile(
title: new Text(
item.content,
style: new TextStyle(fontSize: 20.0),
),
trailing: new Icon(Icons.keyboard_arrow_right,
color: Colors.black, size: 30),
onTap: () => showDialog(
context: context,
builder: (context) {
return AlertDialog(content: Image.network(item.imgSrc));
}),
),
);
return columnContent;
}