赞
踩
目录
adding,margin和decoration内外边距和背景边框
Flutter万物皆组件(Widget)
前言
widget
StatelessWidget和StatefulWidget是flutter的基础组件,日常开发中自定义Widget都是选择继承这两者之一(并不是只有这两种)。两者的区别在于状态的改变,两种widget都是继承自Widget类。
- StatelessWidget面向那些始终不变的UI控件,比如标题栏中的标题, 常见的有Container、ScrollView等;
- 而StatefulWidget则是面向可能会改变UI状态的控件,比如有点击反馈的按钮,常见的有CheckBox、AppBar、TabBar等。
- 一个StatelessWidget可以用多个不同的BuildContext构建,而一个StatefulWidget会为每个BuildContext创建一个State对象。
StatefulWidget的创建需要指定一个State,在需要更新UI的时候调用setState(VoidCallback fn),并在VoidCallback中改变一些变量数值等,组件会重新build以达到刷新状态也就是刷新UI的效果。
State
作用
在widget构建的时候可以被同步读取
在widget的生命周期中可能会被改变生命周期
created:当State对象被创建时候,State.initState方法会被调用;
initialized:当State对象被创建,但还没有准备构建时,State.didChangeDependencies在这个时候会被调用;
ready:State对象已经准备好了构建,State.dispose没有被调用的时候;
defunct:State.dispose被调用后,State对象不能够被构建
MaterialApp
表示一个应用了 Material 界面风格的应用程序,它封装了应用程序实现 Material Design 所需要的一些widget,大多数项目的界面都应该基于
MaterialApp
进行呈现。
import 'package:flutter/material.dart'; void main () => runApp(MyApp()); class MyApp extends StatelessWidget{ @override Widget build(BuildContext context ){ return MaterialApp( // 指定应用程序在任务栏上显示的标题 title:'Text widget', // 指定应用程序的主界面 home:Scaffold( body:Center( child:Text('Hello JSPang') ), // 配置应用程序的主题 theme: ThemeData(primarySwatch: Colors.red), ), ); } }Scaffold脚手架容器组件
该组件是页面结构的脚手架,包含了页面的基本组成单元,例如:
appBar【头部导航条区域】
body【页面主题内容区域】
drawer【侧边栏抽屉区域】
bottomNavigationBar【底部tabBar区域】
floatingActionButton【右下角浮动按钮区域】
Scaffold( // 脚手架 appBar: AppBar(// 导航栏 title: Text('页面标题'), ), body: Center( child: Text('主体内容'), ), floatingActionButton: FloatingActionButton( onPressed: () {}, child: Icon(Icons.add), ), drawer: Drawer(), bottomNavigationBar: BottomNavigationBar(// 底部导航栏 items: [ BottomNavigationBarItem( icon: Icon(Icons.home), ), BottomNavigationBarItem( icon: Icon(Icons.help_center), ), ], currentIndex: _selectedIndex, fixedColor: Colors.lightBlue, onTap: (index) { print("selected: $index"); setState(() { _selectedIndex = index; }); }, ), ), // 主题颜色 theme: ThemeData(primarySwatch: Colors.red), )基础组件
文本组件Text Widget
import 'package:flutter/material.dart'; void main () => runApp(MyApp()); class MyApp extends StatelessWidget{ @override Widget build(BuildContext context ){ return MaterialApp( title:'Text widget', home:Scaffold( body:Center( child:Text('Hello JSPang') ), ), ); } }child里的属性
textAlign:文本的对齐方式
- center: 文本以居中形式对齐,这个也算比较常用的了。
- left:左对齐,经常使用,让文本居左进行对齐,效果和start一样。
- right :右对齐,使用频率也不算高。
- start:以开始位置进行对齐,类似于左对齐。
- end: 以为本结尾处进行对齐,不常用。有点类似右对齐.
child:Text( 'Hello', textAlign:TextAlign.left, )maxLines:设置最多显示的行数,溢出部分默认不显示,可以通过overflow设置
'Hello', textAlign:TextAlign.left, maxLines: 1,overflow:溢出文字显示方式
- clip:直接切断,剩下的文字就没有了,不太友好。
- ellipsis:在后边显示省略号,体验性较好,常用。
- fade: 溢出的部分会进行一个渐变消失的效果,上线的渐变,不是左右。
'Hello', textAlign:TextAlign.left, overflow:TextOverflow.ellipsis, maxLines: 1,style属性的内容比较多可参考api
TextStyle里的属性:
- fontSize:文字大小
- color:设置文字颜色,一般用color:color.fromARGB()
- decoration: TextDecoration.underline,//下划线,也有其他的
- decorationStyle: TextDecorationStyle.solid,//实线
效果:字体大小为25.0,颜色为粉红色,并且有一个下划线。
'Hello', textAlign:TextAlign.left, overflow:TextOverflow.ellipsis, maxLines: 1, style: TextStyle( fontSize:25.0, color:Color.fromARGB(255, 255, 150, 150), decoration:TextDecoration.underline, decorationStyle:TextDecorationStyle.solid, ),汇总
import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart';//一般要引入这俩文件 void main() => runApp(MyApp());//dart语法,箭头函数,单表达式返回语句简写 class MyApp extends StatelessWidget {//继承这个类是固定的 @override//覆写父类的Build方法 Widget build(BuildContext context) {//这里下面写的东西都是组件 return MaterialApp(//返回这个组件 title: "Text Widget", home: Scaffold(//home为窗口本体,scaffold本意为脚手架,其实就是可以搭建里边的内容 body: Center(//内容在屏幕中间,Center也是一个组件 child: Text(//Text组件 "Your life is up to you.", textAlign: TextAlign.left,//文字对齐方式,还有center和right maxLines: 2,//最大行数,超出的部分见下行overflow overflow: TextOverflow.ellipsis,//ellipsis意思是溢出的文字会以省略号显示 style: TextStyle( fontSize: 30.0,//字体大小,注意一定要加.0 color: Color.fromARGB(255, 255, 105, 145),//第一个参数是透明度,后面就是RGB颜色 ), ), ), ), ); } }手势组件GestureDetector
GestureDetector( child: Text("Text"), onTapUp: () { print("onTapUp"); }, onTapDown: () { print("onTapDown"); }, onTapCancel: () { print("onTapCancel"); }, onTap: () { print("onTap"); }, onDoubleTap: () { print("onDoubleTap"); }, onLongPress: () { print("onLongPress"); }, )按钮组件
Flutter开发六、MaterialButton风格按钮以及按钮自适应
图片组件Image
- Image.asset:加载资源图片,就是加载项目资源目录中的图片,会增大打包的包体体积,用的是相对路径。
- Image.network:网络资源图片。
- Image.file:加载本地图片,就是加载本地文件中的图片,这个是一个绝对路径,跟包体无关。
- Image.memory: 加载Uint8List资源图片。
child:Container( child: Image.network( 'http://static/myimg/blogtouxiang.jpg', scale:1.0, // fit: BoxFit.fitWidth,//图片在容器中的填充方式 color: Colors.pink[50],//需要混合在图片上的颜色 colorBlendMode:BlendMode.colorBurn,//图片以何种模式与颜色混合 repeat: ImageRepeat.repeatY,//图片重复,以中心Y轴纵向重复 ), width:300.0, height: 700.0, color: Colors.pink[100],//容器的颜色,我这里把尺寸设置的超过了屏幕,皆为美观 ),child里的属性
fit控制图片拉伸和挤压
BoxFit.fill:全图显示,图片会被拉伸,并充满父容器。
BoxFit.contain:全图显示,显示原比例,可能会有空隙。
BoxFit.cover:显示可能拉伸,可能裁切,充满(图片要充满整个容器,还不变形)。
BoxFit.fitWidth:宽度充满(横向充满),显示可能拉伸,可能裁切。
BoxFit.fitHeight :高度充满(竖向充满),显示可能拉伸,可能裁切。
BoxFit.scaleDown:效果和contain差不多,但是此属性不允许显示超过源图片大小,可小不可大。
child: Image.network( 'http://static/myimg/blogtouxiang.jpg', scale:1.0, fit: BoxFit.fill, ),colorBlendMode图片混合模式能让图片改变颜色
- color:是要混合的颜色,如果你只设置color是没有意义的。
- colorBlendMode:是混合模式,相当于我们如何混合。
child: Image.network( 'http://static/myimg/blogtouxiang.jpg', color: Colors.greenAccent, colorBlendMode: BlendMode.darken, ),repeat图片重复铺满
ImageRepeat.repeat : 横向和纵向都进行重复,直到铺满整个画布。
ImageRepeat.repeatX: 横向重复,纵向不重复。
ImageRepeat.repeatY:纵向重复,横向不重复。
child: Image.network( 'http://static/myimg/blogtouxiang.jpg', repeat: ImageRepeat.repeat, ),CircleAvatar圆形头像
提供圆形的用户头像区域,使用起来比较简单,示例代码如下:
CircleAvatar( backgroundImage: NetworkImage( 'https://images.gitee.com/uploads/91/465191_vsdeveloper.png'), )单选框和复选框
Checkbox
Checkbox( value: isSelected, activeColor: Colors.red, onChanged: (value) { print(value); }, )单选开关
Switch( value: isSelected, activeColor: Colors.red, onChanged: (value){ print(value); },)裁剪组件
Clip的相关组件:
ClipOval:圆形裁剪
ClipRRect:圆角矩形裁剪
ClipRect:矩形裁剪
ClipPath:路径裁剪
ClipOval( child: SizeBox( width: 100, height: 100, child: Image.network("http://xxxxx.png")), )透明度组件opacity
Opacity( opacity: 0.5, child: Text("Text"), )Visibility控制子组件是否可见的组件
Visibility( visible: false,// 子组件是否可见,默认true child: Text("Text"),// 子组件 replacement: Text("data"),// 不可见时显示的组件(当maintainState=false) maintainState: true,//不可见时是否维持状态,默认为false )容器组件
脚手架-文章开头
Container容器
是一个组合类的容器组件,不参与最终的渲染
Container(容器控件)在Flutter是经常使用的控件,它就相当于我们HTML里的
<div>
标签,每个页面或者说每个视图都离不开它
class MyApp extends StatelessWidget{ @override Widget build(BuildContext context ){ return MaterialApp( title:'Text widget', home:Scaffold( body:Center( child:Container( ), ), ), ); } }child里的属性
alignment属性:容器子内容的对齐方式
bottomCenter
:下部居中对齐。botomLeft
: 下部左对齐。bottomRight
:下部右对齐。center
:纵横双向居中对齐。centerLeft
:纵向居中横向居左对齐。centerRight
:纵向居中横向居右对齐。topLeft
:顶部左侧对齐。topCenter
:顶部居中对齐。topRight
: 顶部居左对齐
child:Container( child: Text('Hello',style: TextStyle(fontSize: 40.0),), alignment: Alignment.center, ),设置宽、高和颜色属性
width:设置为double.infinity可以强制在宽度上撑满
height:设置为double.infinity可以强制在高度上撑满。
child:Container( child: Text('Hello',style: TextStyle(fontSize: 40.0),), alignment: Alignment.center, width:500.0, height:400.0, color: Colors.lightBlue, ),adding,margin和decoration内外边距和背景边框
padding内边距为10,EdgeInsets.fromLTRB(value1,value2,value3,value4)分别代表左、上、右、下
height:400.0, color: Colors.lightBlue, padding:const EdgeInsets.all(10.0),margin外边距为10,分开设置也是fromLTRB
margin: const EdgeInsets.all(10.0),
decoration设置背景和边框
背景加入一个渐变(设置了decoration,不要再设置color属性了)
decoration: BoxDecoration( gradient:const LinearGradient( colors:[Colors.lightBlue,Colors.greenAccent,Colors.purple] ) ),红色边框
decoration: BoxDecoration( gradient:const LinearGradient( colors:[Colors.lightBlue,Colors.greenAccent,Colors.purple] ), border:Border.all(width:2.0,color:Colors.red) ),SizeBox装饰类容器
可以设置具体尺寸的控件,child不为null时,如果设置宽高,则强制显示宽高,如果没有设置,则自适应;如果child为null时,可当做间隔使用
SizedBox( height: 16, child: Text("Text"), )DecoratedBox装饰类容器
可以在其子组件绘制前(或者绘制后)绘制一些装饰,如背景、边框、渐变等
DecoratedBox( decoration: BoxDecoration( color: Colors.white,// 背景色 borderRadius: BorderRadius.all(Radius.circular(2.0)),// 圆角 border: Border.all(color: Colors.pink, width: 1),// 边框 gradient: LinearGradient(colors: [Colors.red, Colors.orange[700]]),// 渐变 boxShadow: [// 阴影 BoxShadow( color: Colors.black12, offset: Offset(5.0, 5.0), blurRadius: 4.0) ], ), child: Text("Text"), )SafeArea安全区域
留出刘海和底部的安全区域,很好的解决刘海屏兼容问题
SafeArea(child: Text("Text"))
可滚动组件
列表组件ListView
- 使用ListView,然后在他的内部
children
中,使用了widget
数组,因为是一个列表,所以它接受一个数组- ListView是最常用的可滚动组件之一,它可以沿一个方向线性排布所有子组件,并且它也支持基于Sliver的延迟构建模型
示例
body: ListView( children:<Widget>[ ListTile( leading: Icon(Icons.access_time), title: Text('hello') ), ListTile( leading: Icon(Icons.access_time), title: Text('hello') ), ] ),
图片列表
children:<Widget>[ new Image.network( 'https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3289342822,655864970&fm=26&gp=0.jpg' ), new Image.network( 'https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3289342822,655864970&fm=26&gp=0.jpg' ), ListTile( leading: Icon(Icons.access_time), title: Text('access_time') ), ]
ScrollDirection横向列表
- Axis.horizontal:横向滚动或者叫水平方向滚动。
- Axis.vertical:纵向滚动或者叫垂直方向滚动。
- 不设置垂直滚动
body: ListView( scrollDirection: Axis.horizontal, children:<Widget>[ new Image.network( 'https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3289342822,655864970&fm=26&gp=0.jpg', width: 200, height: 200, ), new Image.network( 'https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3289342822,655864970&fm=26&gp=0.jpg', width: 200, height: 200, ), ] ),
保持列表项的数据状态
多个列表页之间的动态切换,保持每个列表项的滚动距离、数据状态等信息,可以实现 AutomaticKeepAliveClientMixin特征,来保持列表项的滚动状态
class _MovieListState extends State<MovieList> with AutomaticKeepAliveClientMixin { // 重写 wantKeepAlive 函数 @override bool get wantKeepAlive => true; }监听列表是否滚动到页面底部
// 定义私有变量 _scrollCtrl ScrollController _scrollCtrl; // 重写 initState 生命周期函数 @override void initState() { // 此行为默认代码,不能删除 super.initState(); // 初始化一个 ScrollController 滚动控制器 _scrollCtrl = new ScrollController(); // 为 _scrollCtrl 滚动控制器添加监听事件 _scrollCtrl.addListener(() { // _scrollCtrl.position.pixels 当前列表滚动的距离 // _scrollCtrl.position.maxScrollExtent 列表的最大滚动距离 if (_scrollCtrl.position.pixels == _scrollCtrl.position.maxScrollExtent) { // 调用 setState 函数,让页码值 +1 setState(() { _page++; }); // 获取新页面的数据 _getMovieList(); } }); }添加/清理 controller 滚动控制器
ListView.builder( controller: _scrollCtrl, itemCount: _mlist.length, itemBuilder: (BuildContext ctx, int i) {} ) ... // 重新 dispose 函数 @override void dispose() { super.dispose(); // 主动销毁滚动控制器 _scrollCtrl.dispose(); }嵌套组件分离
声明一个List的类
import 'package:flutter/material.dart'; class List extends StatelessWidget{ @override Widget build(BuildContext context){ return ListView( scrollDirection: Axis.horizontal, children: <Widget>[ new Container( width:180.0, color: Colors.lightBlue, ), new Container( width:180.0, color: Colors.amber, ), new Container( width:180.0, color: Colors.deepOrange, ),new Container( width:180.0, color: Colors.deepPurpleAccent, ), ], ); } }在MyAPP类里直接使用这个类
import 'package:flutter/material.dart'; import 'package:untitled/List.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context ){ return MaterialApp( title:'ListView widget', home:Scaffold( body:Center( child:Container( height:200.0, child: List() ), ), ), ); } }
动态加载列表
List集合
List是Dart的集合类型之一
var myList = List()
: 非固定长度的声明。var myList = List(2)
: 固定长度的声明。var myList= List<String>()
:固定类型的声明方式。var myList = [1,2,3]
: 对List直接赋值。List传递和接收
传递
void main() { runApp(MyApp( items: List<String>.generate(1000, (i)=> "Item $i") )); }MyApp类接收
这是一个构造函数,除了Key,增加了一个必传参数,
@required
意思必传。:super
如果父类没有无名无参数的默认构造函数,则子类必须手动调用一个父类构造函数。
final List<String> items; MyApp({Key key, @required this.items}):super(key:key);动态列表ListView.builder()
import 'package:flutter/material.dart'; void main() { runApp(MyApp( items: List<String>.generate(1000, (i)=> "Item $i") )); } class MyApp extends StatelessWidget { final List<String> items; MyApp({Key key, @required this.items}):super(key:key); @override Widget build(BuildContext context ){ print(items); return MaterialApp( title:'ListView widget', home:Scaffold( body: ListView.builder( itemCount:items.length, itemBuilder:(context,index){ return ListTile( title: Text('${items[index]}'), ); } ) ), ); } }运行main方法
网格视图GridView
GridView,顾名思义就是我们用来做网格的,通常我们用来做相册,或者网格状的展示栏
- padding:表示内边距,这个小伙伴们应该很熟悉。
- crossAxisSpacing:网格间的空当,相当于每个网格之间的间距。
- crossAxisCount:网格的列数,相当于一行放置的网格数量。
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context ){ return MaterialApp( title:'ListView widget', home:Scaffold( body: GridView.count( padding:const EdgeInsets.all(20.0), crossAxisSpacing: 10.0, crossAxisCount: 3, children: <Widget>[ Image.network( 'https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3289342822,655864970&fm=26&gp=0.jpg', width: 200, height: 200, ), Image.network( 'https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3289342822,655864970&fm=26&gp=0.jpg', width: 200, height: 200, ), Image.network( 'https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3289342822,655864970&fm=26&gp=0.jpg', width: 200, height: 200, ), const Text('我喜欢玩游戏'), const Text('我喜欢看书'), const Text('我喜欢吃火锅') ], ) ), ); } }
- childAspectRatio:宽高比,这个值的意思是宽是高的多少倍,如果宽是高的2倍,那我们就写2.0,如果高是宽的2倍,我们就写0.5。希望小伙伴们理解一下。
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context ){ return MaterialApp( title:'ListView widget', home:Scaffold( body:GridView( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3,//一行放几幅图片 mainAxisSpacing: 2.0,//行距 crossAxisSpacing: 2.0,//列距 childAspectRatio: 0.7,//图片宽高比,因为海报一般长:宽是4:3嘛所以我搞的是这样 ), children: <Widget>[ Image.network('http://img5.mtime.cn/mt/2018/10/22/104316.77318635_180X260X4.jpg',fit: BoxFit.cover), Image.network('http://img5.mtime.cn/mt/2018/10/10/112514.30587089_180X260X4.jpg',fit: BoxFit.cover), Image.network('http://img5.mtime.cn/mt/2018/11/13/093605.61422332_180X260X4.jpg',fit: BoxFit.cover), Image.network('http://img5.mtime.cn/mt/2018/11/07/092515.55805319_180X260X4.jpg',fit: BoxFit.cover), Image.network('http://img5.mtime.cn/mt/2018/11/21/090246.16772408_135X190X4.jpg',fit: BoxFit.cover), Image.network('http://img5.mtime.cn/mt/2018/11/17/162028.94879602_135X190X4.jpg',fit: BoxFit.cover), Image.network('http://img5.mtime.cn/mt/2018/11/19/165350.52237320_135X190X4.jpg',fit: BoxFit.cover), Image.network('http://img5.mtime.cn/mt/2018/11/16/115256.24365160_180X260X4.jpg',fit: BoxFit.cover), Image.network('http://img5.mtime.cn/mt/2018/11/20/141608.71613590_135X190X4.jpg',fit: BoxFit.cover), ], ) ), ); } }
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'roadkiller', home: Scaffold( appBar: new AppBar( title: new Text('roadkiller'), ), body:GridView( padding: new EdgeInsets.all(3.0),//学过前端好理解吧 gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount:2,//一行放几幅图片 mainAxisSpacing: 2.0,//行距 crossAxisSpacing: 2.0,//列距 childAspectRatio: 0.75//图片宽高比,因为海报一般长:宽是4:3嘛所以我搞的是这样 ), children: <Widget>[ new Image.network('https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=3246509238,1077330305&fm=58&s=DFD513C6886286D432620FBC0300301F'), new Image.network('https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=2512763846,3067531314&fm=58&s=9102AEFBD3A7E4EE4437527303008074'), new Image.network('https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2585813656,2994285865&fm=58&s=C5649A468F378ECC266911AC03008012'), new Image.network('https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=3351456143,3085413485&fm=58&s=0CC0F804845BBFCE320951990300C082'), new Image.network('https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=581555814,2183385336&fm=58&s=2740D94FDE3283DC1259A91301008092'), new Image.network('https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=3527165871,1016449403&fm=58&s=787B20C402B38BC456651C8D0300E088'), new Image.network('https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=2801239214,2575519245&fm=58'), new Image.network('https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=3895436776,1493313546&fm=58&s=C115C730589647FF5E89F0C5030070A1'), new Image.network('https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=3343253172,1276100043&fm=58&s=9DB5109B1C1247F56898AFCA03005037'), new Image.network('https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=1588364103,2036988885&fm=58&s=DFDDA844965203D4CFA4599303008099'), ], ) ), ); } }
布局组件
上面已讲到容器组件Container,布局出来child:Container()盒子还有Row和Column
Row横向布局
水平方向排布组件,不会自动换行
children【子元素】
mainAxisAlignment【横向对其方式】
crossAxisAlignment【纵向对其方式】
Row( mainAxisAlignment: MainAxisAlignment.start,// 主轴对齐方式 crossAxisAlignment: CrossAxisAlignment.start,// 交叉轴对齐方式 children: [ Text("Text1"), Text("Text2"), Text("Text3"), Text("Text4"), ], )Column纵向布局
垂直方向排布组件,不会自动换行
children【子元素】
mainAxisAlignment【纵向对其方式】
crossAxisAlignment【横向对其方式】
Column( children: [ Text("Text1"), Text("Text2"), Text("Text3"), Text("Text4"), ], )要占满整个竖直方向的屏幕,加Expanded
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget{ @override Widget build(BuildContext context) { return MaterialApp( title: 'Column Widget', home: Scaffold( appBar: new AppBar( title: new Text('垂直方向布局'), ), body: Center( child:new Column( mainAxisAlignment: MainAxisAlignment.center,//主轴是相对于你的布局方向来说的,如果你是水平布局那水平轴就是主轴,如果你是垂直布局那竖直轴就是主轴 crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ new RaisedButton( onPressed: (){},//这个是如果按了按钮会进行的操作,我们现在还没有就先不写 color: Colors.lightBlue, child: new Text('blue button'), ), new RaisedButton( onPressed: (){}, color: Colors.pink[200], child: new Text('pink button'), ), new RaisedButton( onPressed: (){}, color: Colors.grey, child: new Text('grey button'), ) ], ), ) ), ); } }
水平布局:Expanded
- 可以使Row、Column、Flex等,子组件在其主轴上展开并填充可用空间,撑开父组件。必须使用在Row、Column、Flex等
- 主要用来控制 flex 布局的占位宽度。需要用在
Row
或Column
子组件内部。
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget{ @override Widget build(BuildContext context) { return MaterialApp( title: 'Row Widget', home: Scaffold( appBar: new AppBar( title: new Text('垂直方向布局'), ), body: new Column( children: <Widget>[ Expanded( child:new RaisedButton( onPressed: (){},//这个是如果按了按钮会进行的操作,我们现在还没有就先不写 color: Colors.lightBlue, child: new Text('blue button'), ) ), Expanded( child:new RaisedButton( onPressed: (){}, color: Colors.pink[200], child: new Text('pink button'), ) ), Expanded( child:new RaisedButton( onPressed: (){}, color: Colors.orange, child: new Text('orange button'), ) ), ], ), ), ); } }
弹性布局Flex
如h5当中的弹性盒子布局,Row和Column都继承自Flex,参数也基本一致所以可以使用Row和Column来代替Flex
Flex( direction: Axis.horizontal, children: <Widget>[ Text("Text1"), Text("Text2"), Text("Text3"), Text("Text4"), ], )流式布局Wrap、Flow
Wrap和Row类似,如果一行控件不够会自动换行
Flow和Column类似,如果一列控件不够会自动换行
Wrap( spacing: 8.0, // 主轴(水平)方向间距 runSpacing: 8.0, // 纵轴(垂直)方向间距 alignment: WrapAlignment.center, // 沿主轴方向居中 children: <Widget>[ Text("Text1"), Text("Text2"), Text("Text3"), Text("Text4"), ], )层叠布局Stack、positioned
- 类似绝对定位,往往与Positioned联合使用,子组件可以根据父容器四个角的位置来确定自身的位置。绝对定位允许子组件堆叠起来(按照代码中声明的顺序)。
- 想要在图片上面层叠放一些字或者Container,就需要采用层叠布局,Stack
- 注意alignment里的FractionalOffset的使用
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget{ @override Widget build(BuildContext context) { var stack = new Stack(//自己写一个Stack组件,减少嵌套,避免恶心 alignment:const FractionalOffset(0.5, 0.9),//注意里边的参数是从0~1的,原点在左上角,所以这两个参数可以让容器固定在图片的正下方 children: <Widget>[ new CircleAvatar(//一个头像 backgroundImage: NetworkImage('https://i1.hdslb.com/bfs/archive/1791fb4c554b65d4bc2d25dfbe6558bceec9066f.jpg'),//这个引入图片的语法和之前不一样注意 radius: 100.0, ), new Container( decoration: BoxDecoration( color: Colors.pinkAccent[200], ), child: Text('鬼舞姬——阿卡丽'), ) ], ); return MaterialApp( title: 'Stack Widget', home: Scaffold( appBar: new AppBar( title: new Text('层叠布局'), ), body: Center( child: stack, ) ), ); } }
相对定位Align
Align组件可以调整子组件的位置,并且可以根据子组件的宽高来确定自身的宽高
Container( height: 120.0, width: 120.0, color: Colors.blue, child: Align( alignment: Alignment.topRight, child: Text("Text"), ), )
补充
AppBar导航条
页面的导航条区域,示例代码如下:
appBar: AppBar( title: Text('页面标题'), // 标题是否居中显示 centerTitle: true, // 右侧的按钮 actions: <Widget>[ Padding( // 为 IconButton 添加右 padding 为 10px padding: EdgeInsets.only(right: 10), child: IconButton( // 右侧的搜索按钮 icon: Icon(Icons.search), onPressed: () {}, ), ) ], )底部TabBar
通常配合
Scaffold
组件的bottomNavigationBar
属性一起使用,用来渲染底部的 TabBar 效果。
- TabBar 最好和有状态页面配合使用
- TabBar 必须指定 TabController 控制器,用来控制 TabBar 的切换
- 如若不指定 TabController 控制器,也可以使用 DefaultTabController 组件,把 TabBar 组件包裹起来,同时提供需要切换的页面个数即可
DefaultTabController( // 指定需要切换的页面个数 length: tablist.length, child: Scaffold( appBar: AppBar(), // 被 tabbar 控制切换的页面 body: TabBarView(), // 指定 tab 项 bottomNavigationBar: TabBar( tabs: tablist ) ), )
- TabController 必须用在拥有 TickerProviderStateMixin 或 SingleTickerProviderStateMixin 特征的类中,因此,必须让 TabController 所在的类实现此特征,
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin { }
Drawer侧边栏抽屉
drawer: Drawer( // 抽屉可能在高度上超出屏幕,所以使用 ListView 组件包裹起来,实现纵向滚动效果 child: ListView( // 干掉顶部灰色区域 padding: EdgeInsets.all(0), // 所有抽屉中的子组件都定义到这里: children: <Widget>[], ))DrawerHeader头部
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_swiper/flutter_swiper.dart'; void main() => runApp(new MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Flutter Demo', home: new MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => new _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { @override Widget build(BuildContext context) { return Scaffold( appBar:AppBar(title:Text("抽屉")), body:Center(child:Text("页面")), drawer: Drawer( child: Column( children: <Widget>[ Row( children: <Widget>[ Expanded( child: DrawerHeader( child: Text('这是头部图片'), decoration: BoxDecoration( image:DecorationImage( image: NetworkImage("https://pic2.zhimg.com/80/v2-40c024ce464642fcab3bbf1b0a233174_hd.jpg"), fit:BoxFit.fill ) ), ),) ], ), ListTile( leading: CircleAvatar( child: Icon(Icons.home), ), title: Text("主页"), onTap: (){ //跳转路由代码 }, ), Divider(), ListTile( leading:CircleAvatar( child: Icon(Icons.folder), ), title:Text("文件") ), Divider(), ListTile( leading: CircleAvatar( child: Icon(Icons.help), ), title: Text("帮助"), ) ], ) ), endDrawer:Drawer( child: Text("右侧"), ) ); } }卡片布局:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget{ @override Widget build(BuildContext context) { var card = new Card( child: Column( children: <Widget>[ ListTile( leading: Icon(Icons.person_add,color: Colors.lightBlue,), title: Text('某大学西校区学生公寓六号楼'), subtitle: Text('roadkiller:15550313333'), ), new Divider(), ListTile( leading: Icon(Icons.person_add,color: Colors.lightBlue,), title: Text('某大学西校区学生公寓六号楼'), subtitle: Text('路小哥:15550313333'), ), new Divider(), ListTile( leading: Icon(Icons.person_add,color: Colors.lightBlue,), title: Text('某大学西校区学生公寓六号楼'), subtitle: Text('陈小哥:15550313333'), ), ], ), ); return MaterialApp( title: 'Stack Widget', home: Scaffold( appBar: new AppBar( title: new Text('卡片布局'), ), body: Center( child: card, ) ), ); } }
宽度撑满:FractionallySizedBox
class ListPage extends StatelessWidget { @override Widget build(BuildContext context) { return Column( children: <Widget>[ FractionallySizedBox( widthFactor: 1,//宽度要100%,即撑满 child: Container( decoration: BoxDecoration( color: Colors.greenAccent ), child: Center( child: Text('宽度撑满'), ), ), ), ], ); } }宽度不撑满:ClipRRect
Padding( padding: EdgeInsets.all(20), child: ClipRRect( borderRadius: BorderRadius.all(Radius.circular(40)), child: Opacity( opacity: 0.6, child: Image.network('http://www.devio.org/img/avatar.png'), ), ), )布局从左到右进行排列,并且自动换行:Wrap
Wrap(//Wrap布局会从左到右进行排列,并且自动换行 spacing: 15,//水平间距 runSpacing: 10,//竖直间距 children: <Widget>[ _chip('C语言'), _chip('C++'), _chip('Java'), _chip('Python'), _chip('HTML'), _chip('CSS'), _chip('JavaScript'), _chip('Dart'), _chip('Flutter'), ], ) _chip(String s) { return Chip( label: Text(s), avatar: CircleAvatar( backgroundColor: Colors.lightBlueAccent[200], child: Text( s.substring(0,1), style:TextStyle(fontSize: 10), ), ), ); }列表ListView
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '追风者从不认输', home: Scaffold( appBar: new AppBar( title: new Text('roadkiller'), ), body: new ListView(//这个东西后面贼有用 children: <Widget>[//这是一个数组,因为列表就是可以装很多组件啊,注意是children: new ListTile( leading: new Icon(Icons.add_shopping_cart), title: new Text('add_shopping_cart'), ), new ListTile( leading: new Icon(Icons.camera_enhance), title: new Text('camera_enhance'), ), new ListTile( leading: new Icon(Icons.restore), title: new Text('restore'), ), ], ) ), ); } }继承来减少嵌套
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '追风者从不认输', home: Scaffold( appBar: new AppBar( title: new Text('roadkiller'), ), body:Center( child: Container( width: 200.0, child: MyList(), ), ), ), ); } }//嵌套少一些了 class MyList extends StatelessWidget{//可以单独把ListView组件写一个类,其实以后无论什么组件都可以的 @override Widget build(BuildContext context) { return ListView(//返回类型写对 scrollDirection: Axis.vertical,//这句话好好理解 children: <Widget>[ new Container( height: 180.0, color: Colors.lightBlueAccent, ), new Container( height: 180.0, color: Colors.amber, ), new Container( height: 180.0, color: Colors.deepOrangeAccent, ), new Container( height: 180.0, color: Colors.purpleAccent, ), ], ); } }listView下进行动态卡片布局
List listData=[ { "title":'first', "imageUrl":'http://img1.izaoxing.com/allimg/c190122/154Q51262OG0-c029.jpg', "description": 'the beautiful' }, { "title":'second', "imageUrl":'https://pic2.zhimg.com/80/v2-40c024ce464642fcab3bbf1b0a233174_hd.jpg', "description": 'the beautiful' }, { "title":'third', "imageUrl":'https://pic2.zhimg.com/v2-848ed6d4e1c845b128d2ec719a39b275_b.jpg', "description": 'the beautiful' }, { "title":'fourth', "imageUrl":'https://pic1.zhimg.com/50/v2-88b5da103cb64fe9e3f0d7b9d33fcfa4_hd.webp', "description": 'the beautiful' } ];
import 'package:flutter/material.dart'; import 'package:flutter_app/res/listData.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( home: Scaffold( appBar: AppBar( title: Text("flutterCard"), ), body: LayoutDemo()), ); } } class LayoutDemo extends StatelessWidget { @override Widget build(BuildContext context) { //TODO: implement build return ListView( children: listData.map((value){ return Card( margin: EdgeInsets.all(10), child: Column( children: <Widget>[ AspectRatio( aspectRatio: 16/9, child: Image.network(value["imageUrl"],fit: BoxFit.fill,), ), ListTile( leading: CircleAvatar( backgroundImage: NetworkImage(value["imageUrl"]), ), title:Text(value["title"]), subtitle: Text(value["description"]) ) ], ) ); }).toList(),//注意这里要转换成列表,因为listView只接受列表 ); } }其他:跨平台应用Flutter框架开发:https://blog.csdn.net/qq_44695727/category_11093329.html
Flutter学习路线-按次路线学习顺畅无比:https://jspang.com/detailed?id=58
Flutter实战视频-移动电商 (第69节更新):https://jspang.com/detailed?id=53#toc28
大佬统计的三百多个组件:http://laomengit.com/flutter/widgets/Form.html
在Flutter中构建布局 - Flutter中文网 :https://flutterchina.club/tutorials/layout/#common-layout-widgets
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。