阅读 2687

Flutter第4天--基础控件(下)+Flex布局详解

Flutter七日游第四天:2018-12-19 天气:晴朗

零、前言

最近有些人问我怎么学的,操作这么6,有没有什么技巧。
今天一开始借助Image来给大家说一个分析的小技巧,让你不到30行代码画出下图
不要问有什么用,有用的时候自然会用到,有知识储备,留个印象也是好的

图片的颜色混合模式.png


Row和Column应该说是非常常用的控件,其中有几个属性挺重要,
本文最后,我将对Flex布局(Row和Column的父类)进行细致的讲解,希望你不要错过。


一、第一组控件:

第一组.png


1.图片Image

图片的使用.png


1.1源码一览:

下面是我从源码里翻译的,仅供参考

  const Image({
    Key key,
ImageProvider       @required this.image,----图片提供器
double              this.width,----宽
double              this.height,----高
Color               this.color,----颜色
BoxFit              this.fit,----适应模式
colorBlendMode      this.colorBlendMode,----颜色混合模式
AlignmentGeometry   this.alignment = Alignment.center,----对齐模式
ImageRepeat         this.repeat = ImageRepeat.noRepeat,----重复模式
String              this.semanticLabel,----语义标签
bool                this.excludeFromSemantics = false,----是否从语义中排除该图像
Rect                this.centerSlice,----.9图的中心区域切片
bool                this.matchTextDirection = false,----是否匹配文字分析
bool                this.gaplessPlayback = false,----图片提供器改变
FilterQuality       this.filterQuality = FilterQuality.low,---过滤器品质
复制代码

1.2:优雅地查看:图片的适应模式--BoxFit

也许你为了查看模式,改一次,看一次,千万不要这样,即费时间,比较的效果又差
你需要学会用数组或map去动态生成,让变化去应对变化,才能以不变应万变。
下面的效果呈现,也就用了十几行代码而已,而且准确地表述了BoxFit的各种情况

图片的适应模式.png

var fitMode = [BoxFit.none,BoxFit.contain, BoxFit.cover,
  BoxFit.fill, BoxFit.fitHeight,BoxFit.fitWidth,BoxFit.scaleDown];

//循环生成Image控件
formImgs() {
  var imgLi = <Widget>[];
  fitMode.forEach((fit) {
    imgLi.add(Container(
        width: 100,
        height: 50,
        color: randomRGB(),
        child: Image(
          image: AssetImage("images/wy_200x300.jpg"),
          fit: fit,
        )));
  });
  return imgLi;
}

var imgBox = Row(
  children: formImgs(),
);
复制代码

1.3:优雅地查看:颜色混合模式--colorBlendMode

晕死---29种叠合模式,Android一共也才18个,Flutter还真会找事...
那么多情况,Row肯定不够使,想想昨天的卡片,Wrap能当此大任

//叠合模式数组
var colorBlendMode = [
  BlendMode.clear,BlendMode.src,BlendMode.dst,
  BlendMode.srcOver,BlendMode.dstOver,BlendMode.srcIn,
  BlendMode.dstIn,BlendMode.srcOut,BlendMode.dstOut,
  BlendMode.srcATop,BlendMode.dstATop,BlendMode.xor,
  BlendMode.plus, BlendMode.modulate,BlendMode.screen,
  BlendMode.overlay,BlendMode.darken,BlendMode.lighten, 
  BlendMode.colorDodge,BlendMode.colorBurn,BlendMode.hardLight,
  BlendMode.softLight,BlendMode.difference,BlendMode.exclusion,
  BlendMode.multiply,BlendMode.hue,BlendMode.saturation,
  BlendMode.color, BlendMode.luminosity,
];

//循环生成Image控件
formImgsColorBlendMode() {
  var imgLi = <Widget>[];
  colorBlendMode.forEach((mode) {
    imgLi.add(Column(children: <Widget>[
      Padding( child:Image(
        width: 60,
        height: 60,
        image: AssetImage("images/icon_90.png"),
        color: Colors.red,
        colorBlendMode: mode,
      ), padding: EdgeInsets.all(5),),
      Text(mode.toString().split(".")[1])
    ]));
  });
  return imgLi;
}

var imgBox = Wrap(
  children: formImgsColorBlendMode(),
);
复制代码

一共就这些代码,就能实现下面的效果,Android也好,Flutter也好,套路都是一样的
当你遇到很多种情况的问题时,都可以用这个套路,多分析,你才能巩固自己的知识库

图片的颜色混合模式.png

重复模式,脑子想想也就知道了,这里就不演示了


1.4:使用Image的方法加载图片

这个等到文件读取再提一下,基本字段和Image是一样的,所以不用担心。

资源:Image.asset(String name,
文件:Image.file(File file,
网络:Image.network(String src,
内存:Image.memory(Uint8List bytes,
复制代码

2.IconButton
2.1源码一览:
  const IconButton({
double                  this.iconSize = 24.0,
EdgeInsetsGeometry      this.padding = const EdgeInsets.all(8.0),
AlignmentGeometry       this.alignment = Alignment.center,
Widget                  @required this.icon,
Color                   this.color,
Color                   this.highlightColor,
Color                   this.splashColor,
Color                   this.disabledColor,
VoidCallback            @required this.onPressed,
String                  this.tooltip
复制代码

2.2简单操作

IconButton.gif

var iconBtn = IconButton(
  padding: EdgeInsets.only(),
  onPressed: () {
    print("clicked");
  },
  icon: Icon(Icons.android, size: 40, color: Colors.deepPurpleAccent),
  tooltip: "android",
  highlightColor: Colors.red,//点击时间稍长的时候背景渐变到这个颜色
  splashColor: Colors.blue,//点击时一闪而过的颜色
  disabledColor: Colors.blueGrey,
);
复制代码

3.ButtonBar
3.1源码一览:
const ButtonBar({
    Key key,
    this.alignment = MainAxisAlignment.end,
    this.mainAxisSize = MainAxisSize.max,
    this.children = const <Widget>[],
复制代码

3.2简单操作

BottonBar使用.png

var btnBar = ButtonBar(
  alignment: MainAxisAlignment.center,
  children: <Widget>[iconBtn,iconBtn,iconBtn,iconBtn],
);
复制代码
卡片拆包:
- - -

二、第二组控件:

第二组.png

这一组都继承自MaterialButton,所以属性几乎一致,这里看一下MaterialButton
经历了这么多控件,属性基本上都差不多,看到名字也知道大概意思。

  const MaterialButton({
    @required this.onPressed,----点击事件----VoidCallback
    this.onHighlightChanged,
    this.textTheme,----按钮文字主题----ButtonTextTheme
    this.textColor,----文字颜色----Color
    this.disabledTextColor,----不可用时文字颜色----Color
    this.color,----背景颜色----Color
    this.disabledColor,----
    this.highlightColor,----
    this.splashColor,----
    this.colorBrightness,
    this.elevation,-----阴影高----
    this.highlightElevation,
    this.disabledElevation,
    this.padding,-----内边距----
    this.shape,-----形状----
    this.clipBehavior = Clip.none,
    this.materialTapTargetSize,
    this.animationDuration,
    this.minWidth,
    this.height,
    this.child,
复制代码

1.RaisedButton--凸起的按钮

RaisedButton和Android的内置Button基本上是一致的

1.1源码一览:
  const RaisedButton({
    @required VoidCallback onPressed,
    ValueChanged<bool> onHighlightChanged,
    ButtonTextTheme textTheme,
    Color textColor,
    Color disabledTextColor,
    Color color,
    Color disabledColor,
    Color highlightColor,
    Color splashColor,
    Brightness colorBrightness,
    double elevation,
    double highlightElevation,
    double disabledElevation,
    EdgeInsetsGeometry padding,
    ShapeBorder shape,
    Clip clipBehavior = Clip.none,
    MaterialTapTargetSize materialTapTargetSize,
    Duration animationDuration,
    Widget child,
复制代码

1.2简单操作

RaisedButton

var raisedButton = RaisedButton(
  onPressed: () {},
  child: Text("Toly"),
  color: Color(0xffF88B0A),
  highlightColor: Colors.blue,
);

复制代码

2.FlatButton--平的按钮

FlatButton相当于精简版的RaisedButton,没有阴影凸起效果 可见源码里关于elevation都被过滤了

2.1源码一览:
  const FlatButton({
    Key key,
    @required VoidCallback onPressed,
    ValueChanged<bool> onHighlightChanged,
    ButtonTextTheme textTheme,
    Color textColor,
    Color disabledTextColor,
    Color color,
    Color disabledColor,
    Color highlightColor,
    Color splashColor,
    Brightness colorBrightness,
    EdgeInsetsGeometry padding,
    ShapeBorder shape,
    Clip clipBehavior = Clip.none,
    MaterialTapTargetSize materialTapTargetSize,
    @required Widget child,
复制代码

2.2简单操作

FlatButton.gif

var flatButton = FlatButton(
  onPressed: () {},
  child: Text("Toly"),
  color: Color(0xffF88B0A),
  highlightColor: Colors.blue,
  textColor: Color(0xffFfffff),
);
复制代码

3.OutlineButton--框按钮

OutlineButton是一个框型按钮

3.1源码一览:
 const OutlineButton({
    Key key,
    @required VoidCallback onPressed,
    ButtonTextTheme textTheme,
    Color textColor,
    Color disabledTextColor,
    Color color,
    Color highlightColor,
    Color splashColor,
    double highlightElevation,
    this.borderSide,
    this.disabledBorderColor,
    this.highlightedBorderColor,
    EdgeInsetsGeometry padding,
    ShapeBorder shape,
    Clip clipBehavior = Clip.none,
    Widget child,
复制代码

3.2简单操作

OutlineButton.gif

var outLineButton = OutlineButton(
  onPressed: () {},
  child: Text("Toly"),
  color: Color(0xffF88B0A),
  highlightColor: Colors.blue,
  textColor: Color(0xff000000),
  borderSide: BorderSide(color: Color(0xff0A66F8), width: 2),
);
复制代码
卡片拆包:
- - -

三、第三组控件:

第三组.png

这组效果如下:好像听到:
汽车人变形,然后AppBar说:我来组成头部;TabBarView说:我来组成身体,BottomNavigationBar说:我来组成脚部


1.TabBar--标签Bar

RaisedButton和Android的内置Button基本上是一致的

1.1源码一览:
  const TabBar({
    Key key,
    @required this.tabs,
    this.controller,
    this.isScrollable = false,
    this.indicatorColor,
    this.indicatorWeight = 2.0,
    this.indicatorPadding = EdgeInsets.zero,
    this.indicator,
    this.indicatorSize,
    this.labelColor,
    this.labelStyle,
    this.labelPadding,
    this.unselectedLabelColor,
    this.unselectedLabelStyle,
复制代码

1.2:实现方法

TabBar测试.png

var tabBar = TabBar(
  labelStyle: TextStyle(fontSize: 20),
  labelColor: Color(0xffF64C19),
  unselectedLabelColor: Colors.white,
  tabs: chartLi.map((item) {
    return Container(
      alignment: AlignmentDirectional.center,
      child: Text(item),
      height: 40,
    );
  }).toList(),
);

//注意一点:主页的Scaffold标签要让DefaultTabController包一下,否则会报错
   home: new DefaultTabController(
        child:scaffold, 
        length: 4))

//并且我将tabBar放在了AppBar的下面,这样好看一点(当然你可以随意放)
    var scaffold= Scaffold(
      appBar: AppBar(
          title: Text("张风捷特烈"),
          bottom:tabBar,
          backgroundColor: Color(0xff54C5F8),
          elevation: 12,
          centerTitle: true,
          toolbarOpacity: .4), //透明度
    );
复制代码

2.TabBarView
2.1源码一览:
  const TabBarView({
    Key key,
    @required this.children,
    this.controller,
    this.physics,
复制代码

2.2简单操作
var chartLi = ["About", "Ball", "Card", "Dog"];

var tabBarView = new TabBarView(
  children: chartLi.map((text) {
    return new Center(
        child: new Text(text, style: TextStyle(fontSize: 20),
    ));
  }).toList(),
);
复制代码

3.BottomNavigationBar--底部Bar

OutlineButton是一个框型按钮

3.1源码一览:
  BottomNavigationBar({
    Key key,
    @required this.items,
    this.onTap,
    this.currentIndex = 0,
    BottomNavigationBarType type,
    this.fixedColor,
    this.iconSize = 24.0,
复制代码

3.2简单操作

底栏.png

var iconInfoMap = {
  "首页": Icon(Icons.home),
  "消息": Icon(Icons.comment),
  "动态": Icon(Icons.toys),
  "联系人": Icon(Icons.contacts),
};

var bottomNavigationBar = BottomNavigationBar(
  items: () {
    var items = <BottomNavigationBarItem>[];
    iconInfoMap.forEach((k, v) {
      items.add(BottomNavigationBarItem(
          title: Text(k), icon: v, backgroundColor: Color(0xff49B1FB)));
    });
    return items;
  }(),
  currentIndex: 1,
  onTap: (position) {
    print(position);
  },
);

//由脚手架将汽车人组合
    var scaffold= Scaffold(
      appBar: AppBar(title: Text("张风捷特烈"),
          bottom:tabBar), 
      body: tabBarView,
      bottomNavigationBar: bottomNavigationBar,
    );
复制代码
- - -

第四组:这组有点坑

第四组.png


1.Drawer:我来组成左臂
1.1源码一览:
  const Drawer({
    Key key,
    this.elevation = 16.0,
    this.child,
    this.semanticLabel,
复制代码

1.2:实现方法

drawer.gif

var draw = Drawer(
    elevation: 5,
    child: Container(
      alignment: AlignmentDirectional.center,
      color: Color(0xff99C6F9),
      child: Text(
        "张风捷特烈",
        style: TextStyle(fontSize: 30),
      ),
    ));


//Scaffold组合
var scaffold= Scaffold(
  appBar: AppBar(title: Text("张风捷特烈"),
      bottom:tabBar), 
  body: tabBarView,
  drawer: draw,
  bottomNavigationBar: bottomNavigationBar,
);
复制代码

2.SnackBar
2.1源码一览:
  const SnackBar({
    Key key,
    @required this.content,
    this.backgroundColor,
    this.action,
    this.duration = _kSnackBarDisplayDuration,
    this.animation,
复制代码

2.2简单操作

SnackBar.gif

var snackBar = SnackBar(
    backgroundColor: Color(0xffFB6431),
    content: Text('Hello!'),
    duration: Duration(seconds: 1),
    action: SnackBarAction(
        label: '确定',
        onPressed: () {
          print("张风捷特烈");
        }));

//坑点来了,笔记记好---------------
//一开始打开总是报错,貌似是context的锅,百度了一下,已经有人填坑了,
//需要Scaffold的context,而不是我认为的那个context

  var scContext;//先声明一下Scaffold的context
  
  @override
  Widget build(BuildContext context) {
    var scaffold = Scaffold(
      appBar: AppBar(//同前,略...),
      //Scaffold的context通过Builder来获取
      body: Builder(builder: (context) {
        scContext = context;
        return tabBarView;
      }),
      drawer: draw,
      bottomNavigationBar: bottomNavigationBar,

      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Scaffold.of(scContext).showSnackBar(snackBar);//这样就行了
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
复制代码

3.BottomSheet
3.1源码一览:
  const BottomSheet({
    Key key,
    this.animationController,
    this.enableDrag = true,
    this.elevation = 0.0,
    @required this.onClosing,
    @required this.builder
复制代码

3.2简单操作

BottomSheet.gif

var bottomSheet = BottomSheet(
    onClosing: () {},
    builder: (context) => (Container(
        color: Color(0xffABF5E0),
        child:Wrap(
          children: <Widget>[
            Center(child: Text('绝域从军计惘然,')),
            Center(child: Text('东南幽恨满词笺。')),
            Center(child: Text('一箫一剑平生意,')),
            Center(child: Text('负尽狂名十五年。')),
          ],
        ))));

//点击打开BottomSheet
floatingActionButton: FloatingActionButton(
  onPressed: () {
    Scaffold.of(scContext).showBottomSheet(bottomSheet.builder);
  },
  tooltip: 'Increment',
  child: Icon(Icons.add),
),
复制代码
- - -

五、第五组

第五组.png


1.TextField:

Flutter版EditText

1.1源码一览:
  const TextField({
    Key key,
    this.controller,----TextEditingController
    this.focusNode,
    this.decoration = const InputDecoration(),
    TextInputType keyboardType,
    this.textInputAction,
    this.textCapitalization = TextCapitalization.none,
    this.style,
    this.textAlign = TextAlign.start,
    this.textDirection,
    this.autofocus = false,
    this.obscureText = false,
    this.autocorrect = true,
    this.maxLines = 1,
    this.maxLength,
    this.maxLengthEnforced = true,
    this.onChanged,
    this.onEditingComplete,
    this.onSubmitted,
    this.inputFormatters,
    this.enabled,
    this.cursorWidth = 2.0,
    this.cursorRadius,
    this.cursorColor,
    this.keyboardAppearance,
    this.scrollPadding = const EdgeInsets.all(20.0),
    this.enableInteractiveSelection = true,
    this.onTap,
复制代码

1.2:实现方法

TextField测试.png

var textField = TextField(
  keyboardType: TextInputType.number,
  textAlign: TextAlign.center,
  maxLines: 1,
  cursorColor: Colors.black,
  cursorWidth: 10,
  style: TextStyle(fontSize: 20, color: Colors.lightBlue),
  onChanged: (str) {
    print(str);
  },
  onEditingComplete: () {
    print("onEditingComplete");
  },
  onSubmitted: (str) {
    print("onSubmitted:" + str);
  },
  onTap: () {
    print("onTap");

  },
);
复制代码

2.Checkbox:

Flutter版CheckBox

2.1源码一览:
  const Checkbox({
    Key key,
    @required this.value,
    this.tristate = false,
    @required this.onChanged,
    this.activeColor,
    this.materialTapTargetSize,
复制代码

2.2:实现方法

Checkbox.png

var checkbox = Checkbox(
  value: true,
  activeColor: Colors.blue,
  onChanged: (value) {
    print(value);
  },
);
复制代码

3.Slider:
3.1源码一览:
  const Slider({
    @required this.value,
    @required this.onChanged,
    this.onChangeStart,
    this.onChangeEnd,
    this.min = 0.0,
    this.max = 1.0,
    this.divisions,
    this.label,
    this.activeColor,
    this.inactiveColor,
    this.semanticFormatterCallback,
复制代码

3.2:实现方法

Slider.png

var slider = Slider(
  min: 100,
  max: 200,
  value: 180,
  activeColor: Colors.green,
  inactiveColor: Colors.grey,
  onChanged: (value) {
    print(value);
  },
  onChangeStart: (v) {},
  onChangeEnd: (v) {},
);
复制代码
- - -

六、第六组

第六组.png


1.Switch:
1.1源码一览:
  const Switch({
    Key key,
    @required this.value,
    @required this.onChanged,
    this.activeColor,
    this.activeTrackColor,
    this.inactiveThumbColor,
    this.inactiveTrackColor,
    this.activeThumbImage,
    this.inactiveThumbImage,
    this.materialTapTargetSize,
复制代码

1.2:实现方法

Switch.png

var switch_ = Switch(
  value: true,
  activeColor: Colors.greenAccent,
  activeTrackColor: Colors.black,
  activeThumbImage: AssetImage("images/icon_90.png"),
  onChanged: (bool value) {
    print(value);
  },
);
复制代码

2.Radio:
2.1源码一览:
  const Radio({
    Key key,
    @required this.value,
    @required this.groupValue,
    @required this.onChanged,
    this.activeColor,
    this.materialTapTargetSize,
复制代码

2.2:实现方法

Radio测试.png

var numLi = [1, 2, 3, 4, 5, 6, 7];
var radios=Wrap(children: numLi.map((i) {
  return Radio<int>(value: i, groupValue: 5, onChanged: (int value) {},);
}).toList());
复制代码

3.Chip + CircleAvatar:

Chip + CircleAvatar.png

var chip = Chip(
  backgroundColor: Color(0xffE5E5E5),
  padding: EdgeInsets.all(3),
  avatar: CircleAvatar(
      backgroundColor: Colors.lightBlue.shade400,
      child: new Text(
        'Toly',
        style: TextStyle(fontSize: 10.0, color: Colors.white),
      )),
  label: Text('张风捷特烈'),
);
复制代码

- - -

Flutter的控件好多啊,常用的差不多也就这样吧


七、Flex布局详解

Flex是什么?是RowColumn的老爸,现在先忘掉RowColumn
等你认清Flex怎么玩的,RowColumn 也就清楚了

1.先看Flex的的属性

可以看出direction是必须的,类型和枚举都在下面列出了
有必要普及几个单词:mainAxis(主轴) Alignment对齐 CrossAxis主轴的交错轴
什么是主轴:direction的方向为主轴,与主轴垂直方向为交错轴

  Flex({
    Key key,
    @required this.direction,
    ----Axis.|----horizontal,
             |----vertical,
             
    this.mainAxisAlignment = MainAxisAlignment.start,
    ----MainAxisAlignment.|----start,
                          |----end,
                          |----center,
                          |----spaceBetween,
                          |----spaceAround,
                          |----spaceEvenly,   
    
    this.mainAxisSize = MainAxisSize.max,
    ----MainAxisSize.|----max
                     |----min 
                     
    this.crossAxisAlignment = CrossAxisAlignment.center,
    ----CrossAxisAlignment.|----start,
                           |----end,
                           |----center,
                           |----stretch,
                           |----baseline,
    
    this.textDirection,
    this.verticalDirection = VerticalDirection.down,
    this.textBaseline,
    List<Widget> children = const <Widget>[],
复制代码

2.布局测试1:水平默认状态

水平默认状态.png

var c1 = Container(width: 50, height: 50, color: Colors.blue);
var c2 = Container(width: 50, height: 80, color: Colors.red);
var c3 = Container(width: 150, height: 50, color: Colors.yellow);

var flex_test = Flex(
  direction: Axis.horizontal,
  children: <Widget>[c1, c2, c3],
);
复制代码

3.水平时主轴的布局行为:默认:MainAxisAlignment.start

控制属性:mainAxisAlignment

水平时主轴的布局行为.png


4.水平时交错轴(纵轴)的布局行为:默认:CrossAxisAlignment.center

水平时交错轴的布局行为.png


5.Flex盒主轴尺寸:mainAxisSize--默认:MainAxisSize.max

就两个值有啥好怕的,max已经测试完了,就剩一个min了
这min更简单,主轴方向的Flex盒就等于内容尺寸,而不是外部容器
这就意味着主轴的布局行为无效,也就像warp_content

如果是主轴水平向的,主轴行为就在水平方向进行,也就是:Row 如果是主轴纵向的,主轴行为就在竖直方向进行,也就是:Column

6.Expanded与Flex的搭配

Expanded,它能与Flex布局进行沟通,来让孩子尺寸变更

我量了一下,如果同时Expanded--c2和c3,最终c2和c3的长度是一样的
如果同时Expanded--c1,c2和c3,最终c1,c2,c3长度都是一样的
复制代码

expaned.png

好了,今天就是这样,基本上也就这么多,明天找几个经典布局练练手


后记:捷文规范

1.本文成长记录及勘误表
项目源码 日期 备注
V0.1-github 2018-12-19 Flutter第4天--基础控件(下)+Flex布局详解
2.更多关于我
笔名 QQ 微信 爱好
张风捷特烈 1981462002 zdl1994328 语言
我的github 我的简书 我的掘金 个人网站
3.声明

1----本文由张风捷特烈原创,转载请注明
2----欢迎广大编程爱好者共同交流
3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正
4----看到这里,我在此感谢你的喜欢与支持


icon_wx_200.png

关注下面的标签,发现更多相似文章
评论