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

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

零、前言

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


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


一、第一组控件:


1.图片Image


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的各种情况

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

//循环生成Image控件
formImgs() {
  var imgLi = [];
  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 = [];
  colorBlendMode.forEach((mode) {
    imgLi.add(Column(children: [
      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也好,套路都是一样的
当你遇到很多种情况的问题时,都可以用这个套路,多分析,你才能巩固自己的知识库

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


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简单操作

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 [],
复制代码

3.2简单操作

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

二、第二组控件:

这一组都继承自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 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简单操作

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 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简单操作

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简单操作

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

三、第三组控件:

这组效果如下:好像听到:
汽车人变形,然后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:实现方法

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简单操作

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

var bottomNavigationBar = BottomNavigationBar(
  items: () {
    var items = [];
    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,
    );
复制代码
- - -

第四组:这组有点坑


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

1.2:实现方法

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简单操作

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简单操作

var bottomSheet = BottomSheet(
    onClosing: () {},
    builder: (context) => (Container(
        color: Color(0xffABF5E0),
        child:Wrap(
          children: [
            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),
),
复制代码
- - -

五、第五组


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:实现方法

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:实现方法

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:实现方法

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

六、第六组


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:实现方法

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:实现方法

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

3.Chip + CircleAvatar:

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 children = const [],
复制代码

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

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: [c1, c2, c3],
);
复制代码

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

控制属性:mainAxisAlignment


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


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长度都是一样的
复制代码

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


后记:捷文规范

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

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


你可能感兴趣的:(Flutter第4天--基础控件(下)+Flex布局详解)