У меня есть список с несколькими зелеными кнопками, и мне нужно изменить цвет кнопки на красный при нажатии. Проблема в том, что при этом все остальные кнопки должны вернуться к своему основному зеленому цвету.
В приведенном ниже примере (рабочая версия на https://www.dartpad.dev/?id=b4ea6414b6a4ffcc7135579e673be845) все кнопки меняют цвет при нажатии независимо от других кнопок, но желаемый эффект заключается в том, что все остальные кнопки должны быть зелеными, когда нажатая кнопка красная.
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
MyWidget(
text: 'Button 1',
onPressed: () => print('Click'),
),
MyWidget(
text: 'Button 2',
onPressed: () => print('Click'),
),
MyWidget(
text: 'Button 3',
onPressed: () => print('Click'),
),
MyWidget(
text: 'Button 4',
onPressed: () => print('Click'),
),
],
)),
),
);
}
}
class MyWidget extends StatefulWidget {
const MyWidget({
Key? key,
required this.text,
required this.onPressed,
}) : super(key: key);
@override
State<MyWidget> createState() => _MyWidgetState();
final String text;
final VoidCallback onPressed;
}
class _MyWidgetState extends State<MyWidget> {
bool isFavourte = false;
@override
Widget build(BuildContext context) {
return ElevatedButton(
style: ElevatedButton.styleFrom(
primary: isFavourte ? Colors.red : Colors.green,
),
onPressed: () {
setState(() => isFavourte = !isFavourte);
widget.onPressed();
},
child: Text(widget.text));
}
}
Как это можно сделать?





Вот пример, делающий именно то, чего вы хотите достичь, сохраняя состояние каждой кнопки в списке и обновляя их все по мере изменения:
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
List<Map> buttonList = [
{
'label': 'button1',
'active': true,
},
{
'label': 'button2',
'active': true,
},
{
'label': 'button3',
'active': true,
},
{
'label': 'button4',
'active': true,
},
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: buttonList.length,
itemBuilder: (context, index){
return ElevatedButton(
onPressed: () => onPressed(buttonList[index]),
style: ButtonStyle(
backgroundColor: buttonList[index]['active']
? MaterialStateProperty.all(Colors.green)
: MaterialStateProperty.all(Colors.red),
),
child: Text(buttonList[index]['label']),
);
},
),
);
}
void onPressed(Map button){
setState(() {
for (var element in buttonList) {
element['active'] = false;
}
button['active'] = true;
});
}
}
Конечно. Важна техника. Сохраните состояние каждой кнопки, чтобы вы могли изменить его.
создал переменную selectedValue в myWidget2 и идентификатор для каждой кнопки, поэтому, когда вы нажимаете кнопку, она будет устанавливать selectedValue = id, так что только кнопка с id = selectedValue станет красной
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
int selectedValue = 0 ;
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: MyWidget2() ,
);
}
}
class ValueChanged extends Notification {
final int selectedValue ;
ValueChanged(this.selectedValue);
}
class MyWidget2 extends StatefulWidget {
const MyWidget2({
Key? key,
}) : super(key: key);
@override
State<MyWidget2> createState() => _MyWidget2State();
}
class _MyWidget2State extends State<MyWidget2> {
int selectedValue = 0 ;
@override
Widget build(BuildContext context) {
return Scaffold(
body: NotificationListener<ValueChanged>(
onNotification: (n) {
setState(() {
selectedValue = n.selectedValue ;
// Trigger action on parent via setState or do whatever you like.
});
return true;
},
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
MyWidget(
text: 'Button 1',
onPressed: () => print('Click'),
id: 1,
selectedValue :selectedValue ,
),
MyWidget(
text: 'Button 2',
onPressed: () => print('Click'),
id: 2,
selectedValue :selectedValue ,
),
MyWidget(
text: 'Button 3',
onPressed: () => print('Click'),
id:3,
selectedValue :selectedValue ,
),
MyWidget(
text: 'Button 4',
onPressed: () => print('Click'),
id:4,
selectedValue :selectedValue ,
),
],
)),
),
);
}
}
class MyWidget extends StatefulWidget {
const MyWidget({
Key? key,
required this.text,
required this.onPressed,
required this.id,
required this.selectedValue,
}) : super(key: key);
@override
State<MyWidget> createState() => _MyWidgetState();
final int id;
final String text;
final VoidCallback onPressed;
final int selectedValue ;
}
class _MyWidgetState extends State<MyWidget> {
@override
Widget build(BuildContext context) {
return ElevatedButton(
style: ElevatedButton.styleFrom(
primary: widget.id == widget.selectedValue ? Colors.red : Colors.green,
),
onPressed: () {
setState(() => ValueChanged(widget.id).dispatch(context));
widget.onPressed();
},
child: Text(widget.text));
}
}
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
final selectedIndexNotifier = ValueNotifier<int?>(null);
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: ValueListenableBuilder<int?>(
valueListenable: selectedIndexNotifier,
builder: (_, selectedIndex, __) => Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (int i = 1; i <= 4; i++)
MyWidget(
key: ValueKey(i),
text: 'Button $i',
isFavorite: selectedIndex == i,
onPressed: () => selectedIndex == i ? selectedIndexNotifier.value = null : selectedIndexNotifier.value = i
)
],
))),
),
);
}
}
class MyWidget extends StatelessWidget {
const MyWidget({
Key? key,
required this.text,
required this.isFavorite,
required this.onPressed,
}) : super(key: key);
@override
Widget build(BuildContext context) => ElevatedButton(
style: ElevatedButton.styleFrom(
primary: isFavorite ? Colors.red : Colors.green,
),
onPressed: onPressed,
child: Text(text));
final String text;
final bool isFavorite;
final VoidCallback onPressed;
}
Сначала я сделал MyWidget без состояния и создал две новые вещи:

Создайте новый файл и скопируйте следующий код и посмотрите результат:
class TestPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: Colors.blue[800],
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyButtonList(
buttons: [
ButtonData(text: 'Test'),
ButtonData(text: 'Test'),
ButtonData(text: 'Test'),
ButtonData(text: 'Test'),
ButtonData(text: 'Test'),
],
),
),
),
);
}
}
class MyButtonList extends StatefulWidget {
const MyButtonList({Key? key, required this.buttons}) : super(key: key);
final List<ButtonData> buttons;
@override
State<MyButtonList> createState() => _MyButtonListState();
}
class _MyButtonListState extends State<MyButtonList> {
late List<bool> favoriateState;
@override
void initState() {
favoriateState = List.generate(
widget.buttons.length, (index) => widget.buttons[index].isFavorite);
super.initState();
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (var i = 0; i < widget.buttons.length; i++)
MyWidget(
text: widget.buttons[i].text,
onPressed: () {
for (var j = 0; j < favoriateState.length; j++) {
favoriateState[j] = false;
}
setState(() {
favoriateState[i] = true;
if (widget.buttons[i].onPressed != null) {
widget.buttons[i].onPressed!();
}
});
},
isFavourte: favoriateState[i],
),
],
);
}
}
class ButtonData {
final String text;
final Function()? onPressed;
final bool isFavorite;
ButtonData({required this.text, this.onPressed, this.isFavorite = false});
}
class MyWidget extends StatelessWidget {
const MyWidget(
{Key? key,
required this.text,
required this.onPressed,
this.isFavourte = false})
: super(key: key);
final String text;
final Function()? onPressed;
final bool isFavourte;
@override
Widget build(BuildContext context) {
return ElevatedButton(
style: ElevatedButton.styleFrom(
primary: isFavourte ? Colors.red : Colors.green,
),
onPressed: onPressed,
child: Text(text));
}
}
Кажется, вы хотите эмулировать RadioButtons с помощью TextButtons.
В группе RadioListTile-ов может быть активен только один. И это то, чего вы хотите добиться, если я правильно вас понял.
Могу ли я предложить вместо этого использовать RadioListTile-s, а затем стилизовать (или тему) их по своему усмотрению: зеленый для неактивных плиток, красный для активных плиток.
Следующее просто демонстрирует использование RadioListTile, дополнительную информацию о стилизации активных/неактивных плиток можно легко найти.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// main application widget
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Application';
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyStatefulWidget(),
),
);
}
}
/// stateful widget that the main application instantiates
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
@override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
enum Fruit { apple, banana }
/// private State class that goes with MyStatefulWidget
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
Fruit? _fruit = Fruit.apple;
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: <Widget>[
RadioListTile<Fruit>(
title: const Text('Apple'),
value: Fruit.apple,
groupValue: _fruit,
onChanged: (Fruit? value) {
setState(() {
_fruit = value;
});
},
),
RadioListTile<Fruit>(
title: const Text('Banana'),
value: Fruit.banana,
groupValue: _fruit,
onChanged: (Fruit? value) {
setState(() {
_fruit = value;
});
},
),
],
),
);
}
}
Источник
Жизнеспособен ли этот подход, если у меня, например, 30 кнопок или более?