当前位置:   article > 正文

Flutter开发五、常用组件与页面布局_flutter sizebox 右对齐

flutter sizebox 右对齐

目录

widget

State

MaterialApp

Scaffold脚手架容器组件

基础组件

文本组件Text Widget

child里的属性

汇总

手势组件GestureDetector

按钮组件

图片组件Image

child里的属性

colorBlendMode图片混合模式能让图片改变颜色

repeat图片重复铺满

CircleAvatar圆形头像

单选框和复选框

Checkbox

单选开关

裁剪组件

透明度组件opacity

Visibility控制子组件是否可见的组件

容器组件

脚手架-文章开头

Container容器

child里的属性

设置宽、高和颜色属性

adding,margin和decoration内外边距和背景边框

SizeBox装饰类容器

DecoratedBox装饰类容器

SafeArea安全区域

可滚动组件

列表组件ListView

  ​

图片列表

 ​

ScrollDirection横向列表

保持列表项的数据状态

监听列表是否滚动到页面底部

添加/清理 controller 滚动控制器

嵌套组件分离

动态加载列表

List集合

List传递和接收

动态列表ListView.builder()

  ​

网格视图GridView

 ​

布局组件

Row横向布局

Column纵向布局

水平布局:Expanded

弹性布局Flex

流式布局Wrap、Flow

层叠布局Stack、positioned

相对定位Align

补充

AppBar导航条

底部TabBar

Drawer侧边栏抽屉

卡片布局:

宽度撑满:FractionallySizedBox

宽度不撑满:ClipRRect

布局从左到右进行排列,并且自动换行:Wrap

列表ListView

listView下进行动态卡片布局


Flutter万物皆组件(Widget)

前言

widget

StatelessWidgetStatefulWidget是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 进行呈现。

  1. import 'package:flutter/material.dart';
  2. void main () => runApp(MyApp());
  3. class MyApp extends StatelessWidget{
  4. @override
  5. Widget build(BuildContext context ){
  6. return MaterialApp(
  7. // 指定应用程序在任务栏上显示的标题
  8. title:'Text widget',
  9. // 指定应用程序的主界面
  10. home:Scaffold(
  11. body:Center(
  12. child:Text('Hello JSPang')
  13. ),
  14. // 配置应用程序的主题
  15. theme: ThemeData(primarySwatch: Colors.red),
  16. ),
  17. );
  18. }
  19. }

Scaffold脚手架容器组件

该组件是页面结构的脚手架,包含了页面的基本组成单元,例如:

  • appBar【头部导航条区域】

  • body【页面主题内容区域】

  • drawer【侧边栏抽屉区域】

  • bottomNavigationBar【底部tabBar区域】

  • floatingActionButton【右下角浮动按钮区域】

  1. Scaffold( // 脚手架
  2. appBar: AppBar(// 导航栏
  3. title: Text('页面标题'),
  4. ),
  5. body: Center(
  6. child: Text('主体内容'),
  7. ),
  8. floatingActionButton: FloatingActionButton(
  9. onPressed: () {},
  10. child: Icon(Icons.add),
  11. ),
  12. drawer: Drawer(),
  13. bottomNavigationBar: BottomNavigationBar(// 底部导航栏
  14. items: [
  15. BottomNavigationBarItem(
  16. icon: Icon(Icons.home),
  17. ),
  18. BottomNavigationBarItem(
  19. icon: Icon(Icons.help_center),
  20. ),
  21. ],
  22. currentIndex: _selectedIndex,
  23. fixedColor: Colors.lightBlue,
  24. onTap: (index) {
  25. print("selected: $index");
  26. setState(() {
  27. _selectedIndex = index;
  28. });
  29. },
  30. ),
  31. ),
  32. // 主题颜色
  33. theme: ThemeData(primarySwatch: Colors.red),
  34. )

基础组件

文本组件Text Widget

  1. import 'package:flutter/material.dart';
  2. void main () => runApp(MyApp());
  3. class MyApp extends StatelessWidget{
  4. @override
  5. Widget build(BuildContext context ){
  6. return MaterialApp(
  7. title:'Text widget',
  8. home:Scaffold(
  9. body:Center(
  10. child:Text('Hello JSPang')
  11. ),
  12. ),
  13. );
  14. }
  15. }

child里的属性

textAlign文本的对齐方式

  • center: 文本以居中形式对齐,这个也算比较常用的了。
  • left:左对齐,经常使用,让文本居左进行对齐,效果和start一样。
  • right :右对齐,使用频率也不算高。
  • start:以开始位置进行对齐,类似于左对齐。
  • end: 以为本结尾处进行对齐,不常用。有点类似右对齐.
  1. child:Text(
  2. 'Hello',
  3. textAlign:TextAlign.left,
  4. )

maxLines:设置最多显示的行数,溢出部分默认不显示,可以通过overflow设置

  1. 'Hello',
  2. textAlign:TextAlign.left,
  3. maxLines: 1,

overflow:溢出文字显示方式

  • clip:直接切断,剩下的文字就没有了,不太友好。
  • ellipsis:在后边显示省略号,体验性较好,常用。
  • fade: 溢出的部分会进行一个渐变消失的效果,上线的渐变,不是左右。
  1. 'Hello',
  2. textAlign:TextAlign.left,
  3. overflow:TextOverflow.ellipsis,
  4. maxLines: 1,

style属性的内容比较多可参考api

TextStyle里的属性:

  • fontSize:文字大小
  • color:设置文字颜色,一般用color:color.fromARGB()
  • decoration: TextDecoration.underline,//下划线,也有其他的
  • decorationStyle: TextDecorationStyle.solid,//实线

效果:字体大小为25.0,颜色为粉红色,并且有一个下划线。

  1. 'Hello',
  2. textAlign:TextAlign.left,
  3. overflow:TextOverflow.ellipsis,
  4. maxLines: 1,
  5. style: TextStyle(
  6. fontSize:25.0,
  7. color:Color.fromARGB(255, 255, 150, 150),
  8. decoration:TextDecoration.underline,
  9. decorationStyle:TextDecorationStyle.solid,
  10. ),

汇总

  1. import 'package:flutter/material.dart';
  2. import 'package:flutter/rendering.dart';//一般要引入这俩文件
  3. void main() => runApp(MyApp());//dart语法,箭头函数,单表达式返回语句简写
  4. class MyApp extends StatelessWidget {//继承这个类是固定的
  5. @override//覆写父类的Build方法
  6. Widget build(BuildContext context) {//这里下面写的东西都是组件
  7. return MaterialApp(//返回这个组件
  8. title: "Text Widget",
  9. home: Scaffold(//home为窗口本体,scaffold本意为脚手架,其实就是可以搭建里边的内容
  10. body: Center(//内容在屏幕中间,Center也是一个组件
  11. child: Text(//Text组件
  12. "Your life is up to you.",
  13. textAlign: TextAlign.left,//文字对齐方式,还有center和right
  14. maxLines: 2,//最大行数,超出的部分见下行overflow
  15. overflow: TextOverflow.ellipsis,//ellipsis意思是溢出的文字会以省略号显示
  16. style: TextStyle(
  17. fontSize: 30.0,//字体大小,注意一定要加.0
  18. color: Color.fromARGB(255, 255, 105, 145),//第一个参数是透明度,后面就是RGB颜色
  19. ),
  20. ),
  21. ),
  22. ),
  23. );
  24. }
  25. }

手势组件GestureDetector

  1. GestureDetector(
  2. child: Text("Text"),
  3. onTapUp: () {
  4. print("onTapUp");
  5. },
  6. onTapDown: () {
  7. print("onTapDown");
  8. },
  9. onTapCancel: () {
  10. print("onTapCancel");
  11. },
  12. onTap: () {
  13. print("onTap");
  14. },
  15. onDoubleTap: () {
  16. print("onDoubleTap");
  17. },
  18. onLongPress: () {
  19. print("onLongPress");
  20. },
  21. )

按钮组件

Flutter开发六、MaterialButton风格按钮以及按钮自适应

图片组件Image

  • Image.asset:加载资源图片,就是加载项目资源目录中的图片,会增大打包的包体体积,用的是相对路径。
  • Image.network:网络资源图片
  • Image.file:加载本地图片,就是加载本地文件中的图片,这个是一个绝对路径,跟包体无关。
  • Image.memory: 加载Uint8List资源图片。
  1. child:Container(
  2. child: Image.network(
  3. 'http://static/myimg/blogtouxiang.jpg',
  4. scale:1.0,
  5. // fit: BoxFit.fitWidth,//图片在容器中的填充方式
  6. color: Colors.pink[50],//需要混合在图片上的颜色
  7. colorBlendMode:BlendMode.colorBurn,//图片以何种模式与颜色混合
  8. repeat: ImageRepeat.repeatY,//图片重复,以中心Y轴纵向重复
  9. ),
  10. width:300.0,
  11. height: 700.0,
  12. color: Colors.pink[100],//容器的颜色,我这里把尺寸设置的超过了屏幕,皆为美观
  13. ),

child里的属性

fit控制图片拉伸和挤压

  • BoxFit.fill:全图显示,图片会被拉伸,并充满父容器。

  • BoxFit.contain:全图显示,显示原比例,可能会有空隙。

  • BoxFit.cover:显示可能拉伸,可能裁切,充满(图片要充满整个容器,还不变形)。

  • BoxFit.fitWidth:宽度充满(横向充满),显示可能拉伸,可能裁切。

  • BoxFit.fitHeight :高度充满(竖向充满),显示可能拉伸,可能裁切。

  • BoxFit.scaleDown:效果和contain差不多,但是此属性不允许显示超过源图片大小,可小不可大。

  1. child: Image.network(
  2. 'http://static/myimg/blogtouxiang.jpg',
  3. scale:1.0,
  4. fit: BoxFit.fill,
  5. ),

colorBlendMode图片混合模式能让图片改变颜色

  • color:是要混合的颜色,如果你只设置color是没有意义的。
  • colorBlendMode:是混合模式,相当于我们如何混合。
  1. child: Image.network(
  2. 'http://static/myimg/blogtouxiang.jpg',
  3. color: Colors.greenAccent,
  4. colorBlendMode: BlendMode.darken,
  5. ),

repeat图片重复铺满

  • ImageRepeat.repeat : 横向和纵向都进行重复,直到铺满整个画布。

  • ImageRepeat.repeatX: 横向重复,纵向不重复。

  • ImageRepeat.repeatY:纵向重复,横向不重复。

  1. child: Image.network(
  2. 'http://static/myimg/blogtouxiang.jpg',
  3. repeat: ImageRepeat.repeat,
  4. ),

CircleAvatar圆形头像

提供圆形的用户头像区域,使用起来比较简单,示例代码如下:

  1. CircleAvatar(
  2. backgroundImage: NetworkImage(
  3. 'https://images.gitee.com/uploads/91/465191_vsdeveloper.png'),
  4. )

单选框和复选框

Checkbox

  1. Checkbox(
  2. value: isSelected,
  3. activeColor: Colors.red,
  4. onChanged: (value) {
  5. print(value);
  6. },
  7. )

单选开关

  1. Switch(
  2. value: isSelected,
  3. activeColor: Colors.red,
  4. onChanged: (value){
  5. print(value);
  6. },)

裁剪组件

Clip的相关组件:
ClipOval:圆形裁剪
ClipRRect:圆角矩形裁剪
ClipRect:矩形裁剪
ClipPath:路径裁剪

  1. ClipOval(
  2. child: SizeBox(
  3. width: 100, height: 100, child: Image.network("http://xxxxx.png")),
  4. )

透明度组件opacity

  1. Opacity(
  2. opacity: 0.5,
  3. child: Text("Text"),
  4. )

Visibility控制子组件是否可见的组件

  1. Visibility(
  2. visible: false,// 子组件是否可见,默认true
  3. child: Text("Text"),// 子组件
  4. replacement: Text("data"),// 不可见时显示的组件(当maintainState=false
  5. maintainState: true,//不可见时是否维持状态,默认为false
  6. )

容器组件

脚手架-文章开头

Container容器

是一个组合类的容器组件,不参与最终的渲染

Container(容器控件)在Flutter是经常使用的控件,它就相当于我们HTML里的<div>标签,每个页面或者说每个视图都离不开它

  1. class MyApp extends StatelessWidget{
  2. @override
  3. Widget build(BuildContext context ){
  4. return MaterialApp(
  5. title:'Text widget',
  6. home:Scaffold(
  7. body:Center(
  8. child:Container(
  9. ),
  10. ),
  11. ),
  12. );
  13. }
  14. }

child里的属性

alignment属性:容器子内容的对齐方式

  • bottomCenter:下部居中对齐。
  • botomLeft: 下部左对齐。
  • bottomRight:下部右对齐。
  • center:纵横双向居中对齐。
  • centerLeft:纵向居中横向居左对齐。
  • centerRight:纵向居中横向居右对齐。
  • topLeft:顶部左侧对齐。
  • topCenter:顶部居中对齐。
  • topRight: 顶部居左对齐
  1. child:Container(
  2. child: Text('Hello',style: TextStyle(fontSize: 40.0),),
  3. alignment: Alignment.center,
  4. ),

设置宽、高和颜色属性

width:设置为double.infinity可以强制在宽度上撑满
height:设置为double.infinity可以强制在高度上撑满。

  1. child:Container(
  2. child: Text('Hello',style: TextStyle(fontSize: 40.0),),
  3. alignment: Alignment.center,
  4. width:500.0,
  5. height:400.0,
  6. color: Colors.lightBlue,
  7. ),

adding,margindecoration内外边距和背景边框

padding内边距为10,EdgeInsets.fromLTRB(value1,value2,value3,value4)分别代表左、上、右、下

  1. height:400.0,
  2. color: Colors.lightBlue,
  3. padding:const EdgeInsets.all(10.0),

margin外边距为10,分开设置也是fromLTRB

margin: const EdgeInsets.all(10.0),

decoration设置背景和边框

背景加入一个渐变(设置了decoration,不要再设置color属性了)

  1. decoration: BoxDecoration(
  2. gradient:const LinearGradient(
  3. colors:[Colors.lightBlue,Colors.greenAccent,Colors.purple]
  4. )
  5. ),

红色边框

  1. decoration: BoxDecoration(
  2. gradient:const LinearGradient(
  3. colors:[Colors.lightBlue,Colors.greenAccent,Colors.purple]
  4. ),
  5. border:Border.all(width:2.0,color:Colors.red)
  6. ),

SizeBox装饰类容器

可以设置具体尺寸的控件,child不为null时,如果设置宽高,则强制显示宽高,如果没有设置,则自适应;如果child为null时,可当做间隔使用

  1. SizedBox(
  2. height: 16,
  3. child: Text("Text"),
  4. )

DecoratedBox装饰类容器

可以在其子组件绘制前(或者绘制后)绘制一些装饰,如背景、边框、渐变等

  1. DecoratedBox(
  2. decoration: BoxDecoration(
  3. color: Colors.white,// 背景色
  4. borderRadius: BorderRadius.all(Radius.circular(2.0)),// 圆角
  5. border: Border.all(color: Colors.pink, width: 1),// 边框
  6. gradient: LinearGradient(colors: [Colors.red, Colors.orange[700]]),// 渐变
  7. boxShadow: [// 阴影
  8. BoxShadow(
  9. color: Colors.black12, offset: Offset(5.0, 5.0), blurRadius: 4.0)
  10. ],
  11. ),
  12. child: Text("Text"),
  13. )

SafeArea安全区域

留出刘海和底部的安全区域,很好的解决刘海屏兼容问题

SafeArea(child: Text("Text"))

可滚动组件

列表组件ListView

  • 使用ListView,然后在他的内部children中,使用了widget数组,因为是一个列表,所以它接受一个数组
  • ListView是最常用的可滚动组件之一,它可以沿一个方向线性排布所有子组件,并且它也支持基于Sliver的延迟构建模型

示例

  1. body: ListView(
  2. children:<Widget>[
  3. ListTile(
  4. leading: Icon(Icons.access_time),
  5. title: Text('hello')
  6. ),
  7. ListTile(
  8. leading: Icon(Icons.access_time),
  9. title: Text('hello')
  10. ),
  11. ]
  12. ),

  

图片列表

  1. children:<Widget>[
  2. new Image.network(
  3. 'https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3289342822,655864970&fm=26&gp=0.jpg'
  4. ),
  5. new Image.network(
  6. 'https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3289342822,655864970&fm=26&gp=0.jpg'
  7. ),
  8. ListTile(
  9. leading: Icon(Icons.access_time),
  10. title: Text('access_time')
  11. ),
  12. ]

 

ScrollDirection横向列表

  • Axis.horizontal:横向滚动或者叫水平方向滚动。
  • Axis.vertical:纵向滚动或者叫垂直方向滚动。
  • 不设置垂直滚动
  1. body: ListView(
  2. scrollDirection: Axis.horizontal,
  3. children:<Widget>[
  4. new Image.network(
  5. 'https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3289342822,655864970&fm=26&gp=0.jpg',
  6. width: 200,
  7. height: 200,
  8. ),
  9. new Image.network(
  10. 'https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3289342822,655864970&fm=26&gp=0.jpg',
  11. width: 200,
  12. height: 200,
  13. ),
  14. ]
  15. ),

 

保持列表项的数据状态

多个列表页之间的动态切换,保持每个列表项的滚动距离、数据状态等信息,可以实现 AutomaticKeepAliveClientMixin特征,来保持列表项的滚动状态

  1. class _MovieListState extends State<MovieList>
  2. with AutomaticKeepAliveClientMixin {
  3. // 重写 wantKeepAlive 函数
  4. @override
  5. bool get wantKeepAlive => true;
  6. }

监听列表是否滚动到页面底部

  1. // 定义私有变量 _scrollCtrl
  2. ScrollController _scrollCtrl;
  3. // 重写 initState 生命周期函数
  4. @override
  5. void initState() {
  6. // 此行为默认代码,不能删除
  7. super.initState();
  8. // 初始化一个 ScrollController 滚动控制器
  9. _scrollCtrl = new ScrollController();
  10. // 为 _scrollCtrl 滚动控制器添加监听事件
  11. _scrollCtrl.addListener(() {
  12. // _scrollCtrl.position.pixels 当前列表滚动的距离
  13. // _scrollCtrl.position.maxScrollExtent 列表的最大滚动距离
  14. if (_scrollCtrl.position.pixels == _scrollCtrl.position.maxScrollExtent) {
  15. // 调用 setState 函数,让页码值 +1
  16. setState(() {
  17. _page++;
  18. });
  19. // 获取新页面的数据
  20. _getMovieList();
  21. }
  22. });
  23. }

添加/清理 controller 滚动控制器

  1. ListView.builder(
  2. controller: _scrollCtrl,
  3. itemCount: _mlist.length,
  4. itemBuilder: (BuildContext ctx, int i) {}
  5. )
  6. ...
  7. // 重新 dispose 函数
  8. @override
  9. void dispose() {
  10. super.dispose();
  11. // 主动销毁滚动控制器
  12. _scrollCtrl.dispose();
  13. }

嵌套组件分离

声明一个List的类

  1. import 'package:flutter/material.dart';
  2. class List extends StatelessWidget{
  3. @override
  4. Widget build(BuildContext context){
  5. return ListView(
  6. scrollDirection: Axis.horizontal,
  7. children: <Widget>[
  8. new Container(
  9. width:180.0,
  10. color: Colors.lightBlue,
  11. ), new Container(
  12. width:180.0,
  13. color: Colors.amber,
  14. ), new Container(
  15. width:180.0,
  16. color: Colors.deepOrange,
  17. ),new Container(
  18. width:180.0,
  19. color: Colors.deepPurpleAccent,
  20. ),
  21. ],
  22. );
  23. }
  24. }

在MyAPP类里直接使用这个类

  1. import 'package:flutter/material.dart';
  2. import 'package:untitled/List.dart';
  3. void main() {
  4. runApp(MyApp());
  5. }
  6. class MyApp extends StatelessWidget {
  7. @override
  8. Widget build(BuildContext context ){
  9. return MaterialApp(
  10. title:'ListView widget',
  11. home:Scaffold(
  12. body:Center(
  13. child:Container(
  14. height:200.0,
  15. child: List()
  16. ),
  17. ),
  18. ),
  19. );
  20. }
  21. }

  

动态加载列表

List集合

List是Dart的集合类型之一

  • var myList = List(): 非固定长度的声明。
  • var myList = List(2): 固定长度的声明。
  • var myList= List<String>():固定类型的声明方式。
  • var myList = [1,2,3]: 对List直接赋值。

List传递和接收

传递

  1. void main() {
  2. runApp(MyApp(
  3. items: List<String>.generate(1000, (i)=> "Item $i")
  4. ));
  5. }

MyApp类接收

这是一个构造函数,除了Key,增加了一个必传参数,@required意思必传。:super如果父类没有无名无参数的默认构造函数,则子类必须手动调用一个父类构造函数。

  1. final List<String> items;
  2. MyApp({Key key, @required this.items}):super(key:key);

动态列表ListView.builder()

  1. import 'package:flutter/material.dart';
  2. void main() {
  3. runApp(MyApp(
  4. items: List<String>.generate(1000, (i)=> "Item $i")
  5. ));
  6. }
  7. class MyApp extends StatelessWidget {
  8. final List<String> items;
  9. MyApp({Key key, @required this.items}):super(key:key);
  10. @override
  11. Widget build(BuildContext context ){
  12. print(items);
  13. return MaterialApp(
  14. title:'ListView widget',
  15. home:Scaffold(
  16. body: ListView.builder(
  17. itemCount:items.length,
  18. itemBuilder:(context,index){
  19. return ListTile(
  20. title: Text('${items[index]}'),
  21. );
  22. }
  23. )
  24. ),
  25. );
  26. }
  27. }

运行main方法

  

网格视图GridView

GridView,顾名思义就是我们用来做网格的,通常我们用来做相册,或者网格状的展示栏 

  • padding:表示内边距,这个小伙伴们应该很熟悉。
  • crossAxisSpacing:网格间的空当,相当于每个网格之间的间距。
  • crossAxisCount:网格的列数,相当于一行放置的网格数量。
  1. import 'package:flutter/material.dart';
  2. void main() {
  3. runApp(MyApp());
  4. }
  5. class MyApp extends StatelessWidget {
  6. @override
  7. Widget build(BuildContext context ){
  8. return MaterialApp(
  9. title:'ListView widget',
  10. home:Scaffold(
  11. body: GridView.count(
  12. padding:const EdgeInsets.all(20.0),
  13. crossAxisSpacing: 10.0,
  14. crossAxisCount: 3,
  15. children: <Widget>[
  16. Image.network(
  17. 'https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3289342822,655864970&fm=26&gp=0.jpg',
  18. width: 200,
  19. height: 200,
  20. ),
  21. Image.network(
  22. 'https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3289342822,655864970&fm=26&gp=0.jpg',
  23. width: 200,
  24. height: 200,
  25. ),
  26. Image.network(
  27. 'https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3289342822,655864970&fm=26&gp=0.jpg',
  28. width: 200,
  29. height: 200,
  30. ),
  31. const Text('我喜欢玩游戏'),
  32. const Text('我喜欢看书'),
  33. const Text('我喜欢吃火锅')
  34. ],
  35. )
  36. ),
  37. );
  38. }
  39. }

 

  • childAspectRatio:宽高比,这个值的意思是宽是高的多少倍,如果宽是高的2倍,那我们就写2.0,如果高是宽的2倍,我们就写0.5。希望小伙伴们理解一下。
  1. import 'package:flutter/material.dart';
  2. void main() {
  3. runApp(MyApp());
  4. }
  5. class MyApp extends StatelessWidget {
  6. @override
  7. Widget build(BuildContext context ){
  8. return MaterialApp(
  9. title:'ListView widget',
  10. home:Scaffold(
  11. body:GridView(
  12. gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
  13. crossAxisCount: 3,//一行放几幅图片
  14. mainAxisSpacing: 2.0,//行距
  15. crossAxisSpacing: 2.0,//列距
  16. childAspectRatio: 0.7,//图片宽高比,因为海报一般长:宽是4:3嘛所以我搞的是这样
  17. ),
  18. children: <Widget>[
  19. Image.network('http://img5.mtime.cn/mt/2018/10/22/104316.77318635_180X260X4.jpg',fit: BoxFit.cover),
  20. Image.network('http://img5.mtime.cn/mt/2018/10/10/112514.30587089_180X260X4.jpg',fit: BoxFit.cover),
  21. Image.network('http://img5.mtime.cn/mt/2018/11/13/093605.61422332_180X260X4.jpg',fit: BoxFit.cover),
  22. Image.network('http://img5.mtime.cn/mt/2018/11/07/092515.55805319_180X260X4.jpg',fit: BoxFit.cover),
  23. Image.network('http://img5.mtime.cn/mt/2018/11/21/090246.16772408_135X190X4.jpg',fit: BoxFit.cover),
  24. Image.network('http://img5.mtime.cn/mt/2018/11/17/162028.94879602_135X190X4.jpg',fit: BoxFit.cover),
  25. Image.network('http://img5.mtime.cn/mt/2018/11/19/165350.52237320_135X190X4.jpg',fit: BoxFit.cover),
  26. Image.network('http://img5.mtime.cn/mt/2018/11/16/115256.24365160_180X260X4.jpg',fit: BoxFit.cover),
  27. Image.network('http://img5.mtime.cn/mt/2018/11/20/141608.71613590_135X190X4.jpg',fit: BoxFit.cover),
  28. ],
  29. )
  30. ),
  31. );
  32. }
  33. }

  

  1. import 'package:flutter/material.dart';
  2. void main() => runApp(MyApp());
  3. class MyApp extends StatelessWidget
  4. {
  5. @override
  6. Widget build(BuildContext context)
  7. {
  8. return MaterialApp(
  9. title: 'roadkiller',
  10. home: Scaffold(
  11. appBar: new AppBar(
  12. title: new Text('roadkiller'),
  13. ),
  14. body:GridView(
  15. padding: new EdgeInsets.all(3.0),//学过前端好理解吧
  16. gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
  17. crossAxisCount:2,//一行放几幅图片
  18. mainAxisSpacing: 2.0,//行距
  19. crossAxisSpacing: 2.0,//列距
  20. childAspectRatio: 0.75//图片宽高比,因为海报一般长:宽是4:3嘛所以我搞的是这样
  21. ),
  22. children: <Widget>[
  23. new Image.network('https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=3246509238,1077330305&fm=58&s=DFD513C6886286D432620FBC0300301F'),
  24. new Image.network('https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=2512763846,3067531314&fm=58&s=9102AEFBD3A7E4EE4437527303008074'),
  25. new Image.network('https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2585813656,2994285865&fm=58&s=C5649A468F378ECC266911AC03008012'),
  26. new Image.network('https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=3351456143,3085413485&fm=58&s=0CC0F804845BBFCE320951990300C082'),
  27. new Image.network('https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=581555814,2183385336&fm=58&s=2740D94FDE3283DC1259A91301008092'),
  28. new Image.network('https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=3527165871,1016449403&fm=58&s=787B20C402B38BC456651C8D0300E088'),
  29. new Image.network('https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=2801239214,2575519245&fm=58'),
  30. new Image.network('https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=3895436776,1493313546&fm=58&s=C115C730589647FF5E89F0C5030070A1'),
  31. new Image.network('https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=3343253172,1276100043&fm=58&s=9DB5109B1C1247F56898AFCA03005037'),
  32. new Image.network('https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=1588364103,2036988885&fm=58&s=DFDDA844965203D4CFA4599303008099'),
  33. ],
  34. )
  35. ),
  36. );
  37. }
  38. }

布局组件

上面已讲到容器组件Container,布局出来child:Container()盒子还有RowColumn

Row横向布局

水平方向排布组件,不会自动换行

children【子元素】
mainAxisAlignment【横向对其方式】
crossAxisAlignment【纵向对其方式】

  1. Row(
  2. mainAxisAlignment: MainAxisAlignment.start,// 主轴对齐方式
  3. crossAxisAlignment: CrossAxisAlignment.start,// 交叉轴对齐方式
  4. children: [
  5. Text("Text1"),
  6. Text("Text2"),
  7. Text("Text3"),
  8. Text("Text4"),
  9. ],
  10. )

Column纵向布局

垂直方向排布组件,不会自动换行

children【子元素】
mainAxisAlignment【纵向对其方式】
crossAxisAlignment【横向对其方式】

  1. Column(
  2. children: [
  3. Text("Text1"),
  4. Text("Text2"),
  5. Text("Text3"),
  6. Text("Text4"),
  7. ],
  8. )

要占满整个竖直方向的屏幕,加Expanded

  1. import 'package:flutter/material.dart';
  2. void main() => runApp(MyApp());
  3. class MyApp extends StatelessWidget{
  4. @override
  5. Widget build(BuildContext context)
  6. {
  7. return MaterialApp(
  8. title: 'Column Widget',
  9. home: Scaffold(
  10. appBar: new AppBar(
  11. title: new Text('垂直方向布局'),
  12. ),
  13. body: Center(
  14. child:new Column(
  15. mainAxisAlignment: MainAxisAlignment.center,//主轴是相对于你的布局方向来说的,如果你是水平布局那水平轴就是主轴,如果你是垂直布局那竖直轴就是主轴
  16. crossAxisAlignment: CrossAxisAlignment.center,
  17. children: <Widget>[
  18. new RaisedButton(
  19. onPressed: (){},//这个是如果按了按钮会进行的操作,我们现在还没有就先不写
  20. color: Colors.lightBlue,
  21. child: new Text('blue button'),
  22. ),
  23. new RaisedButton(
  24. onPressed: (){},
  25. color: Colors.pink[200],
  26. child: new Text('pink button'),
  27. ),
  28. new RaisedButton(
  29. onPressed: (){},
  30. color: Colors.grey,
  31. child: new Text('grey button'),
  32. )
  33. ],
  34. ),
  35. )
  36. ),
  37. );
  38. }
  39. }

水平布局:Expanded

  • 可以使Row、Column、Flex等,子组件在其主轴上展开并填充可用空间,撑开父组件。必须使用在Row、Column、Flex等
  • 主要用来控制 flex 布局的占位宽度。需要用在 Row 或 Column 子组件内部。
  1. import 'package:flutter/material.dart';
  2. void main() => runApp(MyApp());
  3. class MyApp extends StatelessWidget{
  4. @override
  5. Widget build(BuildContext context)
  6. {
  7. return MaterialApp(
  8. title: 'Row Widget',
  9. home: Scaffold(
  10. appBar: new AppBar(
  11. title: new Text('垂直方向布局'),
  12. ),
  13. body: new Column(
  14. children: <Widget>[
  15. Expanded(
  16. child:new RaisedButton(
  17. onPressed: (){},//这个是如果按了按钮会进行的操作,我们现在还没有就先不写
  18. color: Colors.lightBlue,
  19. child: new Text('blue button'),
  20. )
  21. ),
  22. Expanded(
  23. child:new RaisedButton(
  24. onPressed: (){},
  25. color: Colors.pink[200],
  26. child: new Text('pink button'),
  27. )
  28. ),
  29. Expanded(
  30. child:new RaisedButton(
  31. onPressed: (){},
  32. color: Colors.orange,
  33. child: new Text('orange button'),
  34. )
  35. ),
  36. ],
  37. ),
  38. ),
  39. );
  40. }
  41. }

弹性布局Flex

如h5当中的弹性盒子布局,Row和Column都继承自Flex,参数也基本一致所以可以使用Row和Column来代替Flex

  1. Flex(
  2. direction: Axis.horizontal,
  3. children: <Widget>[
  4. Text("Text1"),
  5. Text("Text2"),
  6. Text("Text3"),
  7. Text("Text4"),
  8. ],
  9. )

流式布局Wrap、Flow

Wrap和Row类似,如果一行控件不够会自动换行
Flow和Column类似,如果一列控件不够会自动换行

  1. Wrap(
  2. spacing: 8.0, // 主轴(水平)方向间距
  3. runSpacing: 8.0, // 纵轴(垂直)方向间距
  4. alignment: WrapAlignment.center, // 沿主轴方向居中
  5. children: <Widget>[
  6. Text("Text1"),
  7. Text("Text2"),
  8. Text("Text3"),
  9. Text("Text4"),
  10. ],
  11. )

层叠布局Stack、positioned

  • 类似绝对定位,往往与Positioned联合使用,子组件可以根据父容器四个角的位置来确定自身的位置。绝对定位允许子组件堆叠起来(按照代码中声明的顺序)。
  • 想要在图片上面层叠放一些字或者Container,就需要采用层叠布局,Stack
  • 注意alignment里的FractionalOffset的使用
  1. import 'package:flutter/material.dart';
  2. void main() => runApp(MyApp());
  3. class MyApp extends StatelessWidget{
  4. @override
  5. Widget build(BuildContext context)
  6. {
  7. var stack = new Stack(//自己写一个Stack组件,减少嵌套,避免恶心
  8. alignment:const FractionalOffset(0.5, 0.9),//注意里边的参数是从0~1的,原点在左上角,所以这两个参数可以让容器固定在图片的正下方
  9. children: <Widget>[
  10. new CircleAvatar(//一个头像
  11. backgroundImage: NetworkImage('https://i1.hdslb.com/bfs/archive/1791fb4c554b65d4bc2d25dfbe6558bceec9066f.jpg'),//这个引入图片的语法和之前不一样注意
  12. radius: 100.0,
  13. ),
  14. new Container(
  15. decoration: BoxDecoration(
  16. color: Colors.pinkAccent[200],
  17. ),
  18. child: Text('鬼舞姬——阿卡丽'),
  19. )
  20. ],
  21. );
  22. return MaterialApp(
  23. title: 'Stack Widget',
  24. home: Scaffold(
  25. appBar: new AppBar(
  26. title: new Text('层叠布局'),
  27. ),
  28. body: Center(
  29. child: stack,
  30. )
  31. ),
  32. );
  33. }
  34. }

相对定位Align

Align组件可以调整子组件的位置,并且可以根据子组件的宽高来确定自身的宽高

  1. Container(
  2. height: 120.0,
  3. width: 120.0,
  4. color: Colors.blue,
  5. child: Align(
  6. alignment: Alignment.topRight,
  7. child: Text("Text"),
  8. ),
  9. )

补充

AppBar导航条

页面的导航条区域,示例代码如下:

  1. appBar: AppBar(
  2. title: Text('页面标题'),
  3. // 标题是否居中显示
  4. centerTitle: true,
  5. // 右侧的按钮
  6. actions: <Widget>[
  7. Padding( // 为 IconButton 添加右 padding 为 10px
  8. padding: EdgeInsets.only(right: 10),
  9. child: IconButton( // 右侧的搜索按钮
  10. icon: Icon(Icons.search),
  11. onPressed: () {},
  12. ),
  13. )
  14. ],
  15. )

底部TabBar

通常配合 Scaffold 组件的 bottomNavigationBar 属性一起使用,用来渲染底部的 TabBar 效果。

  • TabBar 最好和有状态页面配合使用
  • TabBar 必须指定 TabController 控制器,用来控制 TabBar 的切换
  • 如若不指定 TabController 控制器,也可以使用 DefaultTabController 组件,把 TabBar 组件包裹起来,同时提供需要切换的页面个数即可
  1. DefaultTabController(
  2. // 指定需要切换的页面个数
  3. length: tablist.length,
  4. child: Scaffold(
  5. appBar: AppBar(),
  6. // 被 tabbar 控制切换的页面
  7. body: TabBarView(),
  8. // 指定 tab 项
  9. bottomNavigationBar: TabBar(
  10. tabs: tablist
  11. )
  12. ),
  13. )
  • TabController 必须用在拥有 TickerProviderStateMixin 或 SingleTickerProviderStateMixin 特征的类中,因此,必须让 TabController 所在的类实现此特征,
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin { }

Drawer侧边栏抽屉

  1. drawer: Drawer(
  2. // 抽屉可能在高度上超出屏幕,所以使用 ListView 组件包裹起来,实现纵向滚动效果
  3. child: ListView(
  4. // 干掉顶部灰色区域
  5. padding: EdgeInsets.all(0),
  6. // 所有抽屉中的子组件都定义到这里:
  7. children: <Widget>[],
  8. ))

DrawerHeader头部

  1. import 'package:flutter/cupertino.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter_swiper/flutter_swiper.dart';
  4. void main() => runApp(new MyApp());
  5. class MyApp extends StatelessWidget {
  6. @override
  7. Widget build(BuildContext context) {
  8. return new MaterialApp(
  9. title: 'Flutter Demo',
  10. home: new MyHomePage(title: 'Flutter Demo Home Page'),
  11. );
  12. }
  13. }
  14. class MyHomePage extends StatefulWidget {
  15. MyHomePage({Key key, this.title}) : super(key: key);
  16. final String title;
  17. @override
  18. _MyHomePageState createState() => new _MyHomePageState();
  19. }
  20. class _MyHomePageState extends State<MyHomePage> {
  21. @override
  22. Widget build(BuildContext context) {
  23. return Scaffold(
  24. appBar:AppBar(title:Text("抽屉")),
  25. body:Center(child:Text("页面")),
  26. drawer: Drawer(
  27. child: Column(
  28. children: <Widget>[
  29. Row(
  30. children: <Widget>[
  31. Expanded(
  32. child: DrawerHeader(
  33. child: Text('这是头部图片'),
  34. decoration: BoxDecoration(
  35. image:DecorationImage(
  36. image: NetworkImage("https://pic2.zhimg.com/80/v2-40c024ce464642fcab3bbf1b0a233174_hd.jpg"),
  37. fit:BoxFit.fill
  38. )
  39. ),
  40. ),)
  41. ],
  42. ),
  43. ListTile(
  44. leading: CircleAvatar(
  45. child: Icon(Icons.home),
  46. ),
  47. title: Text("主页"),
  48. onTap: (){
  49. //跳转路由代码
  50. },
  51. ),
  52. Divider(),
  53. ListTile(
  54. leading:CircleAvatar(
  55. child: Icon(Icons.folder),
  56. ),
  57. title:Text("文件")
  58. ),
  59. Divider(),
  60. ListTile(
  61. leading: CircleAvatar(
  62. child: Icon(Icons.help),
  63. ),
  64. title: Text("帮助"),
  65. )
  66. ],
  67. )
  68. ),
  69. endDrawer:Drawer(
  70. child: Text("右侧"),
  71. )
  72. );
  73. }
  74. }

卡片布局:

  1. import 'package:flutter/material.dart';
  2. void main() => runApp(MyApp());
  3. class MyApp extends StatelessWidget{
  4. @override
  5. Widget build(BuildContext context)
  6. {
  7. var card = new Card(
  8. child: Column(
  9. children: <Widget>[
  10. ListTile(
  11. leading: Icon(Icons.person_add,color: Colors.lightBlue,),
  12. title: Text('某大学西校区学生公寓六号楼'),
  13. subtitle: Text('roadkiller:15550313333'),
  14. ),
  15. new Divider(),
  16. ListTile(
  17. leading: Icon(Icons.person_add,color: Colors.lightBlue,),
  18. title: Text('某大学西校区学生公寓六号楼'),
  19. subtitle: Text('路小哥:15550313333'),
  20. ),
  21. new Divider(),
  22. ListTile(
  23. leading: Icon(Icons.person_add,color: Colors.lightBlue,),
  24. title: Text('某大学西校区学生公寓六号楼'),
  25. subtitle: Text('陈小哥:15550313333'),
  26. ),
  27. ],
  28. ),
  29. );
  30. return MaterialApp(
  31. title: 'Stack Widget',
  32. home: Scaffold(
  33. appBar: new AppBar(
  34. title: new Text('卡片布局'),
  35. ),
  36. body: Center(
  37. child: card,
  38. )
  39. ),
  40. );
  41. }
  42. }

宽度撑满:FractionallySizedBox

  1. class ListPage extends StatelessWidget {
  2. @override
  3. Widget build(BuildContext context) {
  4. return Column(
  5. children: <Widget>[
  6. FractionallySizedBox(
  7. widthFactor: 1,//宽度要100%,即撑满
  8. child: Container(
  9. decoration: BoxDecoration(
  10. color: Colors.greenAccent
  11. ),
  12. child: Center(
  13. child: Text('宽度撑满'),
  14. ),
  15. ),
  16. ),
  17. ],
  18. );
  19. }
  20. }

宽度不撑满:ClipRRect

  1. Padding(
  2. padding: EdgeInsets.all(20),
  3. child: ClipRRect(
  4. borderRadius: BorderRadius.all(Radius.circular(40)),
  5. child: Opacity(
  6. opacity: 0.6,
  7. child: Image.network('http://www.devio.org/img/avatar.png'),
  8. ),
  9. ),
  10. )

布局从左到右进行排列,并且自动换行:Wrap

  1. Wrap(//Wrap布局会从左到右进行排列,并且自动换行
  2. spacing: 15,//水平间距
  3. runSpacing: 10,//竖直间距
  4. children: <Widget>[
  5. _chip('C语言'),
  6. _chip('C++'),
  7. _chip('Java'),
  8. _chip('Python'),
  9. _chip('HTML'),
  10. _chip('CSS'),
  11. _chip('JavaScript'),
  12. _chip('Dart'),
  13. _chip('Flutter'),
  14. ],
  15. )
  16. _chip(String s) {
  17. return Chip(
  18. label: Text(s),
  19. avatar: CircleAvatar(
  20. backgroundColor: Colors.lightBlueAccent[200],
  21. child: Text(
  22. s.substring(0,1),
  23. style:TextStyle(fontSize: 10),
  24. ),
  25. ),
  26. );
  27. }

列表ListView

  1. import 'package:flutter/material.dart';
  2. void main() => runApp(MyApp());
  3. class MyApp extends StatelessWidget
  4. {
  5. @override
  6. Widget build(BuildContext context)
  7. {
  8. return MaterialApp(
  9. title: '追风者从不认输',
  10. home: Scaffold(
  11. appBar: new AppBar(
  12. title: new Text('roadkiller'),
  13. ),
  14. body: new ListView(//这个东西后面贼有用
  15. children: <Widget>[//这是一个数组,因为列表就是可以装很多组件啊,注意是children:
  16. new ListTile(
  17. leading: new Icon(Icons.add_shopping_cart),
  18. title: new Text('add_shopping_cart'),
  19. ),
  20. new ListTile(
  21. leading: new Icon(Icons.camera_enhance),
  22. title: new Text('camera_enhance'),
  23. ),
  24. new ListTile(
  25. leading: new Icon(Icons.restore),
  26. title: new Text('restore'),
  27. ),
  28. ],
  29. )
  30. ),
  31. );
  32. }
  33. }

继承来减少嵌套

  1. import 'package:flutter/material.dart';
  2. void main() => runApp(MyApp());
  3. class MyApp extends StatelessWidget
  4. {
  5. @override
  6. Widget build(BuildContext context)
  7. {
  8. return MaterialApp(
  9. title: '追风者从不认输',
  10. home: Scaffold(
  11. appBar: new AppBar(
  12. title: new Text('roadkiller'),
  13. ),
  14. body:Center(
  15. child: Container(
  16. width: 200.0,
  17. child: MyList(),
  18. ),
  19. ),
  20. ),
  21. );
  22. }
  23. }//嵌套少一些了
  24. class MyList extends StatelessWidget{//可以单独把ListView组件写一个类,其实以后无论什么组件都可以的
  25. @override
  26. Widget build(BuildContext context)
  27. {
  28. return ListView(//返回类型写对
  29. scrollDirection: Axis.vertical,//这句话好好理解
  30. children: <Widget>[
  31. new Container(
  32. height: 180.0,
  33. color: Colors.lightBlueAccent,
  34. ),
  35. new Container(
  36. height: 180.0,
  37. color: Colors.amber,
  38. ),
  39. new Container(
  40. height: 180.0,
  41. color: Colors.deepOrangeAccent,
  42. ),
  43. new Container(
  44. height: 180.0,
  45. color: Colors.purpleAccent,
  46. ),
  47. ],
  48. );
  49. }
  50. }

listView下进行动态卡片布局

  1. List listData=[
  2. {
  3. "title":'first',
  4. "imageUrl":'http://img1.izaoxing.com/allimg/c190122/154Q51262OG0-c029.jpg',
  5. "description": 'the beautiful'
  6. },
  7. {
  8. "title":'second',
  9. "imageUrl":'https://pic2.zhimg.com/80/v2-40c024ce464642fcab3bbf1b0a233174_hd.jpg',
  10. "description": 'the beautiful'
  11. },
  12. {
  13. "title":'third',
  14. "imageUrl":'https://pic2.zhimg.com/v2-848ed6d4e1c845b128d2ec719a39b275_b.jpg',
  15. "description": 'the beautiful'
  16. },
  17. {
  18. "title":'fourth',
  19. "imageUrl":'https://pic1.zhimg.com/50/v2-88b5da103cb64fe9e3f0d7b9d33fcfa4_hd.webp',
  20. "description": 'the beautiful'
  21. }
  22. ];
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_app/res/listData.dart';
  3. void main() => runApp(MyApp());
  4. class MyApp extends StatelessWidget {
  5. @override
  6. Widget build(BuildContext context) {
  7. // TODO: implement build
  8. return MaterialApp(
  9. home: Scaffold(
  10. appBar: AppBar(
  11. title: Text("flutterCard"),
  12. ),
  13. body: LayoutDemo()),
  14. );
  15. }
  16. }
  17. class LayoutDemo extends StatelessWidget {
  18. @override
  19. Widget build(BuildContext context) {
  20. //TODO: implement build
  21. return ListView(
  22. children: listData.map((value){
  23. return Card(
  24. margin: EdgeInsets.all(10),
  25. child: Column(
  26. children: <Widget>[
  27. AspectRatio(
  28. aspectRatio: 16/9,
  29. child: Image.network(value["imageUrl"],fit: BoxFit.fill,),
  30. ),
  31. ListTile(
  32. leading: CircleAvatar(
  33. backgroundImage: NetworkImage(value["imageUrl"]),
  34. ),
  35. title:Text(value["title"]),
  36. subtitle: Text(value["description"])
  37. )
  38. ],
  39. )
  40. );
  41. }).toList(),//注意这里要转换成列表,因为listView只接受列表
  42. );
  43. }
  44. }

其他:跨平台应用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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/289718
推荐阅读
相关标签
  

闽ICP备14008679号