Flutter:自定义单选按钮

Flutter : Custom Radio Button

如何在 flutter 中创建这样的自定义单选按钮组

您可以使用 ListView 和 List Item 创建它,并使用一个局部变量来存储所选项目。您可以根据变量呈现选定的 ListItem。

P.S。如果您需要代码片段,请告诉我。

[编辑]

按照您的要求,这是代码片段,它将向您展示如何维护每个 ListView 项目的状态。

现在您可以使用它并按照您想要的方式进行制作。如果你只想要一个选定的项目,你可以这样写逻辑。

void main() {
  runApp(new MaterialApp(
    home: new ListItemDemo(),
  ));
}

class ListItemDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("ListItem"),
      ),
      body: new ListView.builder(
          itemCount: 10,
          itemBuilder: (BuildContext context, int index) {
            return new MyListItem(
              title: "Hello ${index + 1}",
            );
          }),
    );
  }
}

class MyListItem extends StatefulWidget {
  final String title;

  MyListItem({this.title});

  @override
  _MyListItemState createState() => new _MyListItemState();
}

class _MyListItemState extends State<MyListItem> {
  bool isSelected;

  @override
  void initState() {
    super.initState();
    isSelected = false;
  }

  @override
  Widget build(BuildContext context) {
    return new Row(
      children: <Widget>[
        new Text("${widget.title} ${isSelected ? "true" : "false"}"),
        new RaisedButton(
          onPressed: () {
            if (isSelected) {
              setState(() {
                isSelected = false;
              });
            } else {
              setState(() {
                isSelected = true;
              });
            }
          },
          child: new Text("Select"),
        )
      ],
    );
  }
}

这是完整的代码

class CustomRadio extends StatefulWidget {
  @override
  createState() {
    return new CustomRadioState();
  }
}

class CustomRadioState extends State<CustomRadio> {
  List<RadioModel> sampleData = new List<RadioModel>();

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    sampleData.add(new RadioModel(false, 'A', 'April 18'));
    sampleData.add(new RadioModel(false, 'B', 'April 17'));
    sampleData.add(new RadioModel(false, 'C', 'April 16'));
    sampleData.add(new RadioModel(false, 'D', 'April 15'));
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("ListItem"),
      ),
      body: new ListView.builder(
        itemCount: sampleData.length,
        itemBuilder: (BuildContext context, int index) {
          return new InkWell(
            //highlightColor: Colors.red,
            splashColor: Colors.blueAccent,
            onTap: () {
              setState(() {
                sampleData.forEach((element) => element.isSelected = false);
                sampleData[index].isSelected = true;
              });
            },
            child: new RadioItem(sampleData[index]),
          );
        },
      ),
    );
  }
}

class RadioItem extends StatelessWidget {
  final RadioModel _item;
  RadioItem(this._item);
  @override
  Widget build(BuildContext context) {
    return new Container(
      margin: new EdgeInsets.all(15.0),
      child: new Row(
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          new Container(
            height: 50.0,
            width: 50.0,
            child: new Center(
              child: new Text(_item.buttonText,
                  style: new TextStyle(
                      color:
                          _item.isSelected ? Colors.white : Colors.black,
                      //fontWeight: FontWeight.bold,
                      fontSize: 18.0)),
            ),
            decoration: new BoxDecoration(
              color: _item.isSelected
                  ? Colors.blueAccent
                  : Colors.transparent,
              border: new Border.all(
                  width: 1.0,
                  color: _item.isSelected
                      ? Colors.blueAccent
                      : Colors.grey),
              borderRadius: const BorderRadius.all(const Radius.circular(2.0)),
            ),
          ),
          new Container(
            margin: new EdgeInsets.only(left: 10.0),
            child: new Text(_item.text),
          )
        ],
      ),
    );
  }
}

class RadioModel {
  bool isSelected;
  final String buttonText;
  final String text;

  RadioModel(this.isSelected, this.buttonText, this.text);
}

使用:

void main() {
  runApp(new MaterialApp(
    home: new CustomRadio(),
  ));
}

截图:

我是通过以下逻辑实现的。 如需详细说明请回复

    import 'package:flutter/material.dart';
    
    class Parent extends StatefulWidget {
      Parent({
        Key key,
      }) : super(key: key);
    
      @override
      _ParentState createState() => _ParentState();
    }
    
    class _ParentState extends State<Parent> {
      int _selectedItem = 0;
    
      selectItem(index) {
        setState(() {
          _selectedItem = index;
          print(selectItem.toString());
        });
      }
    
      @override
      Widget build(BuildContext context) {
        //...YOUR WIDGET TREE HERE
    
        return ListView.builder(
          shrinkWrap: true,
          itemCount: 5,
          itemBuilder: (context, index) {
            return CustomItem(
              selectItem, // callback function, setstate for parent
              index: index,
              isSelected: _selectedItem == index,
              title: index.toString(),
            );
          },
        );
      }
    }
    
    class CustomItem extends StatefulWidget {
      final String title;
      final int index;
      final bool isSelected;
      Function(int) selectItem;
    
      CustomItem(
        this.selectItem, {
        Key key,
        this.title,
        this.index,
        this.isSelected,
      }) : super(key: key);
    
      _CustomItemState createState() => _CustomItemState();
    }
    
    class _CustomItemState extends State<CustomItem> {
      @override
      Widget build(BuildContext context) {
        return Row(
          children: <Widget>[
            Text("${widget.isSelected ? "true" : "false"}"),
            RaisedButton(
              onPressed: () {
                widget.selectItem(widget.index);
              },
              child: Text("${widget.title}"),
            )
          ],
        );
      }
    }
import 'package:flutter/material.dart';
class CustomRadio extends StatefulWidget {
  @override
  createState() {
    return new CustomRadioState();
  }
}

class CustomRadioState extends State<CustomRadio> {
  List<RadioModel> sampleData = new List<RadioModel>();

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    sampleData.add(new RadioModel(true, 'A',0xffe6194B));
    sampleData.add(new RadioModel(false, 'B',0xfff58231));
    sampleData.add(new RadioModel(false, 'C',0xffffe119));
    sampleData.add(new RadioModel(false, 'D',0xffbfef45));
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("ListItem"),
      ),
      body: new ListView.builder(
        itemCount: sampleData.length,
        itemBuilder: (BuildContext context, int index) {
          return new InkWell(
            splashColor: Colors.blueAccent,
            onTap: () {
              setState(() {
                sampleData.forEach((element) => element.isSelected = false);
                sampleData[index].isSelected = true;
              });
            },
            child: new RadioItem(sampleData[index]),
          );
        },
      ),
    );
  }
}

class RadioItem extends StatelessWidget {
  final RadioModel _item;
  RadioItem(this._item);
  @override
  Widget build(BuildContext context) {
    return new Container(
      margin: new EdgeInsets.all(15.0),
      child: new Row(
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          new Container(
            height: 25.0,
            width: 25.0,
            alignment: Alignment.center,
            child:Container(
            height: 15.0,
            width: 15.0,
              decoration: new BoxDecoration(
              color:Color(_item.colorCode),
              borderRadius: const BorderRadius.all(const Radius.circular(15)),
            )

            ),
            decoration: new BoxDecoration(
              color: Colors.transparent,
              border: new Border.all(
                  width: 3.0,
                  color: _item.isSelected
                      ? Color(_item.colorCode)
                      : Colors.transparent),
              borderRadius: const BorderRadius.all(const Radius.circular(25)),
            ),
          ),
          new Container(
            margin: new EdgeInsets.only(left: 10.0)
          )
        ],
      ),
    );
  }
}

class RadioModel {
  bool isSelected;
  final String buttonText;
  final int colorCode;

  RadioModel(this.isSelected, this.buttonText,this.colorCode);
}
void main() {
  runApp(new MaterialApp(
    home: new CustomRadio(),
  ));
}

点此查看-> Here

我的 RadioButton 就像 'Radio' 小部件:

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

class RadioButton<T> extends StatefulWidget {
  RadioButton({
    Key key,
    @required this.value,
    @required this.caption,
    @required this.groupValue,
    @required this.onChanged,
  })  : assert(value != null),
        assert(caption != null),
        assert(groupValue != null),
        assert(onChanged != null),
        super(key: key);

  final T value;
  final T groupValue;
  final String caption;
  final Function onChanged;

  @override
  State<StatefulWidget> createState() => _RadioButtonState();
}

class _RadioButtonState extends State<RadioButton> {
  @override
  Widget build(BuildContext context) {
    final bool selected = widget.value == widget.groupValue;

    return GestureDetector(
      onTap: () {
        widget.onChanged(widget.value);
      },
      child: Container(
        width: double.maxFinite,
        decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(8),
            color: selected ? Colors.red : Colors.white),
        child: Padding(
          padding: const EdgeInsets.all(16),
          child: Text(
            widget.caption,
            textAlign: TextAlign.center,
            style: Theme.of(context)
                .textTheme
                .button
                .copyWith(color: selected ? Colors.white : Colors.red),
          ),
        ),
      ),
    );
  }
}

屏幕截图(空安全)


完整代码:

  1. 创建此自定义 class。

    class MyRadioListTile<T> extends StatelessWidget {
      final T value;
      final T groupValue;
      final String leading;
      final Widget? title;
      final ValueChanged<T?> onChanged;
    
      const MyRadioListTile({
        required this.value,
        required this.groupValue,
        required this.onChanged,
        required this.leading,
        this.title,
      });
    
      @override
      Widget build(BuildContext context) {
        final title = this.title;
        return InkWell(
          onTap: () => onChanged(value),
          child: Container(
            height: 56,
            padding: EdgeInsets.symmetric(horizontal: 16),
            child: Row(
              children: [
                _customRadioButton,
                SizedBox(width: 12),
                if (title != null) title,
              ],
            ),
          ),
        );
      }
    
      Widget get _customRadioButton {
        final isSelected = value == groupValue;
        return Container(
          padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
          decoration: BoxDecoration(
            color: isSelected ? Colors.blue : null,
            borderRadius: BorderRadius.circular(4),
            border: Border.all(
              color: isSelected ? Colors.blue : Colors.grey[300]!,
              width: 2,
            ),
          ),
          child: Text(
            leading,
            style: TextStyle(
              color: isSelected ? Colors.white : Colors.grey[600]!,
              fontWeight: FontWeight.bold,
              fontSize: 18,
            ),
          ),
        );
      }
    }
    
  2. 像常规一样在您的小部件中使用它 RadioListTile

    class _MyPageState extends State<MyPage> {
      int _value = 1;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Column(
            children: [
              MyRadioListTile<int>(
                value: 1,
                groupValue: _value,
                leading: 'A',
                title: Text('One'),
                onChanged: (value) => setState(() => _value = value!),
              ),
              MyRadioListTile<int>(
                value: 2,
                groupValue: _value,
                leading: 'B',
                title: Text('Two'),
                onChanged: (value) => setState(() => _value = value!),
              ),
              MyRadioListTile<int>(
                value: 3,
                groupValue: _value,
                leading: 'C',
                title: Text('Three'),
                onChanged: (value) => setState(() => _value = value!),
              ),
            ],
          ),
        );
      }
    }
    

https://i.stack.imgur.com/Hq0O2.png

这是自定义单选组按钮小部件。您可以根据需要轻松自定义所有 属性。使用:

GroupRadioButton(
              label: [Text("A"), Text("B"), Text("C"), Text("D")],
              padding: EdgeInsets.symmetric(vertical: 10),
              spaceBetween: 5,
              radioRadius: 10,
              color: Const.mainColor,
              onChanged: (listIndex) {
                 print(listIndex);
              },
),

这是GroupRadioButton小部件

import 'package:flutter/material.dart';

class GroupRadioButton extends StatefulWidget {
  GroupRadioButton({
    @required this.label,
    @required this.padding,
    @required this.onChanged,
    this.color = Colors.blue,
    this.radioRadius = 14.0,
    this.spaceBetween = 5.0,
    this.mainAxisAlignment = MainAxisAlignment.start,
    this.crossAxisAlignment = CrossAxisAlignment.start,
  });

  final Color color;
  final List<Widget> label;
  final EdgeInsets padding;
  final Function(int) onChanged;
  final double radioRadius;
  final double spaceBetween;
  final MainAxisAlignment mainAxisAlignment;
  final CrossAxisAlignment crossAxisAlignment;

  @override
  _GroupRadioButtonState createState() => _GroupRadioButtonState();
}

class _GroupRadioButtonState extends State<GroupRadioButton> {
  int selectedIndex = 0;
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
        shrinkWrap: true,
        itemCount: widget.label != null ? widget.label.length : 0,
        itemBuilder: (context, index) {
          return LabeledRadio(
            selectedIndex: selectedIndex,
            color: widget.color,
            onChanged: (value) {
              setState(() {
                selectedIndex = value;
                widget.onChanged(value);
                // print(value);
              });
            },
            index: index,
            label: widget.label[index],
            crossAxisAlignment: widget.crossAxisAlignment,
            mainAxisAlignment: widget.mainAxisAlignment,
            radioRadius: widget.radioRadius,
            spaceBetween: widget.spaceBetween,
            padding: widget.padding,
          );
        });
  }
}

class LabeledRadio extends StatelessWidget {
  LabeledRadio({
    @required this.label,
    @required this.index,
    @required this.color,
    //@required this.groupValue,
    //@required this.value,
    @required this.onChanged,
    @required this.radioRadius,
    @required this.padding,
    @required this.spaceBetween,
    @required this.mainAxisAlignment,
    @required this.crossAxisAlignment,
    this.selectedIndex,
  });

  final Color color;
  final int selectedIndex;
  final Widget label;
  final index;
  final EdgeInsets padding;
  //final bool groupValue;
  //final bool value;
  final Function(int) onChanged;
  final double radioRadius;
  final double spaceBetween;
  final MainAxisAlignment mainAxisAlignment;
  final CrossAxisAlignment crossAxisAlignment;

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: () {
        onChanged(index);
      },
      child: Padding(
        padding: padding,
        child: Row(
          mainAxisAlignment: mainAxisAlignment,
          crossAxisAlignment: crossAxisAlignment,
          children: <Widget>[
            Container(
              decoration: BoxDecoration(
                //color: Const.mainColor,
                shape: BoxShape.circle,
                border: Border.all(color: color, width: 2),
              ),
              padding: EdgeInsets.all(2),
              child: selectedIndex == index
                  ? Container(
                      height: radioRadius,
                      width: radioRadius,
                      decoration: BoxDecoration(
                        color: color,
                        shape: BoxShape.circle,
                      ),
                    )
                  : Container(
                      height: radioRadius,
                      width: radioRadius,
                    ),
            ),
            SizedBox(
              width: spaceBetween,
            ),
            label,
          ],
        ),
      ),
    );
  }
}