Flutter - add together the number of SwitchListTile = true - flutter

I have an app I'm working on. It sounds simple in theory of what I want to do, but just cannot make it work.
I want to output Text of how many of the SwitchLiStTile's are true. There are 8 SwitchListTiles, if someone clicks the 3rd and 5th ones, I want the output to be 2. I cannot grasp how I would accomplish this. Everything I have tried has failed. If I could just make the value of the Switch an integer, this would be simple.
removed 1st example code
Granted, if there was truly on 2 switches, this would be way easier. There are 8 (4 in this example) and will be more. This is just shorthand code because I felt I needed to put something. How would I go about getting this solved? I have tried converting the Bools to integers and that just adds more problems. I Can't just use a Dart Operator to add them together when they are not integers anyways. Nothing seems to work without writing line after line, within a HUGE if statement. I'm working with 8 switches which give a huge number of possibilities.
Any help would be awesome.
*** OK so I am going to add some simple code and try and explain what I am doing and what I want.
I'll go ahead and add the 3 files I'm using.
main.dart
import 'package:flutter/material.dart';
import 'result.dart';
import 'data.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'TestApp',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int cluecounter = 0;
final wat = [
false,
false,
false,
false,
];
final _formKey = GlobalKey<FormState>();
final _user = User();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('TestApp'),
),
body: SingleChildScrollView(
padding: EdgeInsets.all(16.0),
child: Builder(
builder: (context) => Form(
key: _formKey,
child: Container(
child: Column(
children: [
Text('Just A Test'),
SwitchListTile(
title: const Text('SwitchListTile 1'),
value: _user.wat1,
onChanged: (bool val) {
return setState(() {
if (_user.wat1 == true) {
cluecounter--;
} else {
cluecounter++;
}
_user.wat1 = val;
});
}),
SwitchListTile(
title: const Text('SwitchListTile 2'),
value: _user.wat2,
onChanged: (bool val) {
return setState(() {
if (_user.wat2 == true) {
cluecounter--;
} else {
cluecounter++;
}
_user.wat2 = val;
});
}),
SwitchListTile(
title: const Text('SwitchListTile 3'),
value: _user.wat3,
onChanged: (bool val) {
return setState(() {
if (_user.wat3 == true) {
cluecounter--;
} else {
cluecounter++;
}
_user.wat3 = val;
});
}),
SwitchListTile(
title: const Text('SwitchListTile 4'),
value: _user.wat4,
onChanged: (bool val) {
return setState(() {
if (_user.wat4 == true) {
cluecounter--;
} else {
cluecounter++;
}
_user.wat4 = val;
});
}),
Text(
'counter value: $cluecounter\n',
textAlign: TextAlign.center,
),
FloatingActionButton.extended(
backgroundColor: const Color(0xff364976),
foregroundColor: const Color(0xffffffff),
onPressed: () {
final form = _formKey.currentState;
form?.save();
_user.save();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
Result(user:this._user),
),
);
},
icon: Icon(Icons.arrow_forward),
label: Text(' Save'),
),
],
),
),
),
),
),
);
}
}
result.dart
import 'package:flutter/material.dart';
import 'data.dart';
class Result extends StatelessWidget {
User user;
Result({Key? key, required this.user}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Results'),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('THE RESULTS'),
if (user.wat1 == true)
const Text(
'Switch 1 is True',
style: TextStyle(fontSize: 16),
),
if (user.wat2 == true)
const Text(
'Switch 2 is True',
style: TextStyle(fontSize: 16),
),
if (user.wat3 == true)
const Text(
'Switch 3 is True',
style: TextStyle(fontSize: 16),
),
if (user.wat4 == true)
const Text(
'Switch 4 is True',
style: TextStyle(fontSize: 16),
),
Text('\n\nSwitch Count >> I WANT THE COUNT HERE <<'
),
],
),
),
);
}
}
and the models
data.dart
class User {
bool wat1 = false;
bool wat2 = false;
bool wat3 = false;
bool wat4 = false;
save() {}
}
On the results, I want to see a count of how many switches are true.

use a List:
final listCng = [true/false] // 8 value default;
Code:
final listCng = [false, false, false, false, false, false, false, false];
return ListView.builder(
itemBuilder: (context, index) => SwitchListTile(
title: const Text('Switch One'),
value: _user.wat1,
onChanged: (bool val) => setState(() {
// option value 3 || value 5
if (index == 2 || index == 4) {
listCng[index] = val;
return;
}
// option other
listCng[index] = val;
})));

You can wrap the data in GestureDetector and have a count variable which is updated onTap.
bool cng1 = false;
bool cng2 = false;
int count = 0;
GestureDetector(
onTap: () => count++,
SwitchListTile(
title: const Text('Switch One'),
value: _user.wat1,
onChanged: (bool val) =>
setState(() => cng1 = val)),
)
If the number is supposed to be displayed in the UI in reactive manner, you can wrap the count++ in a setState.

Related

The argument type 'List<Contact>?' can't be assigned to the parameter type 'List<MultiSelectItem<dynamic>>'

I am trying to test flutter_contacts library.
I am willing to build a view where the user can select several contacts and display each of them in a chip when selected. My problem is that I have List Contact. But for the multiselect, I must provide a List<MultiSelectItem>.
Please, do you know if I can convert my List Contact into List<MultiSelectItem>.
Many thanks.
import 'package:flutter/material.dart';
import 'package:flutter_contacts/flutter_contacts.dart';
import 'package:multi_select_flutter/bottom_sheet/multi_select_bottom_sheet_field.dart';
import 'package:multi_select_flutter/chip_display/multi_select_chip_display.dart';
void TEST() => runApp(const FlutterContactsExample());
final _multiSelectKeyContext = GlobalKey<FormFieldState>();
class FlutterContactsExample extends StatefulWidget {
const FlutterContactsExample({Key? key}) : super(key: key);
#override
State<FlutterContactsExample> createState() => _FlutterContactsExampleState();
}
class _FlutterContactsExampleState extends State<FlutterContactsExample> {
List<Contact>? _contacts;
bool _permissionDenied = false;
late List<bool> isChecked;
#override
void initState() {
super.initState();
_fetchContacts();
}
Future _fetchContacts() async {
if (!await FlutterContacts.requestPermission(readonly: true)) {
setState(() => _permissionDenied = true);
} else {
final contacts = await FlutterContacts.getContacts();
setState(() => _contacts = contacts);
}
}
#override
Widget build(BuildContext context) => MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('flutter_contacts_example')),
body: Column(
children: [
_body(),
///MultiSelect for Context
MultiSelectBottomSheetField(
key: _multiSelectKeyContext,
initialChildSize: 0.7,
maxChildSize: 0.95,
title: const Text("Context", style: TextStyle(fontSize: 19),),
buttonText: const Text(
"Context", style: TextStyle(fontSize: 19),),
searchTextStyle: const TextStyle(fontSize: 19),
searchHintStyle: const TextStyle(fontSize: 19),
itemsTextStyle: const TextStyle(fontSize: 19),
items: _contacts,
searchable: true,
onConfirm: (valueContext) {
setState(() {
_contextSelected = valueContext;
print('mon test ligne 152');
print(_contextSelected);
});
_multiSelectKeyContext.currentState!.validate();
},
chipDisplay: MultiSelectChipDisplay(
textStyle: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold,fontSize: 19),
onTap: (dynamic item) {
setState(() {
});
_multiSelectKeyContext.currentState!.validate();
},
),
),
const SizedBox(height: 40),
],
)));
Widget _body() {
if (_permissionDenied) return const Center(child: Text('Permission denied'));
if (_contacts == null) return const Center(child: CircularProgressIndicator());
return ListView.builder(
itemCount: _contacts!.length,
itemBuilder: (context, i) => ListTile(
title: (Text(_contacts![i].displayName)),
onTap: () async {
final fullContact =
await FlutterContacts.getContact(_contacts![i].id);
await Navigator.of(context).push(
MaterialPageRoute(builder: (_) => ContactPage(fullContact!)));
}
)
);
}
}
class ContactPage extends StatelessWidget {
final Contact contact;
const ContactPage(this.contact, {Key? key}) : super(key: key);
#override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: Text(contact.displayName)),
body: Column(children: [
Text('First name: ${contact.name.first}'),
Text('Last name: ${contact.name.last}'),
Text(
'Phone number: ${contact.phones.isNotEmpty ? contact.phones.first.number : '(none)'}'),
Text(
'Email address: ${contact.emails.isNotEmpty ? contact.emails.first.address : '(none)'}'),
]));
}
In MultiSelectBottomSheetField instead of
items: _contacts,
you should write it like this:
items: _contacts.map((e) => MultiSelectItem(e, e.name)).toList(),
as shown here: link to example.
Because as the error you got and documentation both says, it needs List<MultiSelectItem<V>> here and you gave it List<Contact>?

How to put "RadioButton" or any kind of menu item list inside an "ExpansionPanelList"?

I am looking to create something like the following pic:
But it seems there is no proper example or tutorial on the internet, so I ask here.
I have the following code for simple ExpansionPanelList:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
// Remove the debug banner
debugShowCheckedModeBanner: false,
title: 'Epnasion Radio',
theme: ThemeData(
primarySwatch: Colors.indigo,
),
home: const HomePage());
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// Generating some dummy data
final List<Map<String, dynamic>> _items = List.generate(
20,
(index) => {
'id': index,
'title': 'Item $index',
'description':
'This is the description of the item $index. There is nothing important here. In fact, it is meaningless.',
'isExpanded': false
});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Expansion List'),
),
body: SingleChildScrollView(
child: ExpansionPanelList(
elevation: 3,
// Controlling the expansion behavior
expansionCallback: (index, isExpanded) {
setState(() {
_items[index]['isExpanded'] = !isExpanded;
});
},
animationDuration: const Duration(milliseconds: 600),
children: _items
.map(
(item) => ExpansionPanel(
canTapOnHeader: true,
backgroundColor:
item['isExpanded'] == true ? Colors.grey : Colors.white,
headerBuilder: (_, isExpanded) => Container(
padding: const EdgeInsets.symmetric(
vertical: 15, horizontal: 30),
child: Text(
item['title'],
style: const TextStyle(fontSize: 20),
)),
body: Container(
padding: const EdgeInsets.symmetric(
vertical: 15, horizontal: 30),
child: Text(item['description']),
),
isExpanded: item['isExpanded'],
),
)
.toList(),
),
),
);
}
}
This should work for you
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({
Key? key,
required this.title,
}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> chapterAAnswers = ["A","B"];
List<String> chapterBAnswers = ["A","B","C","D"];
late String selectedAnswerChapterA;
late String selectedAnswerChapterB;
#override
void initState() {
selectedAnswerChapterA = chapterAAnswers[0];
selectedAnswerChapterB = chapterBAnswers[0];
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
children: <Widget>[
ExpansionTile(
title: Text('Chapter A'),
children: <Widget>[
RadioListTile<String>(
title: const Text('A'),
value: chapterAAnswers[0],
groupValue: selectedAnswerChapterA,
onChanged: (String? value) {
setState(() {
selectedAnswerChapterA = value!;
});
},
),
RadioListTile<String>(
title: const Text('B'),
value: chapterAAnswers[1],
groupValue: selectedAnswerChapterA,
onChanged: (String? value) {
setState(() {
selectedAnswerChapterA = value!;
});
},
),
],
),
ExpansionTile(
title: Text('Chapter B'),
children: <Widget>[
RadioListTile<String>(
title: const Text('A'),
value: chapterBAnswers[0],
groupValue: selectedAnswerChapterB,
onChanged: (String? value) {
setState(() {
selectedAnswerChapterB = value!;
});
},
),
RadioListTile<String>(
title: const Text('B'),
value: chapterBAnswers[1],
groupValue: selectedAnswerChapterB,
onChanged: (String? value) {
setState(() {
selectedAnswerChapterB = value!;
});
},
),
RadioListTile<String>(
title: const Text('C'),
value: chapterBAnswers[2],
groupValue: selectedAnswerChapterB,
onChanged: (String? value) {
setState(() {
selectedAnswerChapterB = value!;
});
},
),
RadioListTile<String>(
title: const Text('D'),
value: chapterBAnswers[3],
groupValue: selectedAnswerChapterB,
onChanged: (String? value) {
setState(() {
selectedAnswerChapterB = value!;
});
},
),
],
),
],
),
);
}
}
you can try this, define item:
List<Map<String, dynamic>> _items = List.generate(
10,
(index) => {
'id': index,
'title': 'Item $index',
'description':
'This is the description of the item $index. Lorem Ipsum is simply dummy text of the printing and typesetting industry.',
'isExpanded': false,
'radio': {
'value': [1, 2, 3, 4, 6],
'groupValue': 1
}
});
and then :
SingleChildScrollView(
child: ExpansionPanelList(
elevation: 3,
expansionCallback: (index, isExpanded) {
setState(() {
_items[index]['isExpanded'] = !isExpanded;
});
},
animationDuration: Duration(milliseconds: 600),
children: _items
.map(
(item) => ExpansionPanel(
canTapOnHeader: true,
backgroundColor: item['isExpanded'] == true
? Colors.cyan[100]
: Colors.white,
headerBuilder: (_, isExpanded) => Container(
padding: EdgeInsets.symmetric(vertical: 15, horizontal: 30),
child: Text(
item['title'],
style: TextStyle(fontSize: 20),
)),
body: Container(
padding: EdgeInsets.symmetric(vertical: 15, horizontal: 30),
child: SingleChildScrollView(
child: ListView.builder(
shrinkWrap: true,
itemCount: (item['radio']['value'] as List).length,
itemBuilder: (context, index) {
return RadioListTile(
value: index,
groupValue: item['radio']['groupValue'],
onChanged: (value) {
setState(() {
item['radio']['groupValue'] = value;
});
});
}),
),
),
isExpanded: item['isExpanded'],
),
)
.toList(),
),
)

Open / close filter menu

I have a code that is responsible for building a menu filter. It allows you to filter data by category and then by subcategory.
Initially, subcategories are in a closed state, but when you click on the arrow, they can be opened. Take a look
But my problem is that if I click on the arrow for any category (Country in my case), then all subcategories open at once. Take a look
It's my code
class _FilterDialogUserState extends State<FilterDialogUser> {
Map<String, List<String>?> filters = {};
bool needRefresh = false;
bool isClickedCountry = false;
#override
void initState() {
super.initState();
filters = widget.initialState;
}
List<FilterItem> children = [
FilterItem('Georgia', subitems: [
FilterItem('Tbilisi'),
FilterItem('Batumi'),
]),
FilterItem('Poland', subitems: [
FilterItem('Warsaw'),
FilterItem('Krakow'),
FilterItem('Wroclaw'),
]),
FilterItem('Armenia', subitems: [
FilterItem('Erevan'),
FilterItem('Gyumri'),
]),
];
// Building a dialog box with filters.
#override
Widget build(BuildContext context) {
return SimpleDialog(
title: const Text('Filters',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25,
fontFamily: 'SuisseIntl',
)),
contentPadding: const EdgeInsets.all(16),
// Defining parameters for filtering.
children: [
Column(
children: children.map(
(e) {
return Column(
children: [
InkWell(
onTap: () async {
setState(() {
isClickedCountry = !isClickedCountry;
});
},
child: Row(
children: [
Checkbox(
value: e.selected,
onChanged: (value) => setState(() {
e.subitems.forEach((element) =>
element.selected = value as bool);
e.selected = value as bool;
}),
),
Text(e.text),
const Spacer(),
isClickedCountry
? const Icon(Icons.arrow_circle_up)
: const Icon(Icons.arrow_circle_down)
],
),
),
if (e.subitems.isNotEmpty)
!isClickedCountry
? Container()
: Padding(
padding: const EdgeInsets.fromLTRB(30, 0, 0, 0),
child: Column(
children: e.subitems.map((e) {
return Row(children: [
Checkbox(
value: e.selected,
onChanged: (value) => setState(() {
e.selected = value as bool;
}),
),
Text(e.text),
]);
}).toList(),
),
)
],
);
},
).toList(),
),
]);
}
}
class FilterItem {
final String text;
bool selected;
List<FilterItem> subitems;
FilterItem(
this.text, {
this.selected = false,
this.subitems = const [],
});
}
Tell me, is it possible to change my code so that not all subcategories are opened, but only the one that the user clicks on?
The each main filter item must be controlled one by one.
Define List isClickedCountry variable
Save and load state from List isClickedCountry variable
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: _buildBody(),
floatingActionButton: FloatingActionButton(
onPressed: () {},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
Widget _buildBody() {
return FilterDialogUser();
}
}
class FilterDialogUser extends StatefulWidget {
FilterDialogUser({Key key}) : super(key: key);
#override
State<FilterDialogUser> createState() => _FilterDialogUserState();
}
class _FilterDialogUserState extends State<FilterDialogUser> {
Map<String, List<String>> filters = {};
bool needRefresh = false;
List<bool> isClickedCountry = List.filled(3, false);
#override
void initState() {
super.initState();
// filters = widget.initialState;
}
List<FilterItem> children = [
FilterItem('Georgia', subitems: [
FilterItem('Tbilisi'),
FilterItem('Batumi'),
]),
FilterItem('Poland', subitems: [
FilterItem('Warsaw'),
FilterItem('Krakow'),
FilterItem('Wroclaw'),
]),
FilterItem('Armenia', subitems: [
FilterItem('Erevan'),
FilterItem('Gyumri'),
]),
];
// Building a dialog box with filters.
#override
Widget build(BuildContext context) {
return SimpleDialog(
title: const Text('Filters',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25,
fontFamily: 'SuisseIntl',
)),
contentPadding: const EdgeInsets.all(16),
// Defining parameters for filtering.
children: [
Column(
children: children.map(
(e) {
final int index = children.indexOf(e);
return Column(
children: [
InkWell(
onTap: () async {
setState(() {
isClickedCountry[index] = !isClickedCountry[index];
});
},
child: Row(
children: [
Checkbox(
value: e.selected,
onChanged: (value) => setState(() {
e.subitems.forEach((element) =>
element.selected = value as bool);
e.selected = value as bool;
}),
),
Text(e.text),
const Spacer(),
isClickedCountry[index]
? const Icon(Icons.arrow_circle_up)
: const Icon(Icons.arrow_circle_down)
],
),
),
if (e.subitems.isNotEmpty)
!isClickedCountry[index]
? Container()
: Padding(
padding: const EdgeInsets.fromLTRB(30, 0, 0, 0),
child: Column(
children: e.subitems.map((e) {
return Row(children: [
Checkbox(
value: e.selected,
onChanged: (value) => setState(() {
e.selected = value as bool;
}),
),
Text(e.text),
]);
}).toList(),
),
)
],
);
},
).toList(),
),
]);
}
}
class FilterItem {
final String text;
bool selected;
List<FilterItem> subitems;
FilterItem(
this.text, {
this.selected = false,
this.subitems = const [],
});
}

Flutter setState not updating variable inside ExpansionPanelList

I'm trying to create a filter that update the selected option like the image using a ExpansionPanelList, something like this...
Goal
In my code I'm trying to update a subtitle Text from a property returned from the body of the same ListTile Widget which contain the RadioListTile Widget inside of ExpansionPanel Widget inside of ExpansionPanelList Widget.
The value I want is from another StatefulWidget class where the RadioListTile works perfectly, and the value is returned by a Callback to the class I need to use this variable named _orderByOptionSelected, but the variable I'm using is not updated even inside of the setState method.
Here is the class that contains the RadioListTile selection:
class ElementFilterOrderBy extends StatefulWidget {
const ElementFilterOrderBy({Key? key, required this.onChanged})
: super(key: key);
static const String best = 'best';
static const String reviews = 'reviews';
static const String price = 'price';
static const String location = 'location';
final Function(String) onChanged;
#override
State<ElementFilterOrderBy> createState() => _ElementFilterOrderByState();
}
class _ElementFilterOrderByState extends State<ElementFilterOrderBy> {
String _orderBySelection = ElementFilterOrderBy.best;
#override
Widget build(BuildContext context) {
return Column(
children: [
RadioListTile<String>(
title: const Text(ElementFilterOrderBy.best),
value: ElementFilterOrderBy.best,
groupValue: _orderBySelection,
onChanged: (value) {
setState(() {
_orderBySelection = value!;
widget.onChanged(_orderBySelection);
});
},
activeColor: kPrimaryColor,
),
RadioListTile<String>(
title: const Text(ElementFilterOrderBy.reviews),
value: ElementFilterOrderBy.reviews,
groupValue: _orderBySelection,
onChanged: (value) {
setState(() {
_orderBySelection = value!;
widget.onChanged(_orderBySelection);
});
},
activeColor: kPrimaryColor,
),
RadioListTile<String>(
title: const Text(ElementFilterOrderBy.price),
value: ElementFilterOrderBy.price,
groupValue: _orderBySelection,
onChanged: (value) {
setState(() {
_orderBySelection = value!;
widget.onChanged(_orderBySelection);
});
},
activeColor: kPrimaryColor,
),
RadioListTile<String>(
title: const Text(ElementFilterOrderBy.location),
value: ElementFilterOrderBy.location,
groupValue: _orderBySelection,
onChanged: (value) {
setState(() {
_orderBySelection = value!;
widget.onChanged(_orderBySelection);
});
},
activeColor: kPrimaryColor,
),
],
);
}
}
And this is my class where I'm trying to update the value returned:
class CustomBottomSheet extends StatefulWidget {
const CustomBottomSheet({Key? key}) : super(key: key);
#override
State<CustomBottomSheet> createState() => _CustomBottomSheetState();
}
class _CustomBottomSheetState extends State<CustomBottomSheet> {
late String _orderByOptionSelected;
late String _searchLocation;
late List<ItemExpansionPanel> _optionsFilter;
#override
void initState() {
super.initState();
_orderByOptionSelected = 'best';
_searchLocation = 'Actual Location';
_optionsFilter = [
ItemExpansionPanel(
headerValue: kFilterOptionOrderBy,
widgetBody: ElementFilterOrderBy(
onChanged: (selectedOption) {
setState(() {
_orderByOptionSelected = selectedOption;
});
},
),
optionSelected: _orderByOptionSelected,
),
ItemExpansionPanel(
headerValue: kFilterOptionLocation,
widgetBody: Container(),
optionSelected: _searchLocation,
),
];
}
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(kPaddingApp),
child: Column(
children: [
const Text(
kFilterTitle,
style: kTextStyleBoldBlackBig,
),
const SizedBox(
height: kMarginApp,
),
Expanded(
child: SingleChildScrollView(
child: _buildPanel(),
),
),
],
),
);
}
Widget _buildPanel() {
return ExpansionPanelList(
expansionCallback: (int index, bool isExpanded) {
setState(() {
_optionsFilter[index].isExpanded = !isExpanded;
});
},
children: _optionsFilter.map<ExpansionPanel>((ItemExpansionPanel item) {
return ExpansionPanel(
canTapOnHeader: true,
headerBuilder: (BuildContext context, bool isExpanded) {
return ListTile(
title: Text(item.headerValue),
subtitle: Text(
item.optionSelected,
style: const TextStyle(
color: kAccentColor,
),
),
);
},
body: item.widgetBody,
isExpanded: item.isExpanded,
);
}).toList(),
);
}
}
// stores ExpansionPanel state information
class ItemExpansionPanel {
ItemExpansionPanel({
required this.headerValue,
required this.widgetBody,
required this.optionSelected,
this.isExpanded = false,
});
final Widget widgetBody;
final String headerValue;
bool isExpanded;
String optionSelected;
}
Edit 1: Added more elements on the list to only change the ItemExpansionPanel selected
You should use _orderByOptionSelected as text value not item.optionSelected.
go to CustomBottomSheet then _buildPanel() widget then change it to this.
Widget _buildPanel() {
return ExpansionPanelList(
expansionCallback: (int index, bool isExpanded) {
setState(() {
_optionsFilter[index].isExpanded = !isExpanded;
});
},
children: _optionsFilter.map<ExpansionPanel>((ItemExpansionPanel item) {
return ExpansionPanel(
canTapOnHeader: true,
headerBuilder: (BuildContext context, bool isExpanded) {
return ListTile(
title: Text(item.headerValue),
subtitle: Text(
_orderByOptionSelected,
// item.optionSelected, <== DELETE This
style: const TextStyle(
color: Colors.purple,
),
),
);
},
body: item.widgetBody,
isExpanded: item.isExpanded,
);
}).toList(),
);
}
}

How to add icons to the List View Builder list in flutter?

This is a very basic app which highlights the selected colour on tap.But i want leading icons to the list view. How can i achieve this? If i am adding an icon inside a widget, the same icon is being rendered everywhere. I want unique icons for each list. Please help. Here's my code:
I want to render the icons for each list.
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> texts = ['ME', 'MYSELF', 'I'];
List<bool> isHighlighted = [true, false, false];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Demo App'),
),
drawer: Drawer(
child: Center(
child: Column(children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: texts.length,
itemBuilder: (_, index) {
return GestureDetector(
onTap: () {
for (int i = 0; i < isHighlighted.length; i++) {
setState(() {
if (index == i) {
isHighlighted[index] = true;
} else {
//the condition to change the highlighted item
isHighlighted[i] = false;
}
});
}
},
child: Container(
color: isHighlighted[index]
? Colors.blueAccent
: Colors.white,
child: ListTile(
//i want to display different items for each list in the leading property.
title: Text(texts[index]),
),
),
);
}),
),
Container(
child: Text(
'this is footer',
style: TextStyle(fontSize: 20),
),
)
]),
),
),
);
}
}
Since you have a list with only 3 items in them as defined by your 2 lists above, if you want an icon that goes for each of the entries in those lists then you should define another list that have those icons and render them depending on the index you are on.
// (..)
List<String> texts = ['ME', 'MYSELF', 'I'];
List<bool> isHighlighted = [true, false, false];
// add the icons you want to render for each entry here
List<IconData> icons = [Icons.person, Icons.home, Icons.notifications];
// add screens here or use the approach marked as answer above
List<Widget> screens = [PageOne(), PageTwo(), PageThree()];
Then in your list tile you can then take the icon based on the index
// (...)
child: Container(
color: isHighlighted[index]
? Colors.blueAccent
: Colors.white,
child: ListTile(
//i want to display different items for each list in the leading property.
leading: Icon(icons[index]),
title: Text(texts[index]),
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => screens[index]),
)
),
),
try this
class _MyHomePageState extends State<MyHomePage> {
List<ListItem> _items = [
ListItem(title: 'Me', isSelected: true, icon: Icons.home),
ListItem(title: 'MYSELF', isSelected: false, icon: Icons.cake),
ListItem(title: 'I', isSelected: false, icon: Icons.camera),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Demo App'),
),
drawer: Drawer(
child: Center(
child: Column(children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: _items.length,
itemBuilder: (_, index) {
return GestureDetector(
onTap: () {
for (int i = 0; i < _items.length; i++) {
setState(() {
if (index == i) {
_items[index].isSelected = true;
} else {
//the condition to change the highlighted item
_items[i].isSelected = false;
}
});
}
},
child: Container(
color: _items[index].isSelected
? Colors.blueAccent
: Colors.white,
child: ListTile(
//i want to display different items for each list in the leading property.
leading: Icon(_items[index].icon),
title: Text(_items[index].title),
),
),
);
}),
),
Container(
child: Text(
'this is footer',
style: TextStyle(fontSize: 20),
),
)
]),
),
),
);
}
}
class ListItem {
String title;
bool isSelected;
IconData icon;
ListItem({
this.title,
this.isSelected,
this.icon,
});
}
I made a separate class for each item instead of or having multiple lists.