Flutter入门-常用组件-Card、ListTile、Switch、CheckBox、Radio以及相应的ListTile

831 阅读9分钟

Card

Flutter Card 组件,卡片组件具有圆角和阴影,看起来有立体感。

构造函数

Card({
    Key key,
    this.color,
    this.shadowColor,
    this.elevation,
    this.shape,
    this.borderOnForeground = true,
    this.margin,
    this.clipBehavior,
    this.child,
    this.semanticContainer = true,
})

常用属性

color:颜色

color:Colors.blue[500],

elevation:阴影大小

elevation: elevation: 5.0,

shape:Card 的阴影效果

shape:const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(14.0))), 

margin:外边距

margin: EdgeInsets.only(top: 10.0,bottom: 10.0,left: 10.0,right: 10.0)

child

child: Text("内容"),

listTile

const ListTile({
    Key key,
    Widget leading, //头部图片(显示在标题前)
    Widget title, //标题
    Widget subtitle, //副标题
    Widget trailing, //尾部图标
    bool isThreeLine = false, //是否 3 行显示
    bool dense, //是否显示为紧凑模式(开启时整体条目会显示的更小)
    VisualDensity visualDensity, //组件的视觉密度,通过调整它可以使得标准 Material 组件之间更加紧凑或疏远。例如:VisualDensity(horizontal: -4, vertical: -4)
    EdgeInsetsGeometry contentPadding, //内边距
    bool enabled = true, //是否可用(禁用点击事件)
    GestureTapCallback onTap, //点击事件
    GestureLongPressCallback onLongPress, //长按事件
    bool selected = false, //选中状态(被选中后字体颜色与主题色一致,即 title、subtitle、secondary 是否采用 activeColor 的颜色)
    Color focusColor,
    Color hoverColor,
    FocusNode focusNode, //焦点管理
    bool autofocus = false, //自动获取焦点
  })

  • leading 通常是个 Icon 或 CircleAvatar;title/subtitle 通常是 Text;trailing 通常是 Icon;

  • 上面都是通常的用法,实际使用中 leading/title/subtitle/trailing 都可以是任何 widget,但建议采用通常的用法

  • title 只可单行显示,subtitle 根据 isThreeLine 决定;当不设置 subtitle 时(且 isThreeLine = false),整体条目会单行显示。

  • leading/trailing 小部件的高度会受到整体条目固定高度的限制,宽度不受限制。(整体条目的高度会根据 [是否有subtitle、isThreeLine、dense] 固定为 [48.0、56.0;64.0、72.0;76.0、88.0] 中的某固定值)

image.png

CheckBox和Switch

CheckBox

Switch和复选框Checkbox,它们都是继承自StatefulWidget,但它们的选择状态属于用户数据,所以最好由父Widget来管理其选择状态。当用户点击SwitchCheckbox时,它们会触发onChanged回调,我们可以在此回调中处理选择状态改变逻辑

CheckBox({
  Key key,
  @required bool value,                  //复选框的值
  bool tristate: false,                     //为true时复选框会多一个值为null的状态,复选框内显示为横线
  @required ValueChanged<bool> onChanged,    //点击复选框的回调
  Color activeColor,                      //选中时复选框的颜色
  Color checkColor,                      //选中时对号的颜色
  MaterialTapTargetSize materialTapTargetSize    //有效点击区域的大小
})

Switch

value 当前开关状态 布尔值 onChanged 监听(开关状态改变时调用) activeColor 打开 状态 轨道 和 按钮颜色 activeTrackColor 打开 状态轨道颜色 inactiveThumbColor 关闭 状态按钮颜色 inactiveTrackColor 关闭 状态轨道颜色 activeThumbImage 打开 状态下按钮图片 inactiveThumbImage 关闭 状态下按钮图片 materialTapTargetSize 配置tap目标的最小大小 dragStartBehavior 确定处理拖动启动行为的方式。(没看出有什么变化) Flutter 还提供了仿苹果的Switch组件CupertinoSwitch。下面来介绍一下:

属性 说明 value 当前开关状态 布尔值 onChanged 监听(开关状态改变时调用) activeColor 打开 状态 轨道 和 按钮颜色

Switch(
  activeColor:Colors.red,
  activeTrackColor:Colors.yellow,
  inactiveThumbColor:Colors.pink[200],
  inactiveTrackColor:Colors.black,
  activeThumbImage:AssetImage('images/aa.jpg'),
  inactiveThumbImage:AssetImage('images/ba.jpg'),
  dragStartBehavior:DragStartBehavior.start,
  
  value: this.valuea,
  onChanged: (bool v) {
    setState(() {
      this.valuea = v; 
    });
  }, 
),
CupertinoSwitch(
  value: this.valueb,
  onChanged: (bool value) { 
    setState(() { 
      this.valueb = value; 
    }); 
  },
),

image.png

image.png

Radio

value可以是任意类型的数据,只要和groupValue相同就是选中了
groupValue组内当前的选择的值,相同的groupValue被认定为一组单选框
onChanged选值更改监听
activeColor被选中时候按钮的颜色
fillColor按钮的填充颜色,可以根据状态(如MaterialState.selected)返回颜色值
overlayColor点击时的波纹的颜色
splashRadius波纹的半径

代码如下:在onChanged中修改当前的groupValue的值,当groupValue与这个组件的value属性的值相等时就表示这个单选框选中,否则就是不选中

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
 return new MaterialApp(
   home: Scaffold(
     body: RadioDemo()
   )
 );
}
}

enum SingingCharacter { lafayette, jefferson }

class RadioDemo extends StatefulWidget {
@override
_RadioDemoState createState() => new _RadioDemoState();
}

class _RadioDemoState extends State<RadioDemo> {
SingingCharacter _character = SingingCharacter.lafayette;

@override
Widget build(BuildContext context) {
 return Container(
   padding: EdgeInsets.only(top: 20.0),
   child: Column(
     children: <Widget>[
       Radio(
         value: SingingCharacter.lafayette,
         groupValue: _character,
         activeColor: Colors.red,
         onChanged: (SingingCharacter value) {
           print(value);
           setState(() {
             _character = value;
           });
         },
       ),
       Radio(
         value: SingingCharacter.jefferson,
         groupValue: _character,
         onChanged: (SingingCharacter value) {
           print(value);
           setState(() {
             _character = value;
           });
         },
       )
     ]
   )
 );
}
}

SwitchListTile

SwitchListTile “带标题的开关”

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluttertoast/generated/i18n.dart';

class SwitchDemo extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return TextFieldStateDemo();
  }
}

class TextFieldStateDemo extends State {
  bool _isCheck;

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

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text("Switch 和 SwitchListTile"),
        centerTitle: true,
      ),
      body: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            Row(),
            SizedBox(
              height: 20,
            ),
            Text(
              "一:普通的Switch",
              style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
            ),
            Switch(
              value: _isCheck,
              onChanged: _changed,
            ),
            Text(
              "二:特质的Switch",
              style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
            ),
            Switch(
              //来初始化此 Switch 是否被选中。true 表示选中 ,false 表示不选中
              value: _isCheck,
              //当 拖动改变状态时的回调。
              onChanged: _changed,
              //当 value 是 true 时 滑块的颜色。也就是开的状态时的颜色
              activeColor: Colors.red,
              // 当 value 是 true 时 滑道的颜色
              activeTrackColor: Colors.blueAccent,
              // 当 value 是 true 时 滑块上的图片。 如果 activeColor 也设置了  , 以 activeThumbImage 为准
              // 如果设置的是网络图片的话。当 滑块 滑到这里 才开始加载。所以 没加载出图片的之前 ,以 activeColor 为准

              activeThumbImage: NetworkImage(
                  "https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=3868620627,2694438302&fm=58"),

              // 当 value 是 false 时 滑块的颜色。也就是开的状态时的颜色
              inactiveThumbColor: Colors.amberAccent,
              // 当 value 是 false 时 滑道的颜色
              inactiveTrackColor: Colors.green,
              // 当 value 是 false 时 滑块上的图片。 如果 inactiveThumbColor 也设置了  , 以 inactiveThumbImage 为准
              inactiveThumbImage: AssetImage("images/hashiqi.jpg"),
            ),
            Text(
              "三:SwitchListTile",
              style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
            ),
            SizedBox(
              width: 200,
              child: SwitchListTile(
                // 是否选中 是否打开
                value: _isCheck,
                // 当打开关闭的时候的回调
                onChanged: _changed,
                // 选中时 滑块的颜色
                activeColor: Colors.red,
                // 选中时 滑道的颜色
                activeTrackColor: Colors.black,
                // 选中时 滑块的图片
//              activeThumbImage: AssetImage("images/hashiqi.jpg"),
                // 未选中时 滑块的颜色
                inactiveThumbColor: Colors.green,
                // 未选中时 滑道的颜色
                inactiveTrackColor: Colors.amberAccent,
                // 未选中时 滑块的颜色
                inactiveThumbImage: AssetImage("images/hashiqi.jpg"),
                // 标题
                title: Text("标题"),
                // 副标题 在标题下面的
//              subtitle: Text("副标题"),
                // 是不是三行, true 时: subtitle 不能为null, false时可以为 null
//              isThreeLine: true,
                // 如果为 true ,则 text 和 icon 都用 activeColor 时的color
//              selected: true,
                // 是否垂直密集居中
                dense: true,
                // 左边的一个东西
                secondary: Icon(Icons.access_time),
              ),
            ),
          ]),
    );
  }

  void _changed(isCheck) {
    setState(() {
      _isCheck = isCheck;
    });
  }
}

image.png

CheckBoxListTile

const CheckboxListTitle({
  Key key,
  @required bool value,
  @required ValueChanged<bool> onChanged,
  Color activeColor,
  Widget title,                    //复选框的主标题
  Widget subtitle,                  //复选框的副标题
  bool isThreeLine: false,          //文字是否为三行
  bool dense,                        //是否为垂直密集列表的一部分
  Widget secondary,                //图标
  bool selected: false,              //文字和图标颜色是否为选中的颜色(activeColor)
  ListTileControlAffinity controlAffinity: ListTileControlAffinity.platform    //文字、图标、复选框的排列顺序
});

dense

如果为true,表示这个复选框是一个列表的一部分,会缩小字体。
false时则是默认大小

isThreeLine

是否为三行,这是个坑。
首先要搭配subtitle使用,没有subtitle的话报错。
然后就是这个坑了,不是文字以三行显示,而是告诉程序,我现在的文字(title)是不是有三行。。。
title只有一行
isThreeLinefalse

image.png

isThreeLinetrue

image.png

可以看到为false的时候,文字会垂直局中,为true的时候,文字偏上,这是因为告诉程序这段文字有三行,会把三行文字整体垂直居中显示,这就造成了上边的情况。
title为三行
isThreeLinefalse

image.png

isThreeLinetrue

image.png

可以看到显示上并没有什么区别,这是因为设置为true的时候,告诉了程序这是三行文字,会居中显示,为false的时候实际上是把整个CheckboxListTile的区域撑满,居中不居中并没有什么区别。

title为两行和一行的情况显示是相同的,最多就是三行,超过三行的文字不会显示,会被剪切。
实际上isThreeLine是设置titlesubtitle同时存在时文字在垂直方向的显示方式

RadioListTile

  • value: 此 Radio 的 value 值

  • onChanged: 当选择此 radio 的时候 的回调。 参数就是 此 value 的值

  • groupValue: 如果 Radio 的 value 和 groupValue 一样 就 是此 Radio 选中 其他 设置为 不选中

  • activeColor: 选中的颜色

  • title: 标题, 具有代表性的就是 Text

  • subtitle: 副标题(在 title 下面), 具有代表性的就是 Text

  • isThreeLine = false: // 是否是三行文本

    • true : 副标题 不能为 null
    • false:如果没有副标题 ,就只有一行, 如果有副标题 ,就只有两行
  • dense: 是否密集垂直

  • secondary: 左边的一个控件

  • selected = false: text 和 icon 的 color 是否 是 activeColor 的颜色

  • controlAffinity = ListTileControlAffinity.platform:

    • ListTileControlAffinity.platform 根据不同的平台,来显示 对话框 的位置
    • ListTileControlAffinity.trailing:勾选在右,title 在中,secondary 在左
    • ListTileControlAffinity.leading :勾选在左,title 在中,secondary 在右
import 'package:flutter/material.dart';

class RadioDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
  return Scaffold(
    appBar: AppBar(
      title: Text("Radio 和 Radio RadioListTile "),
      centerTitle: true,
    ),
    body: RadioStateDemo(),
  );
}
}

class RadioStateDemo extends StatefulWidget {
@override
State<StatefulWidget> createState() {
  // TODO: implement createState
  return RadioSDemo();
}
}

class RadioSDemo extends State<RadioStateDemo> {
String groupValue;
String valueLiu;
String valueZhang;
String valueGuo;
String valueLi;

@override
void initState() {
  super.initState();
  groupValue = "刘德华";
  valueLiu = "刘德华";
  valueZhang = "张学友";
  valueGuo = "郭富城";
  valueLi = "黎明";
}

@override
Widget build(BuildContext context) {
  // TODO: implement build
  return Column(
    crossAxisAlignment: CrossAxisAlignment.center,
    children: <Widget>[
      SizedBox(
        height: 20,
      ),
      Text(
        "一:Radio",
        style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
      ),
      Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Radio<String>(
            // 此 Radio 的 value 值
            value: valueLiu,
            // 当选择此 radio 的时候 的回调。 参数就是 此 value 的值
            onChanged: (value) {
              setState(() {
                groupValue = value;
              });
            },
            // 如果 Radio 的 value 和 groupValue 一样 就 是此 Radio 选中 其他 设置为 不选中
            groupValue: groupValue,
            // 选中的颜色
            activeColor: Colors.red,
            // 响应手势的大小 , 默认是 48 * 48
            // MaterialTapTargetSize.shrinkWrap 水波纹 在中间
            // MaterialTapTargetSize.padded  水波纹 靠左上
            materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
          ),
          Radio(
            value: valueZhang,
            onChanged: (value) {
              setState(() {
                groupValue = value;
              });
            },
            groupValue: groupValue,
            activeColor: Colors.red,
            materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
          ),
          Radio(
            value: valueGuo,
            onChanged: (value) {
              setState(() {
                groupValue = value;
              });
            },
            groupValue: groupValue,
            activeColor: Colors.red,
            materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
          ),
          Radio(
            value: valueLi,
            onChanged: (value) {
              setState(() {
                groupValue = value;
              });
            },
            groupValue: groupValue,
            activeColor: Colors.red,
            materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
          ),
        ],
      ),
      SizedBox(
        height: 40,
      ),
      Text(
        "二:RadioListTile",
        style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
      ),
      // 不知道 为什么 Row 父布局 不行(暂时不知道why)
      Column(
//          mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          RadioListTile<String>(
            title: Text(valueLiu),
            // 必须要的属性
            value: valueLiu,
            //是否选中发生变化时的回调, 回调的 bool 值 就是是否选中 , true 是 选中
            groupValue: groupValue,
            onChanged: _changed,
            // 选中时 填充的颜色
            activeColor: Colors.red,
            // 标题, 具有代表性的就是 Text ,
            //        selected 如果是 true :
            //         如果 不设置 text 的 color 的话, text的颜色 跟随 activeColor
            // 副标题(在 title 下面), 具有代表性的就是 Text , 如果 不设置 text 的 color 的话, text的颜色 跟随 activeColor
            subtitle: Text("副标题"),
            // 是否是三行文本
            //        如果是 true : 副标题 不能为 null
            //        如果是 false:
            //                      如果没有副标题 ,就只有一行, 如果有副标题 ,就只有两行
            isThreeLine: false,
            // 是否密集垂直
            dense: false,
            // 左边的一个控件
//              secondary: Text("secondary"),
            // text 和 icon 的 color 是否 是 activeColor 的颜色
            selected: true,
            controlAffinity: ListTileControlAffinity.leading,
          ),
          RadioListTile<String>(
              title: Text(valueZhang),
              value: valueZhang,
              groupValue: groupValue,
              onChanged: _changed),
          RadioListTile<String>(
              title: Text(valueGuo),
              value: valueGuo,
              groupValue: groupValue,
              onChanged: _changed),
          RadioListTile<String>(
              title: Text(valueLi),
              value: valueLi,
              groupValue: groupValue,
              onChanged: _changed),
        ],
      )
    ],
  );
}

void _changed(value) {
  groupValue = value;
  setState(() {});
}
}

image.png