Open / close filter menu - flutter

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 [],
});
}

Related

controlling a dropdown value in flutter

I'm creating a dynamic form in which the user adds more group fields. so initially there is no form but an add button. with this button the user can add as many form fields as they need. this from is a group form consisting two TextFormField and one DropdownButton.
so lets say the user added 4 group forms and filled each form. but then they changed their minds and wanted to remove the second form. when they do that it removes the last index of the listview, but the value is removed correctly at the selected index. for the textfields i can create a list of controllers and dispose them. but how can i do it for the dropdown?
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 Purchased(),
);
}
}
class Purchased extends StatefulWidget {
const Purchased({Key? key}) : super(key: key);
#override
State<Purchased> createState() => _PurchasedState();
}
class _PurchasedState extends State<Purchased> {
List<UserInfo> list = [];
List<TextEditingController> textControllerList = [];
List<TextEditingController> textControllerList1 = [];
#override
void dispose() {
textControllerList.forEach((element) {
element.dispose();
});
textControllerList1.forEach((element) {
element.dispose();
});
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
/// every time you add new Userinfo, it will generate new FORM in the UI
list.add(UserInfo());
setState(() {}); // dont forget to call setState to update UI
},
child: const Icon(Icons.add),
),
body: Column(
children: [
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: list.length,
itemBuilder: ((context, index) {
return Column(
children: [
const Text('phone'),
Text(list[index].phone),
const Text('email'),
Text(list[index].email),
Text('category'),
Text(list[index].category)
],
);
})),
),
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: list.length,
itemBuilder: ((context, index) {
TextEditingController controller = TextEditingController();
TextEditingController controller1 = TextEditingController();
textControllerList.add(controller);
textControllerList1.add(controller1);
return MyForm(
// dont forget use the key, to make sure every MyForm is has identity. to avoid missed build
textEditingController: textControllerList[index],
textEditingController1: textControllerList1[index],
key: ValueKey(index),
//pass init value so the widget always update with current value
initInfo: list[index],
// every changes here will update your current list value
onChangePhone: (phoneVal) {
if (phoneVal != null) {
setState(() {
list[index].setPhone(phoneVal);
});
}
},
onChangeEmail: (emailVal) {
if (emailVal != null) {
list[index].setEmail(emailVal);
setState(() {});
}
},
onChangeCategory: (categoryVal) {
if (categoryVal != null) {
list[index].setCategory(categoryVal);
setState(() {});
}
},
// every changes here will update your current list value
onremove: () {
list.removeAt(index);
textControllerList.removeAt(index);
textControllerList1.removeAt(index);
setState(() {});
});
})),
)
],
),
);
}
}
class MyForm extends StatefulWidget {
final UserInfo initInfo;
final Function(String?) onChangePhone;
final Function(String?) onChangeEmail;
final Function(String?) onChangeCategory;
final TextEditingController textEditingController;
final TextEditingController textEditingController1;
final VoidCallback? onremove;
const MyForm({
super.key,
required this.initInfo,
required this.onChangePhone,
required this.onChangeEmail,
required this.onChangeCategory,
required this.onremove,
required this.textEditingController,
required this.textEditingController1,
});
#override
State<MyForm> createState() => _MyFormState();
}
class _MyFormState extends State<MyForm> {
List<UserInfo> list = <UserInfo>;
final List<String> category = [
'Manager',
'Reception',
'Sales',
'Service',
];
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(12),
child: Column(
children: [
IconButton(
onPressed: widget.onremove,
icon: const Icon(
Icons.remove,
)),
TextFormField(
controller: widget.textEditingController,
onChanged: widget.onChangePhone,
),
TextFormField(
controller: widget.textEditingController1,
onChanged: widget.onChangeEmail,
),
DropdownButton(
isExpanded: true,
hint: const Text(
'Select Category',
style: TextStyle(fontSize: 14),
),
icon: const Icon(
Icons.arrow_drop_down,
color: Colors.black45,
),
iconSize: 30,
items: category
.map((item) => DropdownMenuItem<String>(
value: item,
child: Text(
item,
style: const TextStyle(
fontSize: 14,
),
),
))
.toList(),
onChanged: widget.onChangeCategory)
],
),
);
}
}
class UserInfo {
///define
String _phone = '';
String _email = '';
String _category = '';
/// getter
String get phone => _phone;
String get email => _email;
String get category => _category;
///setter
void setPhone(String phone) {
_phone = phone;
}
void setEmail(String email) {
_email = email;
}
void setCategory(String category) {
_category = category;
}
}
PLEASE any help is appreciated.
In order this code to work you are going to need to install the Provider Package.
With this solution using provider, you dont need to worry about the controllers.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => FormsProvider(),
),
],
child: const Purchased(),
),
);
}
}
class Purchased extends StatefulWidget {
const Purchased({Key? key}) : super(key: key);
#override
State<Purchased> createState() => _PurchasedState();
}
class _PurchasedState extends State<Purchased> {
final List<String> category = [
'Manager',
'Reception',
'Sales',
'Service',
];
#override
Widget build(BuildContext context) {
return Consumer<FormsProvider>(
builder: (context, formsProvider, child) {
List<Form> formsList = formsProvider.listOfForms;
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
formsProvider
.addFormToList(DateTime.now().millisecondsSinceEpoch);
},
child: const Icon(Icons.add),
),
body: Column(
children: [
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: formsList.length,
itemBuilder: ((context, index) {
UserInfo formItemInfo = formsList[index].userInfo;
return Column(
children: [
const Text('phone'),
Text(formItemInfo.phone),
const Text('email'),
Text(formItemInfo.email),
const Text('category'),
Text(formItemInfo.category)
],
);
})),
),
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: formsList.length,
itemBuilder: ((context, index) {
Form form = formsList[index];
return Container(
padding: const EdgeInsets.all(12),
child: Column(
children: [
IconButton(
onPressed: () {
formsProvider.removeFormFromList(form);
},
icon: const Icon(
Icons.remove,
),
),
TextFormField(
onChanged: (phoneVal) {
formsProvider.setPhone(form.id, phoneVal);
},
),
TextFormField(
onChanged: (emailVal) {
formsProvider.setEmail(form.id, emailVal);
},
),
DropdownButton(
isExpanded: true,
hint: const Text(
'Select Category',
style: TextStyle(fontSize: 14),
),
icon: const Icon(
Icons.arrow_drop_down,
color: Colors.black45,
),
iconSize: 30,
items: category
.map((item) => DropdownMenuItem<String>(
value: item,
child: Text(
item,
style: const TextStyle(
fontSize: 14,
),
),
))
.toList(),
onChanged: (categoryVal) {
if (categoryVal != null) {
formsProvider.setCategory(
form.id, categoryVal);
}
},
)
],
),
);
})),
)
],
),
);
},
);
}
}
class FormsProvider extends ChangeNotifier {
List<Form> _listOfForms = [];
List<Form> get listOfForms => _listOfForms;
void addFormToList(int id) {
_listOfForms.add(
Form(id: id, userInfo: UserInfo(category: '', email: '', phone: '')));
notifyListeners();
}
void removeFormFromList(Form form) {
_listOfForms.remove(form);
notifyListeners();
}
void setEmail(int idForm, String newEmail) {
_listOfForms.firstWhere((element) => element.id == idForm).userInfo.email =
newEmail;
notifyListeners();
}
void setPhone(int idForm, String newPhone) {
_listOfForms.firstWhere((element) => element.id == idForm).userInfo.phone =
newPhone;
notifyListeners();
}
void setCategory(int idForm, String newCategory) {
_listOfForms
.firstWhere((element) => element.id == idForm)
.userInfo
.category = newCategory;
notifyListeners();
}
}
class Form {
int id;
UserInfo userInfo;
Form({
required this.id,
required this.userInfo,
});
}
class UserInfo {
String phone;
String email;
String category;
UserInfo({
this.email = '',
this.phone = '',
this.category = '',
});
}
you could create a class
class GroupForm extends StatefulWidget{
TextEditingController controller = TextEditingController();
TextEditingController controller1 = TextEditingController();
List category = [];
GroupForm(this.controller ,this.controller1,this.category)
Widget build(){
return Container(
padding: const EdgeInsets.all(12),
child: Column(
children: [
IconButton(
onPressed: widget.onremove,
icon: const Icon(
Icons.remove,
)),
TextFormField(
controller: widget.textEditingController,
onChanged: widget.onChangePhone,
),
TextFormField(
controller: widget.textEditingController1,
onChanged: widget.onChangeEmail,
),
DropdownButton(
isExpanded: true,
hint: const Text(
'Select Category',
style: TextStyle(fontSize: 14),
),
icon: const Icon(
Icons.arrow_drop_down,
color: Colors.black45,
),
iconSize: 30,
items: category
.map((item) => DropdownMenuItem<String>(
value: item,
child: Text(
item,
style: const TextStyle(
fontSize: 14,
),
),
))
.toList(),
onChanged: widget.onChangeCategory)
],
),
);
}
}
and then you could create a List<GroupForm> to add and remove any object.
List<GroupForm> items = []
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return items[index]
},
)

How to show selected checkbox on prev screen?

I need to display checkboxes selected by the user on the previous page using pop()
I have a function that displays the user's message on the previous page and I need to pass the selected checkboxes in the same way. How to pass them as arguments to pop()?
Screen with checkboxes:
const TextScreen({Key? key}) : super(key: key);
#override
State<TextScreen> createState() => _TextScreenState();
}
class _TextScreenState extends State<TextScreen> {
// initial values for checkboxes
bool _privacy = false;
bool _termsOfUse = false;
// text controller for message input
TextEditingController textController = TextEditingController();
#override
void dispose() {
textController.dispose();
super.dispose();
}
// go to result screen
void getResult(BuildContext context) {
String valueResult = textController.text;
Navigator.pop(context, valueResult);
}
#override
Widget build(BuildContext context) {
//change state for privacy checkbox
_onPrivacyChange(value) {
setState(() {
_privacy = value!;
});
}
//change state for terms of use checkbox
_onTermsOfUSeChange(value) {
setState(() {
_termsOfUse = value!;
});
}
return Scaffold(
appBar: AppBar(
title: const Text('Enter data'),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: textController,
decoration: const InputDecoration(labelText: 'Message')),
const SizedBox(height: 20),
CheckboxListTile(
title: const Text('Privacy'),
controlAffinity: ListTileControlAffinity.leading,
value: _privacy,
onChanged: _onPrivacyChange,
contentPadding: EdgeInsets.zero,
),
CheckboxListTile(
title: const Text('Terms of use'),
controlAffinity: ListTileControlAffinity.leading,
value: _termsOfUse,
onChanged: _onTermsOfUSeChange,
contentPadding: EdgeInsets.zero,
),
ElevatedButton(
onPressed: () {
getResult(context);
},
child: const Text('Display result'))
],
)),
);
}
}
Screen with results display:
class ResultScreen extends StatefulWidget {
const ResultScreen({Key? key}) : super(key: key);
#override
State<ResultScreen> createState() => _ResultScreenState();
}
class _ResultScreenState extends State<ResultScreen> {
String? _valueText = '';
#override
Widget build(BuildContext context) {
// navigation to next screen
void _navToNextScreen(BuildContext context) async {
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => const TextScreen()),
);
// update widget after result comes back
setState(() {
_valueText = result;
});
}
return Scaffold(
appBar: AppBar(
title: const Text('Results'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
_navToNextScreen(context);
},
child: const Text('Enter data'),
),
const SizedBox(height: 50),
Text('Message: $_valueText'),
const SizedBox(height: 20),
Text('Checkboxes: '),
],
)),
);
}
}
I think this should be the job of a simple state management strategy; for communication between separate widgets (in this case, two page widgets), that's the cleanest approach. You should create a common service to which both page widgets are subscribed: one to trigger the changes, the other to capture them and display them, using a ChangeNotifier service along with Consumer widgets, as shown below:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => SelectedData(),
child: MyApp()
)
);
}
class SelectedData extends ChangeNotifier {
bool _privacy = false;
bool _termsOfUse = false;
String _valueResult = '';
bool get privacy => _privacy;
bool get termsOfUse => _termsOfUse;
String get valueResult => _valueResult;
set privacy(bool value) {
_privacy = value;
notifyListeners();
}
set termsOfUse(bool value) {
_termsOfUse = value;
notifyListeners();
}
set valueResult(String value) {
_valueResult = value;
notifyListeners();
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: ResultScreen(),
),
),
);
}
}
class TextScreen extends StatefulWidget {
const TextScreen({Key? key}) : super(key: key);
#override
State<TextScreen> createState() => _TextScreenState();
}
class _TextScreenState extends State<TextScreen> {
// text controller for message input
TextEditingController textController = TextEditingController();
#override
void initState() {
super.initState();
}
#override
void dispose() {
textController.dispose();
super.dispose();
}
// go to result screen
void getResult(BuildContext context) {
Navigator.pop(context);
}
#override
Widget build(BuildContext context) {
SelectedData data = Provider.of<SelectedData>(context, listen: false);
textController.text = data.valueResult;
//change state for privacy checkbox
_onPrivacyChange(value) {
data.privacy = value;
}
//change state for terms of use checkbox
_onTermsOfUSeChange(value) {
data.termsOfUse = value;
}
return Scaffold(
appBar: AppBar(
title: const Text('Enter data'),
),
body: Consumer<SelectedData>(
builder: (context, selectedData, child) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: textController,
onChanged: (value) {
data.valueResult = value;
},
decoration: const InputDecoration(labelText: 'Message')),
const SizedBox(height: 20),
CheckboxListTile(
title: const Text('Privacy'),
controlAffinity: ListTileControlAffinity.leading,
value: selectedData.privacy,
onChanged: _onPrivacyChange,
contentPadding: EdgeInsets.zero,
),
CheckboxListTile(
title: const Text('Terms of use'),
controlAffinity: ListTileControlAffinity.leading,
value: selectedData.termsOfUse,
onChanged: _onTermsOfUSeChange,
contentPadding: EdgeInsets.zero,
),
ElevatedButton(
onPressed: () {
getResult(context);
},
child: const Text('Display result'))
],
));
}
),
);
}
}
class ResultScreen extends StatefulWidget {
const ResultScreen({Key? key}) : super(key: key);
#override
State<ResultScreen> createState() => _ResultScreenState();
}
class _ResultScreenState extends State<ResultScreen> {
#override
Widget build(BuildContext context) {
// navigation to next screen
void _navToNextScreen(BuildContext context) async {
await Navigator.push(
context,
MaterialPageRoute(builder: (context) => const TextScreen()),
);
}
return Scaffold(
appBar: AppBar(
title: const Text('Results'),
),
body: Consumer<SelectedData>(
builder: (context, selectedData, child) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
_navToNextScreen(context);
},
child: const Text('Enter data'),
),
const SizedBox(height: 50),
Text('Message: ${selectedData.valueResult}'),
const SizedBox(height: 20),
const Text('Checkboxes: '),
Text('Privacy: ${selectedData.privacy}'),
Text('Terms of Use: ${selectedData.termsOfUse}')
],
));
}
),
);
}
}
Here's the output when you implement it this way:
So from what i see is you are only passing one value that is message and you what many values to pass at a time so here the map can be used and as pop() function takes dynamic returns you can pass any thing.
From your example i have created a sample example that will be a working proof which will demostrate the using map for passing data to previous screen.
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 const MaterialApp(
debugShowCheckedModeBanner: false,
home: ResultScreen(),
);
}
}
class TextScreen extends StatefulWidget {
const TextScreen({Key? key}) : super(key: key);
#override
_TextScreenState createState() => _TextScreenState();
}
class _TextScreenState extends State<TextScreen> {
// initial values for checkboxes
bool _privacy = false;
bool _termsOfUse = false;
// text controller for message input
TextEditingController textController = TextEditingController();
#override
void dispose() {
textController.dispose();
super.dispose();
}
// go to result screen
void getResult(BuildContext context) {
String valueResult = textController.text;
final data = {
"message":valueResult,
"privacy": _privacy,
'terms':_termsOfUse,
};
Navigator.pop(context, data);
}
#override
Widget build(BuildContext context) {
//change state for privacy checkbox
_onPrivacyChange(value) {
setState(() {
_privacy = value!;
});
}
//change state for terms of use checkbox
_onTermsOfUSeChange(value) {
setState(() {
_termsOfUse = value!;
});
}
return Scaffold(
appBar: AppBar(
title: const Text('Enter data'),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: textController,
decoration: const InputDecoration(labelText: 'Message')),
const SizedBox(height: 20),
CheckboxListTile(
title: const Text('Privacy'),
controlAffinity: ListTileControlAffinity.leading,
value: _privacy,
onChanged: _onPrivacyChange,
contentPadding: EdgeInsets.zero,
),
CheckboxListTile(
title: const Text('Terms of use'),
controlAffinity: ListTileControlAffinity.leading,
value: _termsOfUse,
onChanged: _onTermsOfUSeChange,
contentPadding: EdgeInsets.zero,
),
ElevatedButton(
onPressed: () {
getResult(context);
},
child: const Text('Display result'))
],
)),
);
}
}
class ResultScreen extends StatefulWidget {
const ResultScreen({Key? key}) : super(key: key);
#override
State<ResultScreen> createState() => _ResultScreenState();
}
class _ResultScreenState extends State<ResultScreen> {
String? _valueText = '';
bool _privacyValue =false;
bool _termsOfUse = false;
#override
Widget build(BuildContext context) {
// navigation to next screen
void _navToNextScreen(BuildContext context) async {
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => const TextScreen()),
);
if(result !=null)
{
setState(() {
if(result['message']!=null )_valueText = result['message'];
if(result['privacy']!=null) _privacyValue = result['privacy'];
if(result['terms']!=null) _termsOfUse = result['terms'];
});
}
}
return Scaffold(
appBar: AppBar(
title: const Text('Results'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
_navToNextScreen(context);
},
child: const Text('Enter data'),
),
const SizedBox(height: 50),
Text('Message: $_valueText'),
const SizedBox(height: 20),
Text('Privacy Value: $_privacyValue '),
const SizedBox(height: 20),
Text('Terms Value: $_termsOfUse '),
],
)),
);
}
}
You can make changes as per your needs, So let me know if it works.

Additional filters in Flutter

I am writing an application on Flutter. I was able to make one filter with multiple selections.
But I want to have additional filters.
I used Multi-Select, but in the case of multiple filters, I don't know how to apply it
With what help can I implement this?
import 'package:flutter/material.dart';
class MainPage extends StatelessWidget {
#override
Widget build(context) => Scaffold(
appBar: AppBar(title: Text("f"),
backgroundColor: Colors.black),
drawer: MediaQuery.of(context).size.width < 500 ? Drawer(
child: HomePage(),
) : null,
body: SafeArea(
child:Center(
child: MediaQuery.of(context).size.width < 500 ? Content() :
Row(
children: [
Container(
width: 200.0,
child: HomePage()
),
Container(
width: MediaQuery.of(context).size.width-200.0,
child: Content()
)
]
)
)
)
);
}
List devices_list = ["First device", "Second device", "Third device", "Fourth device", "Fifth device", "Sixth device", "Seventh device", "Eighth device", "Ninth device"];
class Content extends StatelessWidget{
#override
Widget build(context) =>
Scaffold(
backgroundColor: Colors.white,
body: LayoutBuilder(
builder: (context, constraints){
return AnimatedContainer(
duration: Duration(milliseconds: 500),
color: Colors.white,
child: Center(
child: Container(
constraints: BoxConstraints(
maxWidth: 800,),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(5.0),),
child: ListView.builder(
itemCount: devices_list.length,
itemBuilder: (BuildContext context, int index) {
return SizedBox (
height: 60,
key: Key(devices_list[index]),
child: Card(
shape: const RoundedRectangleBorder(
side: BorderSide(color: Colors.black,width: 3),
borderRadius: BorderRadius.all(Radius.circular(15))),
child: TextButton(
onPressed: (){},
child: ListTile(title: Text(devices_list[index]))),
)
);
}
))));
}));
}
class MultiSelect extends StatefulWidget {
final List<String> items;
const MultiSelect({Key? key, required this.items}) : super(key: key);
#override
State<StatefulWidget> createState() => _MultiSelectState();
}
class _MultiSelectState extends State<MultiSelect> {
// this variable holds the selected items
final List<String> _selectedItems = [];
// This function is triggered when a checkbox is checked or unchecked
void _itemChange(String itemValue, bool isSelected) {
setState(() {
if (isSelected) {
_selectedItems.add(itemValue);
} else {
_selectedItems.remove(itemValue);
}
});
}
// this function is called when the Cancel button is pressed
void _cancel() {
Navigator.pop(context);
}
// this function is called when the Submit button is tapped
void _submit() {
Navigator.pop(context, _selectedItems);
}
#override
Widget build(BuildContext context) {
return AlertDialog(
title: const Text('Select Manufactures'),
content: SingleChildScrollView(
child: ListBody(
children: widget.items
.map((item) => CheckboxListTile(
value: _selectedItems.contains(item),
title: Text(item),
controlAffinity: ListTileControlAffinity.leading,
onChanged: (isChecked) => _itemChange(item, isChecked!),
))
.toList(),
),
),
actions: [
TextButton(
child: const Text('Cancel'),
onPressed: _cancel,
),
ElevatedButton(
child: const Text('Submit'),
onPressed: _submit,
),
],
);
}
}
// Implement a multi select on the Home screen
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<String> _selectedItemsManufactures = [];
void _showMultiSelectManufactures() async {
// a list of selectable items
// these items can be hard-coded or dynamically fetched from a database/API
final List<String> _items = [
'Apple',
'Samsung',
'Xiaomi',
'Nokia',
'Huawei',
'Alcatel'
];
final List<String>? results = await showDialog(
context: context,
builder: (BuildContext context) {
return MultiSelect(items: _items);
},
);
// Update UI
if (results != null) {setState(() {_selectedItemsManufactures = results;});}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// use this button to open the multi-select dialog
ElevatedButton(
child: const Text('Manufactures'),
onPressed: _showMultiSelectManufactures,
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.black)
),
),
const Divider(
height: 5,
color: Colors.white,
),
// display selected items
Wrap(
children: _selectedItemsManufactures
.map((e) => Chip(
label: Text(e),
))
.toList(),
)
],
),
),
);
}
}
Addition. Addition. I would like changes in the Menu class to be reflected in the class MainPage.
class DevicesPage extends StatelessWidget {
#override
Widget build(context) => Scaffold(
appBar: AppBar(title: Text("IT"),
backgroundColor: Colors.black),
drawer: MediaQuery.of(context).size.width < 500 ? Drawer(
child: Menu(),
) : null,
body: SafeArea(
child:Center(
child: MediaQuery.of(context).size.width < 500 ? MainPage() :
Row(
children: [
Container(
width: 200.0,
child: Menu()
),
Container(
width: MediaQuery.of(context).size.width-200.0,
child: MainPage()
)
]
)
)
)
);
}
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
#override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
Map<String, List<String>?> filters = {};
List<Phone> filteredPhones = phoneList;
#override
Widget build(BuildContext context) {
return Scaffold(
body: filteredPhones.isEmpty
? const Center(child: Text('No product', style: TextStyle(fontSize: 16),))
: ListView.builder(
itemCount: filteredPhones.length,
itemBuilder: (_, index) {
final currentPhone = filteredPhones[index];
return ListTile(
title: Text(currentPhone.name),
subtitle: Text('${currentPhone.brand}-${currentPhone.color}'),
trailing: Text('${currentPhone.operation_system}'),
);
}
),
);
}
}
class Menu extends StatefulWidget {
const Menu({Key? key}) : super(key: key);
#override
State<Menu> createState() => _MenuState();
}
class _MenuState extends State<Menu> {
Map<String, List<String>?> filters = {};
List<Phone> filteredPhones = phoneList;
void _filter() {
setState(() {
filteredPhones = phoneList;
filters.forEach((key, value) {
if((value ?? []).isNotEmpty) {
filteredPhones = filteredPhones.where((phone) {
switch(key) {
case 'brand':
return value!.contains(phone.brand);
case 'color':
return value!.contains(phone.color);
case 'operation_system':
return value!.contains(phone.operation_system);
return true;
default:
return false;
}
}).toList();
}
});
filters.clear();
Navigator.of(context).pop();
});
}
void _handleCheckFilter(bool checked, String key, String value) {
final currentFilters = filters[key] ?? [];
if(checked) {
currentFilters.add(value);
} else {
currentFilters.remove(value);
}
filters[key] = currentFilters;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('phones List'),
actions: [
IconButton(
icon: const Icon(Icons.filter_alt),
onPressed: () {
showDialog<Filter>(context: context, builder: (_) {
return SimpleDialog(
title: const Text('Filters',textAlign: TextAlign.center,),
contentPadding: const EdgeInsets.all(16),
children: [
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text('Select a brand'),
...brands.map((el) =>
CustomCheckboxTile(
label: el,
onChange: (check) => _handleCheckFilter(check, 'brand', el),
),
).toList(),
const Text('Select a operation_system'),
...operation_system.map((el) =>
CustomCheckboxTile(
label: el,
onChange: (check) => _handleCheckFilter(check, 'operation_system', el),
)
).toList(),
const Text('Select a colors'),
...colors.map((el) =>
CustomCheckboxTile(
label: el,
onChange: (check) => _handleCheckFilter(check, 'color', el),
),
).toList(),
const SizedBox(height: 24,),
ElevatedButton(onPressed: _filter, child: const Text('APPLY')),
],
),
],
);
});
},
),
],
),
body: filteredPhones.isEmpty
? const Center(child: Text('No product', style: TextStyle(fontSize: 16),))
: ListView.builder(
itemCount: filteredPhones.length,
itemBuilder: (_, index) {
final currentPhone = filteredPhones[index];
return ListTile(
title: Text(currentPhone.name),
subtitle: Text('${currentPhone.brand}-${currentPhone.color}'),
trailing: Text('${currentPhone.operation_system}'),
);
}
),
);
}
}
enter image description here
Well... you have many options. One way is to store all filters selected in a Map, like this:
final allFilters = {
"filterName1": "someValue",
"filterName2": "someOtherValue",
"fitlerName3": ["value1", "value2", "value3"]
}
And create a function that can handle each key of this map:
allFilter.forEach((key, value) {
switch(key) {
case "filterName1":
// ...some code
case "fitlerName2":
// ...more code
case "filterName3":
// ...much more code
}
})
You can check this demo project that I created https://github.com/felipeemidio/ListWithMultipleFilters

How to receive data from another statefull widget on the same screen?

So i need to receive data from my RadioDialog stf widget to BoxScreen widget.
I want to show different text depends of random value from boxItems, what i do on this line _randomBoxItem == boxOptionValue ? Text('text1') : Text('text2'),
And i dont know how to get value from _boxOption to boxOptionValue like exmple.
Here is my code:
class BoxScreen extends StatefulWidget {
const BoxScreen({Key? key}) : super(key: key);
static const routeName = '/box';
#override
State<BoxScreen> createState() => _BoxScreenState();
}
class _BoxScreenState extends State<BoxScreen> {
String? boxOptionValue;
final List<String> boxItems = [
'Banana',
'Apple',
'Orange',
];
Future<void> whatIsInTheBox(BuildContext context) async {
return await showDialog(
context: context,
builder: (BuildContext context) => const RadioDialog(),
);
}
#override
Widget build(BuildContext context) {
final String _randomBoxItem = (boxItems..shuffle()).first;
return Scaffold(
appBar: AppBar( title: const Text('BOX Game'),),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(_randomBoxItem),
_randomBoxItem == boxOptionValue ? Text('text1') : Text('text2'),
ElevatedButton.icon(
icon: const Icon(Icons.play_arrow_rounded),
onPressed: () {
whatIsInTheBox(context);
},
label: const Text('What is in BOX?'),
),
],
),
);
}
}
class RadioDialog extends StatefulWidget {
const RadioDialog({Key? key}) : super(key: key);
#override
State createState() => RadioDialogState();
}
class RadioDialogState extends State<RadioDialog> {
// SingingCharacter? _character = SingingCharacter.banana;
String? _boxOption;
void _handleBoxItemChange(String? value) {
setState(() {
_boxOption = value;
});
}
#override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: const EdgeInsets.symmetric(horizontal: 0, vertical: 10),
title: const Text('Select option'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
RadioListTile<String>(
title: const Text('Banana'),
value: 'Banana',
groupValue: _boxOption,
onChanged: _handleBoxItemChange,
),
RadioListTile<String>(
title: const Text('Apple'),
value: 'Apple',
groupValue: _boxOption,
onChanged: _handleBoxItemChange,
),
RadioListTile<String>(
title: const Text('Orange'),
value: 'Orange',
groupValue: _boxOption,
onChanged: _handleBoxItemChange,
),
],
),
actions: [
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('I choose this!'),
),
],
);
}
}
I am with Flutter 2 month. Thanks
In the widget calling the dialog:
someVariable = await showDialog(...);
In the dialog widget:
Navigator.of(context).pop(returnedValue);
Are you able to declare a string outside of your widgets? Then you should be able to use it like so..
String newValue = ''; //**define a string outside the widgets**
class BoxScreen extends StatefulWidget {
const BoxScreen({Key? key}) : super(key: key);
static const routeName = '/box';
#override
State<BoxScreen> createState() => _BoxScreenState();
}
class _BoxScreenState extends State<BoxScreen> {
String? boxOptionValue;
final List<String> boxItems = [
'Banana',
'Apple',
'Orange',
];
Future<void> whatIsInTheBox(BuildContext context) async {
return await showDialog(
context: context,
builder: (BuildContext context) => const RadioDialog(),
);
}
#override
Widget build(BuildContext context) {
final String _randomBoxItem = (boxItems..shuffle()).first;
return Scaffold(
appBar: AppBar( title: const Text('BOX Game'),),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(_randomBoxItem),
_randomBoxItem == boxOptionValue ? Text('text1') : Text('text2'),
ElevatedButton.icon(
icon: const Icon(Icons.play_arrow_rounded),
onPressed: () {
whatIsInTheBox(context);
},
label: const Text('What is in BOX?'),
),
],
),
);
}
}
class RadioDialog extends StatefulWidget {
const RadioDialog({Key? key}) : super(key: key);
#override
State createState() => RadioDialogState();
}
class RadioDialogState extends State<RadioDialog> {
// SingingCharacter? _character = SingingCharacter.banana;
String? _boxOption;
void _handleBoxItemChange(String? value) {
setState(() {
newValue = value; //**Make it equal the new value**
});
}
#override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: const EdgeInsets.symmetric(horizontal: 0, vertical: 10),
title: Text(newValue), //**use it in any widget like this**
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
RadioListTile<String>(
title: const Text('Banana'),
value: 'Banana',
groupValue: _boxOption,
onChanged: _handleBoxItemChange,
),
RadioListTile<String>(
title: const Text('Apple'),
value: 'Apple',
groupValue: _boxOption,
onChanged: _handleBoxItemChange,
),
RadioListTile<String>(
title: const Text('Orange'),
value: 'Orange',
groupValue: _boxOption,
onChanged: _handleBoxItemChange,
),
],
),
actions: [
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('I choose this!'),
),
],
);
}
}

Failed assertion:'items == null || items.isEmpty || value == null || items.where((DropdownMenuItem<T> item) return item.value == value;}).length == 1'

class DropDown extends StatefulWidget {
const DropDown({
this.data,
this.hint,
Key key,
}) : super(key: key);
final List<String> data;
final String hint;
#override
_DropDownState createState() => _DropDownState();
}
String _chosenValue1;
class _DropDownState extends State<DropDown> {
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Container(
width: 250,
padding: const EdgeInsets.all(0.0),
child: DropdownButton<String>(
iconSize: 30,
isExpanded: true,
value: _chosenValue1,
//elevation: 5,
items: widget.data.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
hint: Text(
widget.hint,
style: TextStyle(
color: Colors.black,
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
onChanged: (String value) {
setState(() {
_chosenValue1 = value;
});
},
),
),
);
}
}
DropDown(
data: [
'Non-Blanchable',
'Partial thickness skin',
'Full thickness skin loss involving damage or necrosis',
'Obscured by necrosis'
],
hint: 'Assessment',
),
DropDown(
data: [
'Indistinct, diffuse,none ',
'Distinct,outline clearly'
],
hint: 'Assessment',
),
i have been stuck on this problem for a while now, When i have the same data inside the data it works however all the dropdown would become the same, I want to be able to have different data for different dropdown , but when i do so the error is caused and i cant figure out whats wrong with it
import 'package:flutter/material.dart';
class DropDown extends StatefulWidget {
DropDown({
this.data,
this.hint,
this.initialValue,
Key? key,
}) : super(key: key);
final List<String>? data;
final String? hint;
final String? initialValue;
String chosenValue1 = "";
#override
_DropDownState createState() => _DropDownState();
}
class _DropDownState extends State<DropDown> {
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Container(
width: 250,
padding: const EdgeInsets.all(0.0),
child: DropdownButton<String>(
iconSize: 30,
isExpanded: true,
value: widget.initialValue!.isEmpty ? null : widget.initialValue!,
//elevation: 5,
items: widget.data!.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
hint: Text(
widget.hint!,
style: const TextStyle(
color: Colors.black,
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
onChanged: (value) {
setState(() {
widget.chosenValue1 = value!;
});
},
),
),
);
}
}
import 'package:flutter/material.dart';
import 'dropdown.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
DropDown(
data: const [
'Non-Blanchable',
'Partial thickness skin',
'Full thickness skin loss involving damage or necrosis',
'Obscured by necrosis'
],
hint: 'Assessment',
initialValue: "Non-Blanchable",
),
DropDown(
data: const [
'Indistinct, diffuse,none',
'Distinct,outline clearly'
],
hint: 'Assessment',
initialValue: "",
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
Use the above code it will fix ur error
I tried running your code and, after making your data and hint required params and moving the chosenValue variable inside your _DropDownState, it works perfectly fine. Can you maybe share some steps with how to reproduce the error that you're seeing, because I see two different dropdowns with values I can select independently of each other.
As per your description of how to reproduce the error, I've tried adding navigation between two screens, but it still all works as intended.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Dropdowns(),
);
}
}
class Dropdowns extends StatelessWidget {
const Dropdowns();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.all(40),
child: Column(
children: [
Text('This is the first screen'),
DropDown(
data: [
'Non-Blanchable',
'Partial thickness skin',
'Full thickness skin loss involving damage or necrosis',
'Obscured by necrosis'
],
hint: 'Assessment',
),
DropDown(
data: ['Indistinct, diffuse,none ', 'Distinct,outline clearly'],
hint: 'Assessment',
),
ElevatedButton(
child: Text('Go to second screen'),
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SecondScreen(),
),
);
},
),
],
),
),
);
}
}
class DropDown extends StatefulWidget {
const DropDown({
required this.data,
required this.hint,
Key? key,
}) : super(key: key);
final List<String> data;
final String hint;
#override
_DropDownState createState() => _DropDownState();
}
class _DropDownState extends State<DropDown> {
String? _chosenValue1;
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Container(
width: 250,
padding: const EdgeInsets.all(0.0),
child: DropdownButton<String>(
iconSize: 30,
isExpanded: true,
value: _chosenValue1,
//elevation: 5,
items: widget.data.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
hint: Text(
widget.hint,
style: TextStyle(
color: Colors.black,
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
onChanged: (String? value) {
setState(() {
_chosenValue1 = value;
});
},
),
),
);
}
}
class SecondScreen extends StatelessWidget {
const SecondScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('SECOND SCREEN'),
),
body: Padding(
padding: EdgeInsets.all(40),
child: Column(
children: [
Text('This is the second screen'),
DropDown(
data: [
'Non-Blanchable',
'Partial thickness skin',
'Full thickness skin loss involving damage or necrosis',
'Obscured by necrosis'
],
hint: 'Assessment',
),
DropDown(
data: ['Indistinct, diffuse,none ', 'Distinct,outline clearly'],
hint: 'Assessment',
),
],
),
),
);
}
}
onChanged: (String value) {
setState(() {
_chosenValue = value;
selcat = null; use dropdown as category
_chosenValue == null
? Container()
: _chosenValue == "hi"
? _hi()
: _chosenValue == "hello"
? _hello()
: Container(),