Flutter移动前端
2019-11-26 17:43 小罗世界 阅读(1185) 评论(0) 收藏 举报一、flutter基础
1、flutter介绍
Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。 Flutter可以与现有的代码一起工作。在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter是完全免费、开源的。
2、flutter特点:
- **跨平台:**现在Flutter至少可以跨4种平台,甚至支持嵌入式开发。我们常用的有Linux、Android、IOS,甚至可以在谷歌最新的操作系统上Fuchsia进行运行,经过第三方扩展,甚至可以跑在MacOS和Windows上,到目前为止,Flutter算是支持平台最多的框架了,良好的跨平台性,直接带来的好处就是减少开发成本。
- 原生用户界面: 它是原生的,让我们的体验更好,性能更好。用官方的话讲就是平滑而自然的滑动效果和平台感知,为您的用户带来全新的体验。(可以看一下图片,这是Flutter的表现)
- **开源免费:**这个不用多说,我们只要学会并使用,这些都是免费的。这对于大公司是非常必要的,有人说你可以用破解版什么的....说明你还是小公司,我们公司的软件全部是正版,就更不用说操作系统和生产环境了,否则各种公司的侵权官司你都解决不了。
二、Flutter的开发环境搭建widows版
1,系统条件要求:
- **操作系统:**必须windows7以上64位操作系统。(这个一般都能很好的满足)
- **磁盘空间:**大于3个G,虽然官方说的是400M,但是你还需要安装Android Studio 和 虚拟机,所以至少要3个G左右,如果能达到5个G就更好了(满足多个虚拟机的要求)。
- 需要Git环境:Flutter需要git环境的支持,所以这个也要有,作为一个前端,这个是必备工具,所以我在文章中就不教大家安装了。
2、Java环境的安装
既然要做原生应用了,而且是基于Android的,那还是需要我们安装一下JAVA的环境的,我比一般得到一个新系统后首先做的就是这一步。这个就相当于你按一个软件,你不用考虑太多。
https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
这个地址会随着Java升级有所变化,如果已经改变了,请百度搜索java下载或者直接到Java官网下载。
3、flutter sdk的安装
a、 去官网下载flutter安装包,将安装包zip解压到你想安装Flutter SDK的路径
(如: E:\fluter\flutter;注意,不要将flutter安装到需要一些高权限的路径如C:\Program Files\,这个没必要跟我一样,凭借自己喜好设置就好)。
b、 在Flutter安装目录的flutter文件下找到flutter_console.bat,双击运行并启动flutter命令行,接下来,你就可以在Flutter命令行运行flutter命令了。
c、 配置环境变量,复制安装flutter目录下的bin目录作为配置环境变量的路径,
4、验证flutter测试:
用flutter doctor命令进行测试有以下几种情况:
C:\Users\Administrator>flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, v1.9.1+hotfix.6, on Microsoft Windows [Version 10.0.18362.418], locale zh-CN)
[√] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
[√] Android Studio (version 3.5)
[√] Connected device (1 available)
要全部是打勾才可以用;假如Android Studio是打叉那需要下载Android Studio,若Connected device是感叹号,那需要在Android studio中tools中点击avd安装虚拟机,
5、安装Android Studio:
下载Android Studio:直接到官网进行下载就可以了。下载地址:https://developer.android.com/进入后向下拉,然后看到如下界面,点击红圈处进行下载。
安装Android Studio 软件:这个安装也就是差不多下一步下一步,如果你真的不会,给你个比较详细的教程。https://www.cnblogs.com/xiadewang/p/7820377.html
打开Android Stuido 软件,然后找到Plugin的配置,搜索Flutter插件。
出现上图,点中间的Search in repositories,然后点击安装。
安装完成后,你需要重新启动一下Android Studio软件
6、安装Android证书
安装好Android Studio后,再次打开终端(命令行),输入flutter doctor,这时候的x会明显减少,但是你还是会遇到1-2个,其中有一个就是提示没有安装证书。安装证书只要在终端里执行下面的命令。
flutter doctor --android-licenses
然后会提示你选Y/N,不要犹豫,一律选择Y,就可以把证书安装好。(说的都是一大堆一大堆的英文,我也看不懂是啥)
三、flutter的基础用法
基础组件:
Center(居中布局),Container(相当于div),MaterialApp(材料布局App,基本上都用)
Scaffold(脚手组件布局)
AppBar(导航栏)
context:(上下文)
build函数是创建上下文的
Widget build(BuildContext context)
1、第一个的程序,编写一个hello world
import 'package:flutter/material.dart';//导入flutter包
void main() => runApp(new MyApp());//创建类函数
class MyApp extends StatelessWidget{//创建的函数继承于StatelessWidget,StatelessWidget是没有形状的类
@override//重构函数
Widget build(BuildContext context){ //建立组件,flutter中一切皆是组件
return MaterialApp(//返回组件app
title: 'welcome to flutter', //app名字
home: Scaffold( //组件布局Scaffold,全部都要在在这个Scaffold中编写代码
appBar: AppBar( //页面上的导航条,相当于web中的head(头部)
title: Text('welcome to flutter'), //导航条中的名字
),
body: Center( //主要身体部分
child: Text('hello world'), //center中的属性,text
),
),
);
}
}
2,Text组件
text组件是用于编辑文字,属性如下:
1)、textAlign:是用于编辑字体对齐方式
textAlign: TextAlign.start,//相当于左对齐 // textAlign: TextAlign.left,//左对齐 // textAlign: TextAlign.right,//右对齐 // textAlign: TextAlign.end,//右对齐 textAlign: TextAlign.center,//居中
2)、maxLines:由于显示页面能显示几行,值为多少就显示多少,
maxLines: 1 ,//显示限度最大行数,值只能是数字,让后随便设置数字
3)、overflow:文字溢出,
overflow: TextOverflow.clip,//字体溢出,后面多余的直接舍掉或者不显示(切掉) overflow: TextOverflow.ellipsis,//字体溢出,后面多余部分用三个点(表示省略)常用 // overflow: TextOverflow.fade,//表示字体从上到下显示到透明(隐隐约约) // overflow: TextOverflow.visible//
4)、style:用于样式,字体样式TextStyle
fontSize:字体大小
color:字体颜色,注意Color.fromARGB(a,r,g,b) a代表透明度(0-255),rgb代表红绿蓝
decoration:表示下划线,具体如下代码
style: TextStyle( fontSize: 20.0,//字体大小设置 color: Color.fromARGB(200, 255, 0, 0),//a代表透明度(0-255),rgb代表红绿蓝 decoration: TextDecoration.underline,//下划线 // decorationStyle: TextDecorationStyle.dashed,//矩形虚线 // decorationStyle: TextDecorationStyle.solid,//实线,默认 decorationStyle: TextDecorationStyle.dotted,//小虚线 ),
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; void main(){ runApp(YouApp()); } class YouApp extends StatelessWidget{ @override Widget build(BuildContext context) { return MaterialApp( title: 'welcome to flutter', home: Scaffold( appBar: AppBar( title: Text('flutter'), ),//手机上第一栏导航条 body: Center( child: Text( 'hello,我是哈哈哈,第一学习次flutter前端,这对于我来说是一门新的语言,所以我愿意把我所有的精力都投入在里面,我希望我越来越流弊', // textAlign: TextAlign.start,//相当于左对齐 // textAlign: TextAlign.left,//左对齐 // textAlign: TextAlign.right,//右对齐 // textAlign: TextAlign.end,//右对齐 textAlign: TextAlign.center,//居中 maxLines: 1 ,//显示限度最大行数,值只能是数字,让后随便设置数字 // overflow: TextOverflow.clip,//字体溢出,后面多余的直接舍掉或者不显示(切掉) overflow: TextOverflow.ellipsis,//字体溢出,后面多余部分用三个点(表示省略)常用 // overflow: TextOverflow.fade,//表示字体从上到下显示到透明(隐隐约约) // overflow: TextOverflow.visible// style: TextStyle( fontSize: 20.0,//字体大小设置 color: Color.fromARGB(200, 255, 0, 0),//a代表透明度(0-255),rgb代表红绿蓝 decoration: TextDecoration.underline,//下划线 // decorationStyle: TextDecorationStyle.dashed,//矩形虚线 // decorationStyle: TextDecorationStyle.solid,//实线,默认 decorationStyle: TextDecorationStyle.dotted,//小虚线 ), ), ), ), ); } }
补充: subtitle: Text('this is a subtitle', style: TextStyle(fontWeight: FontWeight.bold),),//副标题,字体加粗
3、container组件
container相当于web前端的div
padding:内边距设置
//padding: const EdgeInsets.all(10.0),//表示上下左右都离边距10个像素
//padding: const EdgeInsets.fromLTRB(10.0, 30.0, 10.0, 10.0),LTRB表示左(left)上(top)右(right)下(bottom),const表示设置常量关键字,其他注意语法(背)
decoration: new BoxDecoration(//装饰器盒子
gradient: const LinearGradient(//gradient界面,线性界面LinearGradient
colors: [Colors.lightBlue,Colors.lightGreen,Colors.limeAccent,Colors.lightBlue]),//添加颜色可以在里面加多或少
border:Border.all(width:5.0,color: Colors.red),//添加边框
上一段代码表示,界面背景设置,背景颜色设置好看
注意:color: Colors.lightBlue, //背景色一定要在color后面加上s
border:表示边框,具体如下代码
body: Center( child: Container( //container相当于div child: Text( 'helllo container', style: TextStyle( fontSize: 25, color: Color.fromARGB(100, 0, 0, 0), decoration: TextDecoration.underline, decorationColor: Color.fromARGB(100, 250, 0, 0), decorationStyle: TextDecorationStyle.solid, ), ), // alignment: Alignment.center, // alignment: Alignment.centerLeft, //中间左对齐 // alignment Alignment.centerRight, //中间右对齐 alignment: Alignment.topLeft, //上左对齐,topRight中间右对齐 // alignment: Alignment.bottomRight, //下右对齐,bottomLeft下左d齐 width: 400.0, height: 400.0, // color: Colors.lightBlue, //背景色一定要在color后面加上s // padding: const EdgeInsets.all(10.0),//表示上下左右都离边距10个像素 padding: const EdgeInsets.fromLTRB(10.0, 30.0, 10.0, 10.0), margin: const EdgeInsets.all(10.0), //边框里外界的距离 decoration: new BoxDecoration(//装饰器盒子 gradient: const LinearGradient(//gradient界面,线性界面LinearGradient colors: [Colors.lightBlue,Colors.lightGreen,Colors.limeAccent,Colors.lightBlue]),//添加颜色可以在里面加多或少 border:Border.all(width:5.0,color: Colors.red),//添加边框 ), ), ),
具体内容见下代码练习:
import 'package:flutter/material.dart'; void main() => runApp(new YourApp()); class YourApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'welcome study container', home: Scaffold( appBar: AppBar( title: Text( 'welcome study container', textAlign: TextAlign.right, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 25, color: Color.fromARGB(255, 0, 0, 255), decoration: TextDecoration.lineThrough, decorationStyle: TextDecorationStyle.solid, decorationColor: Color.fromARGB(255, 200, 0, 0)), ), ), body: Center( child: Container( //container相当于div child: Text( 'helllo container', style: TextStyle( fontSize: 25, color: Color.fromARGB(100, 0, 0, 0), decoration: TextDecoration.underline, decorationColor: Color.fromARGB(100, 250, 0, 0), decorationStyle: TextDecorationStyle.solid, ), ), // alignment: Alignment.center, // alignment: Alignment.centerLeft, //中间左对齐 // alignment Alignment.centerRight, //中间右对齐 alignment: Alignment.topLeft, //上左对齐,topRight中间右对齐 // alignment: Alignment.bottomRight, //下右对齐,bottomLeft下左d齐 width: 400.0, height: 400.0, // color: Colors.lightBlue, //背景色一定要在color后面加上s // padding: const EdgeInsets.all(10.0),//表示上下左右都离边距10个像素 padding: const EdgeInsets.fromLTRB(10.0, 30.0, 10.0, 10.0), margin: const EdgeInsets.all(10.0), //边框里外界的距离 decoration: new BoxDecoration(//装饰器盒子 gradient: const LinearGradient(//gradient界面,线性界面LinearGradient colors: [Colors.lightBlue,Colors.lightGreen,Colors.limeAccent,Colors.lightBlue]),//添加颜色可以在里面加多或少 border:Border.all(width:5.0,color: Colors.red),//添加边框 ), ), ), ), ); } }
4、Image组件
image属性是图片的插入和布局,
Image.network是在网上链接自己想要的图片
image.asset实在项目包里直接调用自己的图片
image.file是在在计算机上调用绝对路径
child: new Image.network(//从网络上的地址,常用 // child: new Image.file()//绝对路径文件图片 // child: new Image.asset()//从项目包里打开文件 'http://pic1.win4000.com/pic/9/35/e9babcbf13.jpg',)
对图片样式的修饰属性为 fit(填充),color(颜色),colorBlendMode(颜色混合属性,如黑白,复古),repeat(图片重复填充样式)
注意:混合属性要和颜色一起用才有混合效果。
样式意义如下:
fit:BoxFit.contain,) //不会处理,保留原图片的比例 // fit:BoxFit.cover,)//不变形,但是图片被裁切了 // fit:BoxFit.fitWidth ,),//横向填充 // fit:BoxFit.fitHeight ,),//纵向填充 // fit:BoxFit.scaleDown ,),//不会该表原图片的大小 fit:BoxFit. fill,//填满宽 color: Colors.lightBlue,//给图片加背景颜色,覆盖性的添加,若想看见图片,就必须加colorBlendMode混合属性一起用 colorBlendMode: BlendMode.darken,//colorBlendMode混合属性,BlendMode有很多属性,这个要配合颜色来配合 // repeat: ImageRepeat.repeatX,//横向重复 // repeat: ImageRepeat.repeatY,//纵向重复 // repeat: ImageRepeat.noRepeat,//不重复 repeat: ImageRepeat.repeat,//重复
代码示例:
import'package:flutter/material.dart'; void main()=>runApp(MyApp()); class MyApp extends StatelessWidget{ @override Widget build(BuildContext context){ return MaterialApp( title: 'app', home: Scaffold( appBar: AppBar( title: Text('image图片布局'), ), body:Container( alignment: Alignment.center, width:500.0, height: 600.0, padding: const EdgeInsets.all(10.0), margin: const EdgeInsets.all(5.0), decoration: BoxDecoration( border: Border.all(width: 2.0,color: Colors.black,style: BorderStyle.solid), gradient: const LinearGradient( colors:[Colors.lightBlue,Colors.lightGreen]), ), child: new Image.network(//从网络上的地址,常用 // child: new Image.file()//绝对路径文件图片 // child: new Image.asset()//从项目包里打开文件 'http://pic1.win4000.com/pic/9/35/e9babcbf13.jpg', // fit:BoxFit.contain,) //不会处理,保留原图片的比例 // fit:BoxFit.cover,)//不变形,但是图片被裁切了 // fit:BoxFit.fitWidth ,),//横向填充 // fit:BoxFit.fitHeight ,),//纵向填充 // fit:BoxFit.scaleDown ,),//不会该表原图片的大小 fit:BoxFit. fill,//填满宽 color: Colors.lightBlue,//给图片加背景颜色,覆盖性的添加,若想看见图片,就必须加colorBlendMode混合属性一起用 colorBlendMode: BlendMode.darken,//colorBlendMode混合属性,BlendMode有很多属性,这个要配合颜色来配合 // repeat: ImageRepeat.repeatX,//横向重复 // repeat: ImageRepeat.repeatY,//纵向重复 // repeat: ImageRepeat.noRepeat,//不重复 repeat: ImageRepeat.repeat,//重复 ), ), ), ); } }
5、ListView Widget (列表组件)
leading:表示图标或这图片在文本之前显示
new ListTile( leading: new Icon(Icons.access_alarm ),//flutter中的小图标,leading是在前面加小图标,在后面加的是trailing,见下代码 title: Text('access_alarm'),//图标名 ), //插入图片 new ListTile( leading: CircleAvatar( //圆形头像填充 backgroundImage: NetworkImage('https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3120914462,2909377693&fm=26&gp=0.jpg'),//图片填充及来源 ), title: Text('horse'),//标题 ),
trailing:表示图片或者图标在文本之后显示
icon:表示图标
trailing: Icon(Icons.airline_seat_legroom_extra),//图标在挑剔后面不显示
示例代码如下:
示例代码注意:一般列表默认是纵下列表(向下滑动的)
横向列表以及嵌套:
横向列表关键语句:scrollDirection: Axis.horizontal, //横向的列表初始化(horizontal)横向的意思
示例代码如下:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( title: 'luodehua DemoApp', home: Scaffold( appBar: AppBar( title: Text('ListView2横向'), ), body: Center( child: Container( height: 200, child: new ListView(//MyList就是继承ListView,在下一段调用 scrollDirection: Axis.horizontal,//横向的列表初始化(horizontal)横向的意思 children: <Widget>[ new Container( width: 100.0, height: 50.0, child: new Text('one', textAlign:TextAlign.center,), color: Colors.red, ), new Container( width: 100.0, height: 50.0, child: new Text('two', textAlign:TextAlign.center,), color: Colors.blue, ), new Container( width: 100.0, height: 50.0, child: new Text('three', textAlign:TextAlign.center,), color: Colors.yellow, ), new Container( width: 200.0, height: 50.0, child: new Text('four', textAlign:TextAlign.center, style: TextStyle(color: Colors.red,),), color: Colors.black, ), ], ), ), ), ), ); } }
与没嵌套对比:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( title: 'luodehua DemoApp', home: Scaffold( appBar: AppBar( title: Text('ListView2横向'), ), body: Center( child: Container( height: 200, child: new MyList(//MyList就是继承ListView,在下一段调用 // scrollDirection: Axis.horizontal,//横向的列表初始化(horizontal)横向的意思 // children: <Widget>[ // new Container( // width: 100.0, // height: 50.0, // child: new Text('one', textAlign:TextAlign.center,), // color: Colors.red, // ), // new Container( // width: 100.0, // height: 50.0, // child: new Text('two', textAlign:TextAlign.center,), // color: Colors.blue, // ), // new Container( // width: 100.0, // height: 50.0, // child: new Text('three', textAlign:TextAlign.center,), // color: Colors.yellow, // ), // new Container( // width: 200.0, // height: 50.0, // child: new Text('four', // textAlign:TextAlign.center, // style: TextStyle(color: Colors.red,),), // color: Colors.black, // ), // ], ), ), ), ), ); } } //利用嵌套 class MyList extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return ListView( scrollDirection: Axis.horizontal, //横向的列表初始化(horizontal)横向的意思 children: <Widget>[ new Container( width: 100.0, height: 50.0, child: new Text( '1', textAlign: TextAlign.center, ), color: Colors.red, ), new Container( width: 100.0, height: 50.0, child: new Text( '2', textAlign: TextAlign.center, ), color: Colors.blue, ), new Container( width: 100.0, height: 50.0, child: new Text( '3', textAlign: TextAlign.center, ), color: Colors.yellow, ), new Container( width: 200.0, height: 50.0, child: new Text( '4', textAlign: TextAlign.center, style: TextStyle( color: Colors.red, ), ), color: Colors.black, ), ], ); } }
难度较大部分列表动态传参:
代码如下:需学习
import 'package:flutter/material.dart'; void main()=>runApp(MyApp( items: new List<String>.generate(100, (i)=>'Item $i')//传进去的动态参数,为字符串类型,generate(意思是生成100个,后面为命名函数(自定义)) // List<String>.generate(length,generator)//length为要传的长度,generator是命名函数 )); class MyApp extends StatelessWidget{ final List<String> items;//items是要传的参数,形参 MyApp({Key key,@required this.items}):super(key:key);//this.items是上面items,@required表示参数是必须传的,super继承 @override Widget build(BuildContext context) { return MaterialApp( title: 'ListView Widget', home: Scaffold( appBar: AppBar( title: Text('welcome study ListView'), ), body:ListView.builder(itemCount: items.length,//builder是动态传参,length为上面的长度100 itemBuilder: (context,index){ return new ListTile( title: new Text('${items[index]}'),//传的实参写法 ); },), ), ); } }
这是构造函数传参进去
MyApp({Key key,@required this.items}):super(key:key);//主函数传参进去接受参数
数据是我们用List.generate生成
接受数据后用ListView.builder方法,作了一个根据传递参数数据形成的动态列表。
6、网格列表组件(gridView widget)
网格组件用于较多于相册布局,
属性:
crossAxisSpacing: 2.0,//横轴网格之间的空格
mainAxisSpacing:2.0,//纵轴网格之间的距离
crossAxisCount: 3,//每一行显示多少列
SliverGridDelegateWithFixedCrossAxisCount(用于固定列数的场景)
SliverGridDelegateWithMaxCrossAxisExtent:用于子元素有最大宽度限制的场景;
childAspectRatio:1,//childAspectRatio(宽高比例,值为2时相当于宽是高的2倍)
用两种方法分别写:
第一种传统写法:
body: GridView.count( padding: const EdgeInsets.all(2.0), crossAxisSpacing: 2.0,//横轴网格之间的空格 mainAxisSpacing:2.0,//纵轴网格之间的距离 crossAxisCount: 3,//每一行显示多少列 children: <Widget>[ new Image.network('http://pic27.nipic.com/20130313/9252150_092049419327_2.jpg'), new Image.network('http://b-ssl.duitang.com/uploads/item/201208/30/20120830173930_PBfJE.jpeg'), new Image.network('http://b-ssl.duitang.com/uploads/blog/201312/04/20131204184148_hhXUT.jpeg'), new Image.network('http://pic27.nipic.com/20130324/9252150_152129329000_2.jpg'), new Image.network('http://img.52z.com/upload/news/image/20180621/20180621055734_59936.jpg'), new Image.network('http://img.52z.com/upload/news/image/20180621/20180621055734_59936.jpg'), new Image.network('http://img.52z.com/upload/news/image/20180621/20180621055734_59936.jpg'), new Image.network('http://img.52z.com/upload/news/image/20180621/20180621055734_59936.jpg'), new Image.network('http://img.52z.com/upload/news/image/20180621/20180621055734_59936.jpg'), ],
第二种写法:
body: GridView( gridDelegate:SliverGridDelegateWithFixedCrossAxisCount( //gridDelegate如何控制排列子元素的一个委托,SliverGridDelegateWithFixedCrossAxisCount(用于固定列数的场景) //SliverGridDelegateWithFixedCrossAxisCount(用于固定列数的场景) //SliverGridDelegateWithMaxCrossAxisExtent:用于子元素有最大宽度限制的场景; //crossAxisCount:列数,即一行有几个子元素; //mainAxisSpacing:主轴方向上的空隙间距; //crossAxisSpacing:次轴方向上的空隙间距; //childAspectRatio:子元素的宽高比例。 crossAxisCount: 3, crossAxisSpacing: 2.0, mainAxisSpacing: 2.0, childAspectRatio:1,//childAspectRatio(宽高比例,值为2时相当于宽是高的2倍) ) , children: <Widget>[ new Image.network('http://pic27.nipic.com/20130313/9252150_092049419327_2.jpg',fit: BoxFit.cover), new Image.network('http://b-ssl.duitang.com/uploads/item/201208/30/20120830173930_PBfJE.jpeg',fit: BoxFit.cover), new Image.network('http://b-ssl.duitang.com/uploads/blog/201312/04/20131204184148_hhXUT.jpeg',fit: BoxFit.cover), new Image.network('http://pic27.nipic.com/20130324/9252150_152129329000_2.jpg',fit: BoxFit.cover), new Image.network('http://img.52z.com/upload/news/image/20180621/20180621055734_59936.jpg',fit: BoxFit.cover), new Image.network('http://img.52z.com/upload/news/image/20180621/20180621055734_59936.jpg',fit: BoxFit.cover), new Image.network('http://img.52z.com/upload/news/image/20180621/20180621055734_59936.jpg',fit: BoxFit.cover), new Image.network('http://img.52z.com/upload/news/image/20180621/20180621055734_59936.jpg',fit: BoxFit.cover), new Image.network('http://img.52z.com/upload/news/image/20180621/20180621055734_59936.jpg',fit: BoxFit.cover), ], ),
这两种写法都可以,主要看你个人习惯爱好,
完整代码示例(以图片相册为例)
//网格列表 import 'package:flutter/material.dart'; void main()=>runApp(YourApp()); class YourApp extends StatelessWidget{ @override Widget build(BuildContext context){ return MaterialApp( title: 'GridView', home: Scaffold( appBar: AppBar( title: Text('welcome study GridView'), ), // body: GridView.count( // padding: const EdgeInsets.all(2.0), // crossAxisSpacing: 2.0,//横轴网格之间的空格 // mainAxisSpacing:2.0,//纵轴网格之间的距离 // crossAxisCount: 3,//每一行显示多少列 // children: <Widget>[ // new Image.network('http://pic27.nipic.com/20130313/9252150_092049419327_2.jpg'), // new Image.network('http://b-ssl.duitang.com/uploads/item/201208/30/20120830173930_PBfJE.jpeg'), // new Image.network('http://b-ssl.duitang.com/uploads/blog/201312/04/20131204184148_hhXUT.jpeg'), // new Image.network('http://pic27.nipic.com/20130324/9252150_152129329000_2.jpg'), // new Image.network('http://img.52z.com/upload/news/image/20180621/20180621055734_59936.jpg'), // new Image.network('http://img.52z.com/upload/news/image/20180621/20180621055734_59936.jpg'), // new Image.network('http://img.52z.com/upload/news/image/20180621/20180621055734_59936.jpg'), // new Image.network('http://img.52z.com/upload/news/image/20180621/20180621055734_59936.jpg'), // new Image.network('http://img.52z.com/upload/news/image/20180621/20180621055734_59936.jpg'), // ], body: GridView( gridDelegate:SliverGridDelegateWithFixedCrossAxisCount( //gridDelegate如何控制排列子元素的一个委托,SliverGridDelegateWithFixedCrossAxisCount(用于固定列数的场景) //SliverGridDelegateWithFixedCrossAxisCount(用于固定列数的场景) //SliverGridDelegateWithMaxCrossAxisExtent:用于子元素有最大宽度限制的场景; //crossAxisCount:列数,即一行有几个子元素; //mainAxisSpacing:主轴方向上的空隙间距; //crossAxisSpacing:次轴方向上的空隙间距; //childAspectRatio:子元素的宽高比例。 crossAxisCount: 3, crossAxisSpacing: 2.0, mainAxisSpacing: 2.0, childAspectRatio:1,//childAspectRatio(宽高比例,值为2时相当于宽是高的2倍) ) , children: <Widget>[ new Image.network('http://pic27.nipic.com/20130313/9252150_092049419327_2.jpg',fit: BoxFit.cover), new Image.network('http://b-ssl.duitang.com/uploads/item/201208/30/20120830173930_PBfJE.jpeg',fit: BoxFit.cover), new Image.network('http://b-ssl.duitang.com/uploads/blog/201312/04/20131204184148_hhXUT.jpeg',fit: BoxFit.cover), new Image.network('http://pic27.nipic.com/20130324/9252150_152129329000_2.jpg',fit: BoxFit.cover), new Image.network('http://img.52z.com/upload/news/image/20180621/20180621055734_59936.jpg',fit: BoxFit.cover), new Image.network('http://img.52z.com/upload/news/image/20180621/20180621055734_59936.jpg',fit: BoxFit.cover), new Image.network('http://img.52z.com/upload/news/image/20180621/20180621055734_59936.jpg',fit: BoxFit.cover), new Image.network('http://img.52z.com/upload/news/image/20180621/20180621055734_59936.jpg',fit: BoxFit.cover), new Image.network('http://img.52z.com/upload/news/image/20180621/20180621055734_59936.jpg',fit: BoxFit.cover), ], ), ), ); } }
7、stack widget(层叠组件)
层叠就是把几个组件叠在一起,主要属性
alingnment属性是
alignment: FractionalOffset(0.5, 0.8), //里面参数为x轴,y轴,参数值范围时0~1,相对与Widget
代码示例:
//stack层叠组件 import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { //放在build函数中定义变量 var stack = Stack( //用变量代表组件,下面直接调用变量即可。 alignment: FractionalOffset(0.5, 0.8), //里面参数为x轴,y轴,参数值范围时0~1,相对与Widget children: <Widget>[ new CircleAvatar( radius: 100.0, //圆形形状百分之百 backgroundImage: new NetworkImage( 'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=745669936,3962756491&fm=26&gp=0.jpg',), ), new Container( decoration: BoxDecoration( border: Border.all( width: 2.0, color: Colors.red, style: BorderStyle.solid), color: Colors.lightBlue, ), padding: const EdgeInsets.all(10.0), child: Text('我好看'), ), ], ); // TODO: implement build return MaterialApp( title: 'Column widget', home: Scaffold( appBar: AppBar( title: Text('Column widget'), ), body: Center( //直接调用stack变量。 child: stack, ), ), ); } }
8、positioned widget(位置组件)
属性就是:
left:左
top:上
bottom:下
right:右
可根据这写属性调组件的位置布局。
//positioned位置布局 import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { var stack = Stack( // alignment: FractionalOffset(0.5 ,0.1), children: <Widget>[ new CircleAvatar( radius: 100.0, backgroundImage: NetworkImage( 'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=745669936,3962756491&fm=26&gp=0.jpg'), ), new Positioned( left: 70, top: 110, child: MyContainer(), ), new Positioned(//位置布局 child: MyText(), left: 50.0, top: 150.0, ) ], ); return MaterialApp( title: 'Positioned Widget', home: Scaffold( appBar: AppBar( title: Text('Position study'), ), body: Center( child: stack, ), ), ); } } class MyContainer extends StatelessWidget { @override Widget build(BuildContext context) { return new Container( padding: const EdgeInsets.all(10.0), child: Text( '我好看', style: TextStyle( color: Colors.red, fontWeight: FontWeight.bold, ), ), decoration: BoxDecoration( color: Colors.lightGreen, ), ); } } class MyText extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return Text( '加微信聊天', style: TextStyle( fontSize: 20.0, fontWeight: FontWeight.bold, fontFamily: 'yahei', color: Colors.blue), ); } }
9、Row widget(水平方向布局组件)
属性:
children <Widget>[ ]:相当于这是一个组件数组,【】里面可以全放组件。
children: <Widget>[ new Expanded( //expanded是灵活性的,可以根据具体信息平均分配,合适Android页面大小,可灵活运用expand来调整按钮的大小。 child: RaisedButton( onPressed: (){}, color:Colors.red , child: Text('red Button'), ), ), Expanded( child:RaisedButton( onPressed: (){},//点击按钮时触发的事件,或者要执行的任务。 color:Colors.lightBlue , child: Text('blue Button'), ), ), Expanded( child: RaisedButton( onPressed: (){}, color:Colors.lightGreen , child: Text('Green button'), ), ), ],
crossAxisAlignment:水平方向的对齐方式(start(开始对齐(左)),end(末尾对齐(右)), center(居中,默认))
crossAxisAlignment:CrossAxisAlignment.start,
Expanded widget:是灵活组件,根据具体信息平均分配空间布局大小
组件实例如上
实例代码:
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; void main()=>runApp(MyApp()); class MyApp extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( title: 'Row widget', home: Scaffold( appBar: AppBar( title: Text('Row widget'), ), body: Row( //水平组件布局 crossAxisAlignment:CrossAxisAlignment.start, children: <Widget>[ new Expanded( //expanded是灵活性的,可以根据具体信息平均分配,合适Android页面大小,可灵活运用expand来调整按钮的大小。 child: RaisedButton( onPressed: (){}, color:Colors.red , child: Text('red Button'), ), ), Expanded( child:RaisedButton( onPressed: (){},//点击按钮时触发的事件,或者要执行的任务。 color:Colors.lightBlue , child: Text('blue Button'), ), ), Expanded( child: RaisedButton( onPressed: (){}, color:Colors.lightGreen , child: Text('Green button'), ), ), ], ), ), ); } }
10、Column Widget(垂直方向布局组件)
属性:
crossAxisAlignment:CrossAxisAlignment.center ,//对齐方式布局center为默认布局,他是相对与字体的长度对齐(水平方向副轴)
mainAxisAlignment: MainAxisAlignment.center,//垂直方向为主轴
同上,有children<Widget>[]
注意:区分主轴和副轴的,如果是水平方向主轴就是横向的,如果是垂直方向的布局,主轴就是纵轴。
实例代码:
//垂直方向上的组件 import 'package:flutter/material.dart'; void main()=>runApp(MyApp()); class MyApp extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( title: 'Column widget', home: Scaffold( appBar: AppBar( title: Text('Column widget'), ), body: Center( //在中间显示居中 child:Column( //水平组件布局 crossAxisAlignment:CrossAxisAlignment.center ,//对齐方式布局center为默认布局,他是相对与字体的长度对齐(水平方向副轴) mainAxisAlignment: MainAxisAlignment.center,//垂直方向为主轴 children: <Widget>[ Expanded( //灵活组件布局Expanded平均分配页面的比例,假如没加Expanded时不灵活布局 child:Text('Welcome to flutter'), ), Text('my name is haha'),//不灵活布局 Expanded( child:Text('i love web'),//灵活布局 ), Text('i like flutter'),//不灵活布局 Text('Welcome study Column'),//不灵活布局 ], ), ), ), ); } }
11、Card widget(卡片组件)
卡片:就是设置个人卡片或者
举例卡片和Column(垂直方向布局),列表瓦片(ListTile)混合用
代码示例:
//卡片布局 import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { var card = new Card( //卡片布局 child: new Column( children: <Widget>[ new ListTile( //列表瓦片 title: Text('姓名:李大妹',style: TextStyle(color:Colors.yellow,fontWeight: FontWeight.bold,),), subtitle: Text('电话:123466789',style: TextStyle(fontWeight: FontWeight.bold),), leading: Image.network('https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=745669936,3962756491&fm=26&gp=0.jpg'), trailing: Icon(Icons.account_box), ), new Divider( //分割线 height: 20.0, color: Colors.pink,),// new ListTile( title: Text('姓名:李二妹',style: TextStyle(color:Colors.red,fontWeight: FontWeight.bold,),), subtitle: Text('电话:123466789',style: TextStyle(fontWeight: FontWeight.bold),), leading: Image.network('https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3017568428,4147600612&fm=26&gp=0.jpg'), trailing: Icon(Icons.account_box), ), new Divider( height: 20.0, color: Colors.pink,), new ListTile( title: Text('姓名:李三妹',style: TextStyle(color:Colors.lightBlue,fontWeight: FontWeight.bold,),), subtitle: Text('电话:123466789',style: TextStyle(fontWeight: FontWeight.bold),), leading: Image.network('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1575026182108&di=e69c3887d571ba4e5166fbe03893976c&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201708%2F02%2F20170802084658_UViBH.jpeg'), trailing: Icon(Icons.account_box), ), new Divider( height: 20.0, color: Colors.pink,), new ListTile( title: Text('姓名:李老大',style: TextStyle(color:Colors.black,fontWeight: FontWeight.bold,),), subtitle: Text('电话:123466789',style: TextStyle(fontWeight: FontWeight.bold),), leading: Image.network('https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1838136197,2974424351&fm=26&gp=0.jpg'), trailing: Icon(Icons.account_box), ), new Divider( height: 20.0, color: Colors.pink, ), new ListTile( title: Text('姓名:李老幺',style: TextStyle(color:Colors.lightGreenAccent,fontWeight: FontWeight.bold,),), subtitle: Text('电话:123466789',style: TextStyle(fontWeight: FontWeight.bold),), leading: Image.network('https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3722902562,2037055323&fm=26&gp=0.jpg'), trailing: Icon(Icons.account_box), ), ], ), ); return MaterialApp( title: 'Card Widget', home: Scaffold( appBar: AppBar( title: Text('Card study'), ), body: Center( child: card, ), ), ); } }
12、Dviider Widget(分割线组件)
用于分割组件与组件
常用属性:
height:高度
color:颜色
具体代码:
new ListTile( title: Text('姓名:李三妹',style: TextStyle(color:Colors.lightBlue,fontWeight: FontWeight.bold,),), subtitle: Text('电话:123466789',style: TextStyle(fontWeight: FontWeight.bold),), leading: Image.network('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1575026182108&di=e69c3887d571ba4e5166fbe03893976c&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201708%2F02%2F20170802084658_UViBH.jpeg'), trailing: Icon(Icons.account_box), ), new Divider( height: 20.0, color: Colors.pink,), new ListTile( title: Text('姓名:李老大',style: TextStyle(color:Colors.black,fontWeight: FontWeight.bold,),), subtitle: Text('电话:123466789',style: TextStyle(fontWeight: FontWeight.bold),), leading: Image.network('https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1838136197,2974424351&fm=26&gp=0.jpg'), trailing: Icon(Icons.account_box), ),
13、RaisedButton Widget(凸起按钮组件)
一般所有的组件都有(触发事件属性)
属性:
onpressed(出发事件)
onPressed: (){ //Navigator是对象属性,就是我们要跳转的页面 Navigator.push(context, MaterialPageRoute( //MaterialPageRoute路由组件,搜索,push是跳转到下一页 builder:(context)=>new Secend(),//第二页面自定义类Secend, builder建一个函数 ), );//Navigator },
onpressed:(){};大括号里有常用属性,Navigator是一个导航器,有两个导航方式:Navigator.push(context,Route),Navigator.pop(context)
Navigator.push(context,Route)第一个参数是上下文,第二个参数是路由导航属性(MaterialPageRoute),它里面又包含了一个builder属性,builder是一个匿名函数生成器。
一般导航页面时利用按钮中的触发事件onPressed来实现的,
代码示例:
//导航页面跳转 import 'package:flutter/material.dart'; void main(){ runApp(MaterialApp( title: 'dao hang ye mian', home:First(),//第一页自定义类First )); } class First extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar( title: Text('首页'), ), body: RaisedButton( //凸起按钮 child: Text('下一页'), onPressed: (){ //Navigator是对象属性,就是我们要跳转的页面 Navigator.push(context, MaterialPageRoute( //MaterialPageRoute路由组件,搜索,push是跳转到下一页 builder:(context)=>new Secend(),//第二页面自定义类Secend, builder建一个函数 ), );//Navigator }, ), ); } } class Secend extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( title: '第二页', home: Scaffold( appBar: AppBar( title: Text('第二页'), ), body: RaisedButton( child: Text('返回上一页'), onPressed: (){ Navigator.pop(context);//pop是跳转到上一页 }, ), ), ); } }
动态传参导航页面代码示例,
假如看不懂就看视频解析:技术胖的flutter基础教程,
https://search.bilibili.com/all?keyword=flutter&from_source=banner_search
或者搜索
http://www.jspang
代码示例
import 'package:flutter/material.dart'; //自定义对象 class ProDuct{ //数据类型的声明 final String title;//商品列表, 参数 final String productinfo;//商品信息, 参数 ProDuct(this.title,this.productinfo);//构造函数,传参 } //类入口 void main(){ runApp(MaterialApp( title: 'daohanlist', home: ProDuctList( //自定义入口名 //generate是传参生成器, produrcts:List.generate(10,(i)=>ProDuct('商品属性 $i','这是一个商品属性,编号为:$i'))//给类传参,produrcts自定义属性变量 ), ), ); } class ProDuctList extends StatelessWidget{ final List<ProDuct> produrcts;//变量 ProDuctList({Key key,@required this.produrcts}):super(key:key);//声明传参 @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar( title: Text('商品列表'), ), body: ListView.builder( //动态传参一般用ListView.builder itemCount: produrcts.length,//第一个参数 itemBuilder: (context,index){ //匿名函数的参数,(上下文context,索引index) return ListTile( title: Text(produrcts[index].title),//显示各一个参数 onTap: (){ Navigator.push(context,MaterialPageRoute( builder: (context)=>new Productinfo(produrct:produrcts[index]),//相当于produrct就是produrcts[index],两个是相同的值相同对象 )); },//点击右响应事件 ); } ), ); } } class Productinfo extends StatelessWidget{ final ProDuct produrct;//定义一个ProDuct类中的示例对象produrct Productinfo({Key key,@required this.produrct}):super(key:key);//super关键字是代表继承父类 @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar( title: Text('${produrct.title}'),//传ProDuct中的参数 ), body: Container( height: 500.0, color: Colors.pink, child:Center(child:Text('${produrct.productinfo}'),),),//传ProDuct中的商品信息,变量名已在上诉声明 ); } }
带有返回数据的导航页面
Dart中的异步请求和等待和ES6中的方法很像,直接使用async...await就可以实现。比如下面作了一个的方法,
然后进行跳转,注意这时候是异步的。等待结果回来之后,我们再显示出来内容。具体代码如下:
代码:
_NavigateToxiaojiejie(BuildContext context) async{ //async 表示异步, _NavigateToxiaojiejie自定义内部函数(下划线开头),也是要接受上下文
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context)=> SecendPage(),
),
);
Scaffold.of(context).showSnackBar( //展示返回的数据
SnackBar( //重要
content: Text('$result'),
),
);//snackBar是scaffold中的属性,(showSnackBar)就是接受子页面返回的信息
}
}
SnackBar的使用
SnackBar是用户操作后,显示提示信息的一个控件,类似Tost,会自动隐藏。
SnackBar是以Scaffold的showSnackBar方法来进行显示的。
Scaffold.of(context).showSnackBar( //展示返回的数据
SnackBar( //重要
content: Text('$result'),
),
);//snackBar是scaffold中的属性,(showSnackBar)就是接受子页面返回的信息
返回数据的方式
返回数据其实是特别容易的,只要在返回时带第二个参数就可以了。context之后的就是第二个参数。
Navigator.pop(context,'腰围3cm,电话号码:123453');
代码示例:
import 'package:flutter/material.dart'; import 'package:flutter_app/%E5%AF%BC%E8%88%AA%E9%A1%B5%E9%9D%A21.dart'; void main(){ runApp(MaterialApp( title: 'yi bu daohang', home: FistPage(), )); } class FistPage extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text('找人'),), body: Center( child: NextButton(), ), ); } } class NextButton extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return RaisedButton( onPressed: (){_NavigateToxiaojiejie(context);},// _NavigateToxiaojiejie自定义的函数,context相当于类中的默认self child: Text('小姐姐信息',style: TextStyle( color: Colors.red,fontWeight: FontWeight.bold,fontSize: 20.0, ),), ); } _NavigateToxiaojiejie(BuildContext context) async{ //async 表示异步, _NavigateToxiaojiejie自定义内部函数(下划线开头),也是要接受上下文 final result = await Navigator.push( context, MaterialPageRoute( builder: (context)=> SecendPage(), ), ); Scaffold.of(context).showSnackBar( //展示返回的数据 SnackBar( //重要 content: Text('$result'), ), );//snackBar是scaffold中的属性,(showSnackBar)就是接受子页面返回的信息 } } class SecendPage extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar( title: Text('小姐姐信息'), ), body: Column( children: <Widget>[ RaisedButton( child: Text('长腿小姐姐'), onPressed: (){ Navigator.pop(context,'大腿长2米,电话号码:46466577'); }, ), RaisedButton( child: Text('小蛮腰小姐姐'), onPressed: (){ Navigator.pop(context,'腰围3cm,电话号码:123453'); }, ), ], ), ); } }
四、静态资源和项目图片的处理
pubspec.yaml 文件
如果想配置项目资源文件,就需要使用pubspec.yaml文件,需要把资源文件在这里声明。
比如在项目根目录下新建了一个image文件夹,文件夹下面放了一个图片,图片的名称叫做00.jpg,
那我们在pubspec.yaml文件里就要写如下代码进行声明。
assets:
- image/00.jpg
使用项目图片资源
有了声明后,我们就可以直接在项目中引用这个文件了。这里使用最简单的代码结构,只用了一张图片。代码如下:
import 'package:flutter/material.dart'; void main(){ runApp(MyApp()); } class MyApp extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( title: 'hello world', home: Scaffold( appBar: AppBar( title: Text('image'), ), body: Image.asset('image/00.jpg'),//这里就是从项目包里打开文件 ), ); } }
Flutter客户端打包
到现在为止,我相信小伙伴都能做出一些漂亮的页面了,也有了难道朋友面前显一显的冲动。想要安装到手机上,我们必须要进行打包,这节课我们就学学Android客户端如何打包apk。
看视屏
http://www.jspang.com/posts/2019/02/01/flutter-base4.html
配置APP的图标
想配置APP的图片,你需要找到下面的目录:
项目根目录/android/app/src/main/res/
进入之后你会看到很多mipmap-为前缀命名的文件夹,后边的是像素密度,可以看出图标的分辨率。
- mdpi (中) ~160dpi
- hdpi (高) ~240dip
- xhdpi (超高) ~320dip
- xxhdpi (超超高) ~480dip
- xxxhdpi (超超超高) ~640dip
将对应像素密度的图片放入对应的文件夹中,图片记得用png格式,记得名字要统一,才能一次性进行配置。
#AndroidManifest.xml 文件
这个文件主要用来配置APP的名称、图标和系统权限,所在的目录在:
项目根目录/android/app/src/main/AndroidManifest.xml
生成 keystore
这里的坑挺多的,小伙伴一定要注意。官方写的非常简单,只要在终端运行如下代码就可以成功,但事实是报错。
下代码官方写:
keytool -genkey -v -keystore ~/key.jks -keyalg RSA -keysize 2048 -validity 10000 -alia
出现这种错误,见链接
http://jspang.com/static/upload/20181217/5xuQKZ9PfP4YniYgWcNR53j4.png
根本找不到这个目录,真的很坑,其实我们只是没有配置环境变量。但是为了一个包配置环境变量是不知道的。
这时候可以用下面的命令找到keytool.exe的位置。
在你的终端运行如下代码:
flutter doctor -v
我的是在webstorm软件终端下,假如是在vs code软件下就在vs code终端运行,
http://jspang.com/static/upload/20181217/cHrgJ3SdAqFk1Ivta82vIP7g.png
这时候你直接拷贝命令并进行输入,但这里也有个坑,就是如果文件夹中间带有空空,你需要用带引号扩上。
D:\Program\Android\'Android Studio'\jre\bin\keytool -genkey -v -keystore ~/key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key
这就可以了吗?那你就太天真了,还是会报错。
http://jspang.com/static/upload/20181217/7dTEF6Pshi4qew0-p-MWXNtX.png
这个错误的主要问题是目录不存在和没有写权限,所以我们要更换一个有写权限的目录。我们把命令改成了下面的形式。
D:\Program\Android\'Android Studio'\jre\bin\keytool -genkey -v -keystore D:\key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key
这时候就可以创建成功了。你的D盘下面就会有一个Jks的文件,记住这个文件不能共享给任何人。
有了这个key.jks文件后,可以到项目目录下的android文件夹下,创建一个名为key.properties的文件,并打开粘贴下面的代码。
storePassword=<password from previous step> //输入上一步创建KEY时输入的 密钥库 密码
keyPassword=<password from previous step> //输入上一步创建KEY时输入的 密钥 密码
keyAlias=key
storeFile=<E:/key.jks> //key.jks的存放路径
我的文件最后是这样的:
storePassword=123123
keyPassword=123123
keyAlias=key
storeFile=D:/key.jks
这个工作中也不要分享出去哦,这个Key就算生成成功了。
#配置key注册
key生成好后,需要在build.gradle文件中进行配置。这个过程其实很简单,就是粘贴复制一些东西,你是不需要知道这些文件的具体用处的。
第一项:
进入项目目录的/android/app/build.gradle文件,在android{这一行前面,加入如下代码:
def keystorePropertiesFile = rootProject.file("key.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
把如下代码进行替换
buildTypes {
release {
signingConfig signingConfigs.debug
}
}
替换成的代码:
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
#生成apk
直接在终端中输入:
flutter build apk
这时候就打包成功了,剩下的安装过程我就省略,不作过多的介绍了。
安装过程见视频:
https://www.bilibili.com/video/av35800108?p=25
浙公网安备 33010602011771号