This is what I wan't to do:
In my app I've 2 custom widgets called SelectItems
. If the user clicks on item, it changes the background color. Everything is working just fine, but a code, which manages the state is a bit cumbersome:
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,
),
home: Home(),
);
}
}
class Home extends StatefulWidget {
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
List<SelectItemModel> selectItemModels = [
SelectItemModel(
text: 'First item',
isSelected: true,
),
SelectItemModel(
text: 'Second item',
isSelected: false,
),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.only(
top: 60,
),
color: Colors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: _buildSelectItems(),
),
),
);
}
List<SelectItem> _buildSelectItems() {
List<SelectItem> items = [];
for (var i = 0; i < selectItemModels.length; i++) {
var item = selectItemModels[i];
items.add(SelectItem(
text: item.text,
isSelected: item.isSelected,
onPressed: () => {_select(i)},
));
}
return items;
}
void _select(int index) {
for (var i = 0; i < selectItemModels.length; i++) {
setState(() {
i == index
? selectItemModels[i].isSelected = true
: selectItemModels[i].isSelected = false;
});
}
}
}
class SelectItem extends StatelessWidget {
const SelectItem({
@required this.text,
@required this.isSelected,
@required this.onPressed,
});
final String text;
final bool isSelected;
final VoidCallback onPressed;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onPressed,
child: Container(
width: 150,
height: 150,
decoration: BoxDecoration(
color: _getColor(isSelected),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: Colors.black38,
width: 2,
),
),
child: Center(
child: Text(text),
),
),
);
}
Color _getColor(bool isSelected) {
return isSelected ? Colors.blueAccent : Colors.white;
}
}
class SelectItemModel {
SelectItemModel({
this.text,
this.isSelected,
});
String text;
bool isSelected;
}
So how this works:
I've created a custom widget SelectItem
. It just displays a square shape with text in it. If it's selected
, then the color is blue, otherwise white.
class SelectItem extends StatelessWidget {
const SelectItem({
@required this.text,
@required this.isSelected,
@required this.onPressed,
});
final String text;
final bool isSelected;
final VoidCallback onPressed;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onPressed,
child: Container(
width: 150,
height: 150,
decoration: BoxDecoration(
color: _getColor(isSelected),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: Colors.black38,
width: 2,
),
),
child: Center(
child: Text(text),
),
),
);
}
Color _getColor(bool isSelected) {
return isSelected ? Colors.blueAccent : Colors.white;
}
}
Then I've a SelectItemModel
:
class SelectItemModel {
SelectItemModel({
this.text,
this.isSelected,
});
String text;
bool isSelected;
}
which I use in Home
StateFullWidget
to fill a List of SelectItems
.
class Home extends StatefulWidget {
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
List<SelectItemModel> selectItemModels = [
SelectItemModel(
text: 'First item',
isSelected: true,
),
SelectItemModel(
text: 'Second item',
isSelected: false,
),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.only(
top: 60,
),
color: Colors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: _buildSelectItems(),
),
),
);
}
List<SelectItem> _buildSelectItems() {
List<SelectItem> items = [];
for (var i = 0; i < selectItemModels.length; i++) {
var item = selectItemModels[i];
items.add(SelectItem(
text: item.text,
isSelected: item.isSelected,
onPressed: () => {_select(i)},
));
}
return items;
}
void _select(int index) {
for (var i = 0; i < selectItemModels.length; i++) {
setState(() {
i == index
? selectItemModels[i].isSelected = true
: selectItemModels[i].isSelected = false;
});
}
}
}
I think this part is the most problematic. Is there a simpler way of filling the widget with child widgets and managing the click behavior?