Flutter 是Google推出并开源的移动应用开发框架,主打跨平台、高保真、高性能。 开发者可以通过Dart语言开发App,一套代码同时运行在iOS 和Android平台。 Flutter提供了丰富的组件、接口,开发者可以很快地为Flutter添加native扩展。
以下是个人学习笔记
Text Widget TextAlign属性:left(左对齐)、center(居中对齐)、right(右对齐)
maxLines属性:设置最多显示的行数
overflow属性:clip:直剩下的文字不显示;ellipsis:显示省略号;
fade:溢出的部分会进行一个上线的渐变消失的效果
style属性:见https://docs.flutter.io/flutter/painting/TextStyle-class.html
Container(容器控件) Alignment属性 1 2 3 4 5 6 7 8 9 bottomCenter:下部居中对齐。 botomLeft: 下部左对齐。 bottomRight:下部右对齐。 center:纵横双向居中对齐。 centerLeft:纵向居中横向居左对齐。 centerRight:纵向居中横向居右对齐。 topLeft:顶部左侧对齐。 topCenter:顶部居中对齐。 topRight: 顶部居左对齐。
设置宽、高和颜色属性 1 2 3 width:500.0, height:400.0, color: Colors.lightBlue,
padding属性 margin属性 decoration属性: container 的修饰器,设置背景和边框 1 2 3 4 5 6 decoration:new BoxDecoration( // 渐变 gradient:const LinearGradient( colors:[Colors.lightBlue,Colors.greenAccent,Colors.purple] // 颜色 ), border:Border.all(width:2.0,color:Colors.red) // 边框 ),
Image图片组件 路径属性 1 2 3 4 Image.asset:加载资源图片,相对路径。 Image.network:网络资源图片。 Image.file:加载本地图片,绝对路径。 Image.memory: 加载Uint8List资源图片。
fit属性的设置:控制图片的拉伸和挤压 1 2 3 4 5 6 BoxFit.fill:全图显示,图片会被拉伸,并充满父容器。 BoxFit.contain:全图显示,显示原比例,可能会有空隙。 BoxFit.cover:显示可能拉伸,可能裁切,充满(图片要充满整个容器,还不变形)。 BoxFit.fitWidth:宽度充满(横向充满),显示可能拉伸,可能裁切。 BoxFit.fitHeight :高度充满(竖向充满),显示可能拉伸,可能裁切。 BoxFit.scaleDown:效果和contain差不多,但是此属性不允许显示超过源图片大小,可小不可大
colorBlendMode(图片混合模式) 1 2 color:是要混合的颜色,如果你只设置color是没有意义的。 colorBlendMode:是混合模式,相当于我们如何混合。
repeat图片重复 1 2 3 ImageRepeat.repeat : 横向和纵向都进行重复,直到铺满整个画布。 ImageRepeat.repeatX: 横向重复,纵向不重复。 ImageRepeat.repeatY:纵向重复,横向不重复。
ListView 列表组件 demo
1 2 3 4 5 6 7 8 body: new ListView( children:<Widget>[ new ListTile( leading: new Icon(Icons.access_time), title: new Text('access_time' ) ) ] ),
横向列表的使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Axis.horizontal:横向滚动或者叫水平方向滚动。 Axis.vertical:纵向滚动或者叫垂直方向滚动。 child:new 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, ), ], )
动态列表的使用 List类型的使用:
1 2 3 4 5 var myList = List(): 非固定长度的声明。 var myList = List(2): 固定长度的声明。 var myList= List<String>():固定类型的声明方式。 var myList = [1,2,3]: 对List直接赋值。 动态列表 ListView.builder()
GridView网格列表组件 padding:表示内边距,这个小伙伴们应该很熟悉。
crossAxisSpacing:网格间的空当,相当于每个网格之间的间距。
crossAxisCount:网格的列数,相当于一行放置的网格数量。
demo
1 2 3 4 5 6 7 8 9 10 11 12 13 body:GridView.count( padding:const EdgeInsets.all(20.0), crossAxisSpacing: 10.0, crossAxisCount: 3, children: <Widget>[ const Text('I am Jspang' ), const Text('I love Web' ), const Text('jspang.com' ), const Text('我喜欢玩游戏' ), const Text('我喜欢看书' ), const Text('我喜欢吃火锅' ) ], )
水平布局Row的使用 自适应:Expanded demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 body:new Row( children: <Widget>[ Expanded( child:new RaisedButton( onPressed: (){ }, color:Colors.redAccent, child:new Text('红色按钮' ) ) ), Expanded(child:new RaisedButton( onPressed: (){ }, color:Colors.orangeAccent, child: new Text('黄色按钮' ), ) ), Expanded(child:new RaisedButton( onPressed: (){ }, color:Colors.pinkAccent, child:new Text('粉色按钮' ) ) ) ], )
非自适应 1 2 3 4 5 6 7 8 body:new Row( children: <Widget>[ new RaisedButton( onPressed: (){ }, color:Colors.redAccent, child:new Text('红色按钮' ) ),
垂直布局Column组件 1 2 3 4 5 6 7 body:Column( children: <Widget>[ Text('I am JSPang' ), Text('my website is jspang.com' ), Text('I love coding' ) ], )
1 2 3 4 5 6 CrossAxisAlignment.star:居左对齐。 CrossAxisAlignment.end:居右对齐。 CrossAxisAlignment.center:居中对齐。 main轴:如果你用column组件,那垂直就是主轴,如果你用Row组件,那水平就是主轴。 cross轴:cross轴我们称为幅轴,是和主轴垂直的方向。比如Row组件,那垂直就是幅轴,Column组件的幅轴就是水平方向的。
1 2 3 4 5 6 7 8 9 body:Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Text('I am JSPang' ), Text('my website is jspang.com' ), Text('I love coding' ) ], )
水平方向相对屏幕居中
1 2 3 4 5 6 7 8 body:Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Center(child:Text('I am JSPang' )), Center(child:Text('my website is jspang.com' )), Center(child:Text('I love coding' )) ], )
Expanded属性的使用
1 2 3 4 5 6 7 8 body:Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Center(child:Text('I am JSPang' )), Expanded(child:Center(child:Text('my website is jspang.com' ))), Center(child:Text('I love coding' )) ], )
Stack层叠布局 层叠布局的 alignment 属性:alignment属性是控制层叠的位置的,建议在两个内容进行层叠时使用。它有两个值X轴距离和Y轴距离,值是从0到1的,都是从上层容器的左上角开始算起的
CircleAvatar组件的使用,常用于头像之类的
1 2 3 4 new CircleAvatar( backgroundImage: new NetworkImage('' ), radius: 100.0, ),
Positioned组件
1 2 3 4 5 6 bottom: 距离层叠组件下边的距离 left:距离层叠组件左边的距离 top:距离层叠组件上边的距离 right:距离层叠组件右边的距离 width: 层叠定位组件的宽度 height: 层叠定位组件的高度
demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var stack = new Stack( children: <Widget>[ new CircleAvatar( backgroundImage: new NetworkImage('http://jspang.com/static//myimg/blogtouxiang.jpg' ), radius: 100.0, ), new Positioned( top:10.0, left:10.0, child: new Text('JSPang.com' ), ), new Positioned( bottom:10.0, right:10.0, child: new Text('技术胖' ), ) ], );
卡片组件布局 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 var card = new Card( child: Column( children: <Widget>[ ListTile( title:new Text('吉林省吉林市昌邑区' ,style: TextStyle(fontWeight: FontWeight.w500),), subtitle: new Text('技术胖:1513938888' ), leading: new Icon(Icons.account_box,color: Colors.lightBlue,), ), new Divider(), ListTile( title:new Text('北京市海淀区中国科技大学' ,style: TextStyle(fontWeight: FontWeight.w500),), subtitle: new Text('胜宏宇:1513938888' ), leading: new Icon(Icons.account_box,color: Colors.lightBlue,), ), new Dvider(), ], ), )
route路由层级 1 2 3 4 5 6 7 RaisedButton按钮组件 child:可以放入容器,图标,文字。让你构建多彩的按钮。 onPressed:点击事件的相应,一般会调用Navigator组件。 Navigator.push 和 Navigator.pop Navigator.push:是跳转到下一个页面,它要接受两个参数一个是上下文context,另一个是要跳转的函数。 Navigator.pop:是返回到上一个页面,使用时传递一个context(上下文)参数,使用时要注意的是,你必须是有上级页面的,也就是说上级页面使用了Navigator.push。
导航参数的传递和接收 声明数据结构类:
Dart中可以使用类来抽象一个数据,比如我们模仿一个商品信息,有商品标题和商品描述。我们定义了一个Product类,里边有两个字符型变量,title和description。
title:是商品标题。
description: 商品详情描述
代码如下:
1 2 3 4 5 class Product{ final String title; //商品标题 final String description; //商品描述 Product(this.title,this.description); }
构建一个商品列表
1 2 3 4 5 6 7 8 9 10 11 void main (){ runApp(MaterialApp( title:'数据传递案例' , home:ProductList( products:List.generate( 20, (i)=>Product('商品 $i' ,'这是一个商品详情,编号为:$i' ) ), ) )); }
导航参数的传递:使用Navigator组件,然后使用路由MaterialPageRoute传递参数
1 2 3 4 5 6 Navigator.push( context, MaterialPageRoute( builder:(context)=>new ProductDetail(product:products[index]) ) );
子页面接受参数并显示,需要声明ProductDetail这个类(组件),先要作的就是接受参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 import 'package:flutter/material.dart' ; //传递的数据结构,也可以理解为对商品数据的抽象 class Product{ final String title; //商品标题 final String description; //商品描述 Product(this.title,this.description); } void main (){ runApp(MaterialApp( title:'数据传递案例' , home:ProductList( products:List.generate( 20, (i)=>Product('商品 $i' ,'这是一个商品详情,编号为:$i' ) ), ) )); } class ProductList extends StatelessWidget{ final List<Product> products; ProductList({Key key,@required this.products}):super(key:key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title:Text('商品列表' )), body:ListView.builder( itemCount:products.length, itemBuilder: (context,index){ return ListTile( title:Text(products[index].title), onTap:(){ Navigator.push( context, MaterialPageRoute( builder:(context)=>new ProductDetail(product:products[index]) ) ); } ); }, ) ); } } class ProductDetail extends StatelessWidget { final Product product; ProductDetail({Key key ,@required this.product}):super(key:key); @override Widget build(BuildContext context) { return new Scaffold( appBar: AppBar( title:Text('${product.title}' ), ), body:Center(child: Text('${product.description}' ),) ); } }
页面跳转并返回数据
异步请求和等待:async…await
1 2 3 SnackBar的使用 SnackBar是用户操作后,显示提示信息的一个控件,类似Tost,会自动隐藏。SnackBar是以Scaffold的showSnackBar方法来进行显示的。 Scaffold.of(context).showSnackBar(SnackBar(content:Text('$result' )));
返回数据的方式
返回数据其实是特别容易的,只要在返回时带第二个参数就可以了。
Navigator.pop(context,’xxxx’); //xxx就是返回的参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 import 'package:flutter/material.dart' ; void main (){ runApp(MaterialApp( title:'页面跳转返回数据' , home:FirstPage() )); } class FirstPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar:AppBar(title:Text("找小姐姐要电话" )), body:Center( child: RouteButton(), ) ); } } //跳转的Button class RouteButton extends StatelessWidget { @override Widget build(BuildContext context) { return RaisedButton( onPressed:(){ _navigateToXiaoJieJie(context); }, child: Text('去找小姐姐' ), ); } _navigateToXiaoJieJie(BuildContext context) async{ //async是启用异步方法 final result = await Navigator.push(//等待 context, MaterialPageRoute(builder: (context)=> XiaoJieJie()) ); Scaffold.of(context).showSnackBar(SnackBar(content:Text('$result' ))); } } class XiaoJieJie extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar:AppBar( title:Text('我是小姐姐' ) ), body:Center( child:Column( children: <Widget>[ RaisedButton( child: Text('大长腿小姐姐' ), onPressed: (){ Navigator.pop(context,'大长腿:1511008888' ); }, ) , RaisedButton( child: Text('小蛮腰小姐姐' ), onPressed: (){ Navigator.pop(context,'大长腿:1511009999' ); }, ) , ], ) ) , ); } }
静态资源和项目图片的处理 pubspec.yaml 如果想配置项目资源文件,就需要使用pubspec.yaml文件,需要把资源文件在这里声明。
比如在项目根目录下新建了一个images文件夹,文件夹下面放了一个图片,图片的名称叫做blogtouxiang.jpg,那我们在pubspec.yaml文件里就要写如下代码进行声明。
1 2 assets: - images/blogtouxiang.jpg
使用项目图片资源 有了声明后,我们就可以直接在项目中引用这个文件了。这里使用最简单的代码结构,只用了一张图片。代码如下:
1 2 3 4 5 6 7 8 9 10 import 'package:flutter/material.dart' ; void main()=>runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return Container( child: Image.asset('images/blogtouxiang.jpg' ), ); } }
Flutter客户端打包 配置 项目根目录/android/app/src/main/res/AndroidManifest.xml
文件 这个文件主要用来配置APP的名称、图标和系统权限,所在的目录在:
1 2 3 项目根目录/android/app/src/main/AndroidManifest.xml android:label="flutter_app" //配置APP的名称,支持中文 android:icon="@mipmap/ic_launcher" //APP图标的文件名称
生成 keystore D:\Program\Android\'Android Studio'\jre\bin\keytool -genkey -v -keystore D:\key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key
有了这个key.jks文件后,可以到项目目录下的android文件夹下,创建一个名为key.properties的文件,并打开粘贴下面的代码。
1 2 3 4 storePassword=<password from previous step> //输入上一步创建KEY时输入的 密钥库 密码 keyPassword=<password from previous step> //输入上一步创建KEY时输入的 密钥 密码 keyAlias=key storeFile=<E:/key.jks> //key.jks的存放路径
配置key注册 key生成好后,需要在build.gradle文件中进行配置。这个过程其实很简单,就是粘贴复制一些东西,你是不需要知道这些文件的具体用处的。
第一项:
进入项目目录的/android/app/build.gradle
文件,在android{这一行前面,加入如下代码:
1 2 3 def keystorePropertiesFile = rootProject.file("key.properties" ) def keystoreProperties = new Properties() keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
把如下代码进行替换
1 2 3 4 5 buildTypes { release { signingConfig signingConfigs.debug } }
替换成的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 signingConfigs { release { keyAlias keystoreProperties['keyAlias' ] keyPassword keystoreProperties['keyPassword' ] storeFile file(keystoreProperties['storeFile' ]) storePassword keystoreProperties['storePassword' ] } } buildTypes { release { signingConfig signingConfigs.release } }
生成apk flutter build apk