GOOGLE ADS

miércoles, 13 de abril de 2022

Baldosa de doble expansión

Quiero escribir filtros con categorías y subcategorías que pueden tener la forma de una lista desplegable. Escribí esto con un ExpansionTile y puse una casilla de verificación en el título del ExpansionTile. Y me encontré con el problema de que la casilla de verificación para subcategorías funciona, pero la casilla de verificación para la categoría no funciona. Aquellas. cuando hace clic en una categoría, las subcategorías se abren / cierran. Pero si hace clic en la casilla de verificación en la categoría, no sucede nada. Echar un vistazo.

ingrese la descripción de la imagen aquí

¿Cómo puedo solucionar el problema para que la casilla de verificación en la categoría reaccione a los clics?

 class _FilterDialogUserState extends State<FilterDialogUser> {
Map<String, List<String>?> filters = {};
@override
void initState() {
super.initState();
filters = widget.initialState;
}
void _handleCheckFilter(bool checked, String key, String value) {
final currentFilters = filters[key]?? [];
if (checked) {
currentFilters.add(value);
} else {
currentFilters.remove(value);
}
setState(() {
filters[key] = currentFilters;
});
}
List<String> germany = ['Germany'];
List<String> germanyCar = [
'Audi',
'BMW',
'Volkswagen'
];
@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),
children: [
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ExpansionTile(
tilePadding: EdgeInsets.zero,
childrenPadding: EdgeInsets.symmetric(horizontal: 15),
title: CustomCheckboxTile(
value: filters['germany']?.contains(germany)?? false,
onChange: (check) => _handleCheckFilter(
check, 'germany', germany.toString()),
label: germany
.toString()
.replaceAll("[", '')
.replaceAll("]", ''),
),
initiallyExpanded: () {
for (final f in germanyCar) {
if (filters['germanyCar']?.contains(f)?? false) {
return true;
}
}
return false;
}(),
children: germanyCar.map((e) {
return Row(children: [
CustomCheckboxTile(
value: filters['germanyCar']?.contains(e)?? false,
onChange: (check) =>
_handleCheckFilter(check, 'germanyCar', e),
label: e,
),
]);
}).toList(),
),
const SizedBox(
height: 5,
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
widget.onApplyFilters(filters);
},
child: const Text('APPLY',
style: TextStyle(color: Colors.black)),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.grey),
)),
const SizedBox(
height: 5,
),
ElevatedButton(
onPressed: () async {
setState(() {
filters.clear();
});
widget.onApplyFilters(filters);
},
child: const Text('RESET FILTERS',
style: TextStyle(color: Colors.black)),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.grey),
)),
])
]);
}
}

custom_checkbox_tile.dart

 class CustomCheckboxTile extends StatelessWidget {
final String label;
final bool value;
final void Function(bool)? onChange;
const CustomCheckboxTile({Key? key,
required this.label, required this.value, this.onChange,}): super(key: key);
@override
Widget build(BuildContext context) {
return Row(
children: [
Checkbox(
visualDensity: VisualDensity.compact,
value: value,
onChanged: (_) {
if(onChange!= null) {
onChange!(!value);
}
},
),
Text(label),
],
);
}
}


Solución del problema

Aquí hay un ejemplo completo. Para mí, es más fácil y más limpio manejar datos de gran tamaño ValueNotifierque con setState. Puedes usar este código o personalizarlo como quieras. Copie y pegue en el DartPad para encontrar su respuesta.

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Checkbox Expansion Tile Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const Home(title: 'Checkbox Expansion Tile Demo'),
);
}
}
class Home extends StatefulWidget {
final String title;
const Home({Key? key, required this.title}): super(key: key);
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
@override
Widget build(BuildContext context) {
final controller = CountryController([
Country(
name: 'Germany',
cars: [
Car(name: 'Audi'),
Car(name: 'BMW'),
Car(name: 'Volkswagen'),
],
),
Country(
name: 'Sweden',
cars: [
Car(name: 'Koenigsegg'),
Car(name: 'Polestar'),
Car(name: 'Volvo'),
],
),
]);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: ValueListenableBuilder<List<Country>>(
valueListenable: controller,
builder: (context, countries, _) => Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
for (Country country in countries)
ExpansionTile(
title: Text(country.name),
leading: Checkbox(
value: country.isChecked,
onChanged: (value) {
//controller.checkCountry(country.name);
controller.checkAllByCountry(country.name, value);
},
),
children: [
for (Car car in country.cars)
CustomCheckboxTile(
title: car.name,
value: car.isChecked,
onChanged: (value) {
controller.checkCar(car.name);
},
),
],
),
TextButton(
onPressed: () {
controller.checkAll(true);
},
child: const Text('CHECK ALL'),
),
TextButton(
onPressed: () {
controller.checkAll(false);
},
child: const Text('RESET ALL'),
),
TextButton(
onPressed: () {
print(countries);
},
child: const Text('PRINT ALL'),
),
],
),
),
),
);
}
}
class CustomCheckboxTile extends ListTile {
CustomCheckboxTile({
Key? key,
required String title,
required bool value,
double leftPadding = 28.0,
ValueChanged<bool?>? onChanged,
}): super(
key: key,
title: Text(title),
leading: Padding(
padding: EdgeInsets.only(left: leftPadding),
child: Checkbox(
value: value,
onChanged: onChanged,
),
),
);
}
class Country {
Country({
required this.name,
this.isChecked = false,
this.cars = const [],
});
final String name;
final List<Car> cars;
bool isChecked;
@override
String toString() =>
'Country(name: $name, isChecked: $isChecked, cars: $cars)';
}
class Car {
Car({required this.name, this.isChecked = false});
final String name;
bool isChecked;
@override
String toString() => 'Car(name: $name, isChecked: $isChecked)';
}
class CountryController extends ValueNotifier<List<Country>> {
CountryController(List<Country>? countries): super(countries?? const []);
void checkCountry(String countryName) {
value = [
for (var country in value)
if (country.name == countryName)
Country(
name: country.name,
isChecked: country.isChecked =!country.isChecked,
cars: country.cars,
)
else
country
];
}
void checkCar(String carName) {
value = [
for (var country in value)
Country(
name: country.name,
isChecked: country.isChecked,
cars: [
for (var car in country.cars)
if (car.name == carName)
Car(
name: car.name,
isChecked: car.isChecked =!car.isChecked,
)
else
car
],
),
];
}
void checkAllByCountry(String countryName, bool? isChecked) {
if (isChecked == null) return;
value = [
for (var country in value)
if (country.name == countryName)
Country(
name: country.name,
isChecked: isChecked,
cars: [
for (var car in country.cars)
Car(
name: car.name,
isChecked: isChecked,
),
],
)
else
country
];
}
void checkAll(bool? isChecked) {
value = [
for (var country in value)
Country(
name: country.name,
isChecked: isChecked?? false,
cars: [
for (var car in country.cars)
Car(
name: car.name,
isChecked: isChecked?? false,
),
],
),
];
}
}

Actualizar

Se agregó verificar todo y restablecer todos los botones.

No hay comentarios:

Publicar un comentario

Regla de Firestore para acceder a la generación de subcolección Permisos faltantes o insuficientes

Tengo problemas con las reglas de Firestore para permitir el acceso a algunos recursos en una subcolección. Tengo algunos requests document...