Flutter - 如何在点击时更改按钮颜色并禁用其他按钮?

Flutter - How to change button color on the click and disable other buttons?

我有一个带有几个绿色按钮的列表视图,我需要在单击时将按钮的颜色更改为红色。问题是这样做时所有其他按钮都需要返回到它们的基本颜色绿色。

在下面的这个例子中(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;
    });
  }
}

在 myWidget2 中创建了 selectedValue 变量并为 evrey 按钮创建了 id,所以当你按下一个按钮时,它会设置 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 无状态并创建了两个新东西:

  1. ButtonData Class:分离需要控制的实际数据并使其可扩展。
  2. MyButtonList:StatefulWidget,包含一个布尔值列表以跟踪当前活动的 Button 这是一个例子:

创建新文件并复制以下代码并查看结果:

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));
  }
}

您似乎想通过使用 TextButtons 来模拟 RadioButtons。

一组 RadioListTile-s 只有一个可以激活。如果我理解正确的话,这就是你想要实现的目标。

我可以建议改用 RadioListTile-s,然后根据您的喜好设置样式(或主题):绿色表示不活动的 Tiles,红色表示活动的 Tiles。

下面只是演示RadioListTile的用法,更多关于样式的信息active-/nonactive-Tiles可以很容易地找到。

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

来源

https://googleflutter.com/flutter-radiolisttile/