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}
}
Related
I have to create multiple files for different stalls but it seems so wrong and I know there's a better way but I just don't know how. Is there a way to create something like a page builder that will let me create multiple pages with different information from a single file. The difficult part is to make the onTap function of the images send the user to the stall_page of the selected stall. I tried doing this by making a view attribute in which I create a page and manually import the page route. But that involves creating a stall_info and stall_page for every single stall.
Instead of creating stall1_page, stall2_page and so on, can I create a generic stall function that will use the same page but just change the data? I know that's LITERALLY the point of object oriented programming languages but I'm really new to them as you'll tell my previous stupid questions.
This is the homescreen dashboard
class GridDashboard extends StatelessWidget {
Item item1 = Item(
title: 'Tray blazers',
subtitle: 'Open',
event: 'by Chef Tracy',
img: 'assets/images/tray_blazers-cr.png',
view: stallPage,
);
Item item2 = Item(
title: 'Papa Rimz',
subtitle: 'Open',
event: '',
img: 'assets/images/papa_rimz.png',
view: papaRimzPage,
);
Item item3 = Item(
title: 'W SAUCE',
subtitle: 'Open',
event: '',
img: 'assets/images/w_sauce-removebg.png',
view: wSaucePage,
);
Item item4 = Item(
title: 'African Kitchen',
subtitle: 'Open',
event: '',
img: 'assets/images/cherry-kitchen.png',
view: africanKitchenPage,
);
Item item5 = Item(
title: 'Suya Craze',
subtitle: 'Open',
event: '',
img: 'assets/images/suya_craze.png',
view: suyaCrazePage,
);
Item item6 = Item(
title: 'Zulkys cafe',
subtitle: 'Open',
event: '',
img: 'assets/images/zulkys-removeb.png',
view: zulkysCafePage,
);
Item item7 = Item(
title: 'Street food',
subtitle: 'Open',
event: '',
img: 'assets/images/street_food--removebg-.png',
view: streetFoodPage,
);
#override
Widget build(BuildContext context) {
List<Item> myList = [
item1,
item2,
item3,
item4,
item5,
item6,
item7,
];
return Flexible(
child: GridView.count(
childAspectRatio: 1.0,
padding: const EdgeInsets.only(left: 16, right: 16),
crossAxisCount: 2,
crossAxisSpacing: 18,
mainAxisSpacing: 18,
children: myList.map(
(data) {
return Container(
decoration: BoxDecoration(
color: const Color(0xff453658),
borderRadius: BorderRadius.circular(10),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: () {
Navigator.of(context).pushNamed(data.view);
},
child: Image.asset(
data.img,
width: 90, //double.infinity
),
),
const SizedBox(height: 14),
Text(
data.title,
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 13,
color: Colors.white,
),
),
const SizedBox(height: 8),
Text(
data.subtitle,
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 10,
color: Colors.white38,
),
),
const SizedBox(height: 8),
// Text(
// data.event,
// style: const TextStyle(
// fontWeight: FontWeight.w600,
// fontSize: 11,
// color: Colors.white70,
// ),
// ),
],
),
);
},
).toList(),
),
);
}
}
class Item {
String title;
String subtitle;
String event;
String img;
String view;
Item({
required this.title,
required this.subtitle,
required this.event,
required this.img,
required this.view,
});
}
This is my stall_page:
class StallPage extends StatefulWidget {
const StallPage({super.key});
#override
State<StallPage> createState() => _StallPageState();
}
class _StallPageState extends State<StallPage> {
var selected = 0;
final pageController = PageController();
final stall = Stall.generateRestaurant1();
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xff392850), //kBackground,
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomAppBar(
Icons.arrow_back_ios_outlined,
Icons.search_outlined,
leftCallback: () => Navigator.of(context).pop(),
),
StallInfo(), //
FoodList(
selected,
(int index) {
setState(() {
selected = index;
});
pageController.jumpToPage(index);
},
stall,
),
Expanded(
child: FoodListView(
selected,
(int index) {
setState(() {
selected = index;
});
},
pageController,
stall,
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 25),
height: 60,
child: SmoothPageIndicator(
controller: pageController,
count: stall.menu.length,
effect: CustomizableEffect(
dotDecoration: DotDecoration(
width: 8,
height: 8,
color: Colors.grey.withOpacity(0.5),
borderRadius: BorderRadius.circular(8),
),
activeDotDecoration: DotDecoration(
width: 10,
height: 10,
color: kBackground,
borderRadius: BorderRadius.circular(10),
dotBorder: const DotBorder(
color: kPrimaryColor,
padding: 2,
width: 2,
),
),
),
onDotClicked: (index) => pageController.jumpToPage(index),
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
backgroundColor: kPrimaryColor,
elevation: 2,
child: const Icon(
Icons.shopping_cart_outlined,
color: Colors.black,
size: 30,
),
),
);
}
}
This is my stall_info
class StallInfo extends StatelessWidget {
final stall = Stall.generateRestaurant1();
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(top: 40),
padding: const EdgeInsets.symmetric(horizontal: 25),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
stall.name,
style: const TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
Row(
children: [
Container(
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: Colors.blueGrey.withOpacity(0.4),
borderRadius: BorderRadius.circular(5),
),
child: Text(
stall.label,
style: const TextStyle(
color: Colors.white,
),
)),
const SizedBox(
width: 10,
),
],
)
],
),
ClipRRect(
borderRadius: BorderRadius.circular(50),
child: Image.asset(
stall.logoUrl,
width: 80,
),
),
],
),
const SizedBox(
height: 5,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
stall.desc,
style: const TextStyle(fontSize: 16),
),
Row(
children: [
const Icon(
Icons.star_outline,
color: Colors.amber,
),
Text(
'${stall.score}',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(width: 15),
],
)
],
)
],
),
);
}
}
And this is stall
class Stall {
String name;
String label;
String logoUrl;
String desc;
num score;
Map<String, List<Food>> menu;
Stall(
this.name,
this.label,
this.logoUrl,
this.desc,
this.score,
this.menu,
);
static Stall generateRestaurant1() {
return Stall(
'Tray blazers',
'Restaurant',
'assets/images/tray_blazers.jpg',
'Tray Blazers by Chef Tracy',
4.5,
{
'Recommended': Food.generateRecommendedFoods1(),
'Popular': Food.generatePopularFoods1(),
'Smoothie': [],
'Rice': [],
},
);
}
}
If I understand the question correctly, you want to open the StallPage but show different values on the page depending on which image (pertaining to a given 'Stall') was selected on the previous page? I.e. clicking on item2 should open the StallPage with the restaurant title "Papa Rimz" etc.?
In that case, you can pass the argument to your new route builder via the onTap() function as a constructor parameter instead of calling Stall.generateRestaurant1() with hardcoded values in a given dart file.
StallInfo
Instead of getting your stall data inside the build method, you simply accept it as a required parameter for your widget. Now you have access to the data (title, ...) anywhere inside here.
class StallInfo extends StatelessWidget {
// Contains the stall object with its name, label, menu etc.
final Stall stall;
StallInfo({super.key, required this.stall});
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(top: 40),
padding: const EdgeInsets.symmetric(horizontal: 25),
child: Column(
...
),
);
}
}
HomeScreen
I'm a bit confused as to what the item list in your your home screen is for. Are these food items in a restaurant? Because if so, I think it would be much easier to save them inside the stall as a list of items and then use that list here:
List<Stall> _stalls = [...];
I'd like to note here that you hardcoded all the items by name and then, in your build method, added them to a list. Since you don't need their names anywhere, it would be just a little bit better to move the List<Stall> myList outside the build method and simply assign the objects directly (that is, before you add a real database):
class GridDashboard extends StatelessWidget {
List<Stall> _stalls = [
Stall('Tray blazers', ...),
Stall('Papa Rimz', ...),
];
#override
Widget build(BuildContext context) {
// do something with your stalls, onTap, pass the element directly
....
children: _stalls.map(
(data) {
return GestureDetector(
onTap: (){
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => StallPage(stall: data)
));
}
);
}),
}
}
If you use a builder function for your GridView (which you should if there can be a lot of stalls), in the onTap() you can instead call:
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => StallPage(stall: _stalls.elementAt(index))
));
StallPage
This page will look something like this
class StallPage extends StatefulWidget {
final Stall stall; // Take in the stall you passed from your home screen
const StallPage({super.key, required this.stall});
#override
State<StallPage> createState() => _StallPageState();
}
class _StallPageState extends State<StallPage> {
var selected = 0;
final pageController = PageController();
#override
Widget build(BuildContext context) {
return Scaffold(
...
StallInfo(stall: widget.stall), // This is how you can access the values passed inside a StatefulWidget
...
);
}
}
The StateNotifier with the gender does not update as the dropdown choice changes. I have these providers at the beginning of the file:
class GenderController extends StateNotifier<String>{
GenderController(String state) : super(state);
}
final profileProvider = FutureProvider.autoDispose((ref) {
final details = ref.watch(authToken);
var data = API().staffProfile(token: details['token'], staffID: details['ID']);
return data;
});
final gender = StateNotifierProvider.autoDispose((ref) => GenderController(""));
And this is what the build method looks like in a ConsumerWidget:
Widget build(BuildContext context, WidgetRef ref) {
var dropdownValue = ref.watch(gender);
final details = ref.watch(profileProvider);
return details.when(
data: (data){
dropdownValue = data['gender'];
// show the form with the info
return Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: formKey,
child: Column(
children: [
DropdownButton2(
isExpanded: true,
underline: Container(
color: kDarkGrey,
height: 1.0,
),
buttonPadding: const EdgeInsets.symmetric(vertical: 10.0),
hint: const CustomText(
text: "Gender",
fontSize: 16.0,
color: kBlack,
),
items: genders
.map((item) => DropdownMenuItem<String>(
value: item,
child: Text(
item,
style: const TextStyle(
fontSize: 16,
color: kBlack,
),
overflow: TextOverflow.ellipsis,
),
))
.toList(),
value: dropdownValue == "" ? null : dropdownValue.toString(),
onChanged: (value) {
ref.watch(gender.notifier).state = value!;
}
),
],
)
),
);
},
error: (err, _){
debugPrint(_.toString());
return const Center(
child: CustomText(
text: "Error getting profile",
),
);
},
loading: () => Center(
child: CircularProgressIndicator(
color: kPrimary,
),
)
);
}
I have also tried making the data type of the dropdownValue to be final and then assigning the data to it as ref.watch(gender.notifier).state = data['gender']; but that resulted in a "At least listener of the StateNotifier Instance of 'GenderController' threw an exception when the notifier tried to update its state." error. Please help.
In callbacks, you must call ref.read
ref.read(gender.notifier).state = data['gender'];
I figured it out. I initialised the dropdown before calling the details.when like this:
DropdownButton2 dropdown = DropdownButton2(
isExpanded: true,
underline: Container(
color: kDarkGrey,
height: 1.0,
),
buttonPadding: const EdgeInsets.symmetric(vertical: 10.0),
hint: const CustomText(
text: "Gender",
fontSize: 16.0,
color: kBlack,
),
items: genders
.map((item) => DropdownMenuItem<String>(
value: item,
child: Text(
item,
style: const TextStyle(
fontSize: 16,
color: kBlack,
),
overflow: TextOverflow.ellipsis,
),
))
.toList(),
value: dropdownValue == "" ? null : dropdownValue.toString(),
onChanged: (value) {
ref.read(gender.notifier).state = value!;
}
);
And then I updated the StateNotifier after the data was returned in the FutureProvider like so:
final profileProvider = FutureProvider.autoDispose((ref) async {
final details = ref.watch(authToken);
var data = await API().staffProfile(token: details['token'], staffID: details['ID']);
ref.read(gender.notifier).state = data['gender'];
return data;
});
Everything works fine now, and as it should.
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');
}
},
I'm making a list of notifications using switches (there will be fifteen in total), but the way I did they turn them all on and off together, how do I turn them on and off individually? And do they accept refactoring to make the code cleaner?
I'm using SwitchListTile.
class CardButton extends StatefulWidget {
const CardButton({Key? key}) : super(key: key);
#override
State<CardButton> createState() => _CardButtonState();
}
class _CardButtonState extends State<CardButton> {
bool _toggled = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
Card(
child: SwitchListTile(
contentPadding: EdgeInsets.only(left: 16.0),
title: Text(
'botton',
style: TextStyle(
color: Colors.black,
),
),
value: _toggled,
onChanged: (bool value) {
setState(() => _toggled = value);
},
),
),
Card(
child: SwitchListTile(
contentPadding: EdgeInsets.only(left: 16.0),
title: Text(
'botton',
style: TextStyle(
color: Colors.black,
),
),
value: _toggled,
onChanged: (bool value) {
setState(() => _toggled = value);
},
),
),
Card(
child: SwitchListTile(
contentPadding: EdgeInsets.only(left: 16.0),
title: Text(
'botton',
style: TextStyle(
color: Colors.black,
),
),
value: _toggled,
onChanged: (bool value) {
setState(() => _toggled = value);
},
),
),
],
);
}
}
You need to create variables to hold the switch state for each switch (toggle) - in your case 15 in total.
From your sample code with individual values for each switch:
class CardButton extends StatefulWidget {
const CardButton({Key? key}) : super(key: key);
#override
State<CardButton> createState() => _CardButtonState();
}
class _CardButtonState extends State<CardButton> {
bool _switch1Toggled = false;
bool _switch2Toggled = false;
bool _switch3Toggled = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
Card(
child: SwitchListTile(
contentPadding: EdgeInsets.only(left: 16.0),
title: Text(
'switch 1',
style: TextStyle(
color: Colors.black,
),
),
value: _switch1Toggled,
onChanged: (bool value) {
setState(() => _switch1Toggled = value);
},
),
),
Card(
child: SwitchListTile(
contentPadding: EdgeInsets.only(left: 16.0),
title: Text(
'switch 2',
style: TextStyle(
color: Colors.black,
),
),
value: _switch2Toggled,
onChanged: (bool value) {
setState(() => _switch2Toggled = value);
},
),
),
Card(
child: SwitchListTile(
contentPadding: EdgeInsets.only(left: 16.0),
title: Text(
'switch 3',
style: TextStyle(
color: Colors.black,
),
),
value: _switch3Toggled,
onChanged: (bool value) {
setState(() => _switch3Toggled = value);
},
),
),
],
);
}
}
Thank you very much for your tip Ranvir Mohanlal. I created this template based on your information. I think it worked better.
class MultiSwitch extends StatefulWidget {
const MultiSwitch({Key? key}) : super(key: key);
#override
State<MultiSwitch> createState() => _MultiSwitchState();
}
class _MultiSwitchState extends State<MultiSwitch> {
bool val1 = true;
bool val2 = false;
bool val3 = false;
onChangeFunction1(bool newValue1) {
setState(() {
val1 = newValue1;
});
}
onChangeFunction2(bool newValue2) {
setState(() {
val2 = newValue2;
});
}
onChangeFunction3(bool newValue3) {
setState(() {
val3 = newValue3;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
customSwitch('button', val1, onChangeFunction1),
customSwitch('button', val2, onChangeFunction2),
customSwitch('button', val3, onChangeFunction3),
],
),
);
}
}
Widget customSwitch(String text, bool val, Function onChangeMethod) {
return Card(
child: SwitchListTile(
title: Text(
text,
style: const TextStyle(
color: Colors.black,
fontSize: 18,
),
),
value: val,
onChanged: (newValue) {
onChangeMethod(newValue);
}
),
);
}
I want to make the color of the values in the hour list yellow with the data in the selected list. Here is the code for the dropdown list and screenshot. how can I do that.
enter image description here
import 'package:flutter/material.dart';
class deneme extends StatefulWidget {
#override
_denemeState createState() => _denemeState();
}
class _denemeState extends State<deneme> {
List<String> hour = ["09:00", "10:00", "11:00", "12:00", "13:00", "14:00", "15:00", "16:00", "17:00"];
List<String> selected = ["09:00", "12:00", "16:00"];
int hourId;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Deneme"),
),
body: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.blueAccent, width: 2),
borderRadius: BorderRadius.all((Radius.circular(10)))),
padding: EdgeInsets.symmetric(vertical: 4, horizontal: 24),
margin: EdgeInsets.all(12),
child: DropdownButtonHideUnderline(
child: DropdownButton<int>(
items: hour.map((h) {
return DropdownMenuItem<int>(
child: Text(
h,
style: TextStyle(fontSize: 24),
),
value: hour.indexOf(h),
);
}).toList(),
value: hourId,
onChanged: (secilenOncelikId) {
setState(() {
hourId = secilenOncelikId;
});
},
hint: Text("Select Hour"),
),
),
),
);
}
}
Just compute the background color for each item: var backgroundColor = (selected.contains(h)) ? Colors.yellow : Colors.white; and assign it in your existing TextStyle:
child: DropdownButton<int>(
items: hour.map((h) {
var backgroundColor = (selected.contains(h)) ? Colors.yellow : Colors.white;
return DropdownMenuItem<int>(
child: Text(
h,
style: TextStyle(
fontSize: 24,
backgroundColor: backgroundColor,
),
),
value: hour.indexOf(h),
);
}).toList(),
value: hourId,
onChanged: (secilenOncelikId) {
setState(() {
hourId = secilenOncelikId;
});
},
hint: Text("Select Hour"),
),
Results:
you can wrap your child of Drop down Menu Item child (text) by colored container