Flutter框架概览
Flutter是什么
- 是一款移动应用程序SDK,一份代码可以同时生成ios和Android两个高性能、高保真的应用程序
- 目标是使开发人员能够交付在不同平台上都感觉自然流畅的高性能应用程序。我们兼容滚动行为,排版,图标等方面的差异
Flutter优势
- 跨平台:至少可以跨4种平台,甚至支持嵌入式开发,常用的有(Linux,Android,IOS,甚至可以在谷歌最新的操作系统上Fuchsia进行运行),经过第三方扩展,甚至可以跑在MacOS和Windows上。Flutter算是支持平台最多的框架,良好的跨平台性,减少开发成本
- 原生用户界面:它是原生的,体验好,性能好
- 开源免费
主流框架的对比
- Cordova:Cordova基于网页技术进行包装,利用插件的形式开发移动应用
- RN(React Native):RN的效率由于是将View编译成了原生View,所以效率上要比基于Cordova的HTML5高很多,但是它也有效率问题,RN的渲染机制是基于前端框架的考虑,复杂的UI渲染是需要依赖多个view叠加,RN的列表方案不友好
- Flutter:在渲染技术上,选择了自己实现(GDI),有更好的可控性,使用了新的语言Dart,避免了RN的那种通过桥接器与Javascript通讯导致效率底下的问题,所以性能方面比RN更高一筹;
120fps超高性能
Flutter采用GPU渲染技术,所以性能极高
Flutter编写的应用是可以达到120fps(每秒传输帧数),完全可以胜任游戏的制作,RN的性能只能达到60fps。
核心原则
Flutter包括一个现代的响应式框架,一个2D渲染引擎,现成的widget和开发工具
Dart语法Function函数
Dart是面向对象的语言,即使是函数也是对象,并且属于Function类型的对象,这意味着函数可以分配给变量或作为参数传递给其他函数
Dart是一切皆对象,包括数字和函数
StatefulWidget和StatelessWidget
- StatefulWidget:具有可变状态的窗口部件,也就是你在使用应用的时候就可以随时变化,如果常见的进度条,随着进度不断变化
- StatelessWidge:不可变状态窗口部件,也就是你在使用时不可以改变,如果固定的文字
flutter的基本代码
import 'package:flutter/material.dart';
//主函数(入口函数),下面我会简单说说Dart的函数
void main() =>runApp(MyApp());
// 声明MyApp类
class MyApp extends StatelessWidget{
//重写build方法
@override
Widget build(BuildContext context){
//返回一个Material风格的组件
return MaterialApp(
title:'Welcome to Flutteraa',
home:Scaffold(
//创建一个Bar,并添加文本
appBar:AppBar(
title:Text('Welcome to Flutter'),
),
//在主体的中间区域,添加一个hello world 的文本
body:Center(
child:Text(
'Hello JSPang ,非常喜欢前端,并且愿意为此奋斗一生。我希望可以出1000集免费教程。',
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,
),
),
),
),
);
}
}
常用组件
Container容器组件的使用
Alignment属性:这个属性针对的是Container内child的对齐方式,也就是容器子内容的对齐方式,并不是容器本身的对齐方式
child:Container(
child:new Text('Hello JSPang',style: TextStyle(fontSize: 40.0),),
alignment: Alignment.center,
),
- bottomCenter:下部居中对齐
- bottomLeft:下部左对齐
- bottomRight:下部右对齐
- center:纵横双向居中对齐
- centerLeft:纵向居中横向居左对齐
- centerRight:纵向居中横向居右对齐
- topLeft:顶部左侧对齐
- topCenter:顶部居中对齐
- topRight:顶部居左对齐
设置宽,高和颜色属性
设置宽,高和颜色属性只要在属性名称后加入浮点型数字就可以
child:Container(
child:new Text('Hello JSPang',style: TextStyle(fontSize: 40.0),),
alignment: Alignment.center,
width:500.0,
height:400.0,
color: Colors.lightBlue,
),
padding,margin,decoration
padding
padding:const EdgeInsets.all(10.0),
padding:const EdgeInsets.fromLTRB(value1,value2,value3,value4)
// 我们用EdgeInsets.fromLTRB(value1,value2,value3,value4) 可以满足我们的需求,LTRB分别代表左、上、右、下。
margin
// margin是container和外部元素的距离
margin: const EdgeInsets.all(10.0),
margin:const EdgeInsets.fromLTRB(value1,value2,value3,value4)
decoration
// decoration是container的修饰器,主要的功能是设置背景和边框
// 如果要给背景加入一个渐变,需要使用BoxDecoration这个类,(注意,如果设置了decoration,就不要再设置color属性了,会冲突)
decoration: BoxDecoration(
gradient: const LinearGradient(colors: [Colors.lightBlue,Colors.greenAccent,Colors.purple] )),
border
border:Border.all(width:2.0,color:Colors.red)
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter 学习',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('Iris'),
),
body: Center(
child: Container(
child: new Text('世界那么大,我想去看看',style: TextStyle(fontSize: 20.0),),
alignment: Alignment.center,
width: 500.0,
height: 400.0,
padding: const EdgeInsets.fromLTRB(10.0, 30.0, 0.0, 0.0),
margin: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Colors.lightBlue,Colors.greenAccent,Colors.purple]
),
border: Border.all(width: 1.0,color: Colors.red)
),
),
),
),
);
}
}
加入图片的几种方式
- Image.asset:加载资源图片,就是加载项目资源目录中的图片,加入图片后会增大打包的包体体积,用的是相对路径
- Image.network:网络资源图片,意思就是需要加入一段http://xxxx.xxx的这样的网络路径地址
- Image.file:加载本地图片,就是加载本地文件中的图片,这是一个绝对路径,跟包体无关
- Image.memory:加载Uint8List资源图片
fit属性的设置
- BoxFit.fill:全图显示,图片会被拉伸,并充满父容器
- BoxFit.contain:全图显示,显示原比例,可能会有空隙
- BoxFix.cover:显示可能拉伸,可能裁切,充满(图片要充满整个容器,还不变形 )
- BoxFit.fitWidth:宽度充满(横向充满),显示可能拉伸,可能裁切
- BoxFit.fitHeight:高度充满(竖向充满),显示可能拉伸,可能裁切
- BoxFit.scaleDown:效果和contain差不多,但是此属性不允许显示超过源图片大小,可小不可大
图片的混合模式
图片混合模式(colorBlendMode)和color属性配合使用,能让图片改变颜色,里面的模式非常多,产生的效果也非常丰富
child:new Image.network(
'https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=2247692397,1189743173&fm=5',
color: Colors.greenAccent,
colorBlendMode: BlendMode.darken,
),
- color:是要混合的颜色,如果你只设置color是没有意义的
- colorBlendMode:是混合模式
repeat图片重复
- ImageRepeat.repeat :横向和纵向都进行重复,直到铺满整个画布
- ImageRepeat.repeatX:横向重复,纵向不重复
- ImageRepeat.repeatY:纵向重复,横向不重复
child:new Image.network(
'http://jspang.com/static/myimg/blogtouxiang.jpg',
repeat: ImageRepeat.repeat,
),
body: Center(
child: Container(
child:new Image.network(
'https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=2247692397,1189743173&fm=5',
scale: 1.0,
// fit: BoxFit.cover,
color: Colors.greenAccent,
colorBlendMode: BlendMode.darken,
repeat: ImageRepeat.repeat,
),
width: 500.0,
height: 600.0,
color: Colors.lightBlue,
),
),
ListView列表组件简介
ListTile组件
body: new ListView(
children:<Widget>[
new ListTile(
leading:new Icon(Icons.access_time),
title:new Text('access_time')
),
new ListTile(
leading:new Icon(Icons.account_balance),
title:new Text('account_balance')
),
]
),
图片列表的使用
body: new ListView(
children: <Widget>[
new Image.network( 'https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=2247692397,1189743173&fm=5'),
new Image.network( 'https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=1569462993,172008204&fm=5'),
new Image.network( 'https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=1853832225,307688784&fm=5')
],
),
横向列表的使用
body: Center(
child: Container(
height: 200.0,
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,
),new Container(
width: 180.0,
color: Colors.deepPurpleAccent,
)
],
),
),
)
scrollDirection属性
ListView组件的scrollDirection属性只是两个值,一个是横向滚动,一个是纵向滚动,默认的就是垂直滚动,所以如果是垂直滚动,一般不进行设置
- Axis.horizontal:横向滚动或者叫水平方向滚动
- Axis.vertical:纵向滚动或者叫垂直方向滚动
代码优化
// 使用类,减少嵌套
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:Center(
child:Container(
height:200.0,
child:MyList()
),
),
),
);
}
}
class MyList 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,
),
],
);
}
}
List类型的使用
List是Dart的集合类型之一,可以理解为数组,它声明的几种方式
-
var myList = List()
:非固定长度的声明 -
var myList = List(2)
:固定长度的声明 -
var myList = List<string>()
:固定类型的声明方式 -
var myList = [1,2,3]
:对List直接赋值
使用一个List传递,然后直接用List中的generate
方法进行生产List里的元素。最后生产一个带值的List变量
void main () => runApp(MyApp(
items: new List<String>.generate(1000, (i)=> "Item $i")
));
// main函数的runApp中调用了MyApp类,再使用类传递了一个item参数,并使用generate生成器对item进行赋值
// generate方法传递两个参数,第一个参数是生成的个数,第二个是方法
接受参数
final List<String> items;
MyApp({Key key, @required this.items}):super(key:key)
// 构造函数,@required是比传的意思,:super如果父类没有无名无参的默认构造函数,子类必须手动调用一个父类构造函数
动态列表ListView.builder()
import 'package:flutter/material.dart';
void main () => runApp(MyApp(
// 传递参数
items: new 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 ){
return MaterialApp(
title:'ListView widget',
home:Scaffold(
// 动态列表
body:new ListView.builder(
itemCount:items.length,
itemBuilder:(context,index){
return new ListTile(
title:new Text('${items[index]}'),
);
}
)
),
);
}
}
GridView网格列表组件
网格列表常用来显示多张图片,类似于相册功能
body: new GridView.count(
padding: const EdgeInsets.all(20.0),
crossAxisSpacing: 10.0,
crossAxisCount: 3,
children: <Widget>[
const Text('我的呱呱'),
const Text('我喜欢吃'),
const Text('我是个淑女的小姐姐'),
const Text('我是个会写代码的萌妹纸'),
const Text('我喜欢吃火锅'),
const Text('我喜欢看书')
],
)
// padding 内边距
// crossAxisSpacing: 网格间的空挡,相当于每个网格间的距离
// crossAxisCount: 网格的列数,相当于一行放置网格的数量
GridView.count:创建一个在横轴上具有固定数量网格块的平铺的布局
GridView.extent:使用具有最大横轴范围的网格块创建布局
自定义SliverGridDelegate:可以生成任意2D排列的子代,包括未对齐或重叠的排列
-
要创建具有大量(或无限)个子节点的网络,将GridView.builder构造函数与gridDelegate的 SliverGridDelegateWithFixedCrossAxisCount 或SliverGridDelegateWithMaxCrossAxisExtent 一起使用
import 'package:flutter/material.dart';
// 主函数(入口函数)
void main() => runApp(MyApp(
// 传递参数
// items: new List<String>.generate(10, (i)=>"Item $i")
));
// 声明MyApp类
class MyApp extends StatelessWidget{
// 接受参数
// final List<String> items;
// MyApp({Key key, @required this.items}):super(key:key);
// 重写build方法
@override
Widget build(BuildContext context){
// 返回一个Material风格的组件
return MaterialApp(
title: '好好学习',
home: Scaffold(
// 主体中间区域
body: new GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
mainAxisSpacing: 2.0,
crossAxisSpacing: 2.0,
childAspectRatio: 0.7
),
children: <Widget>[
new Image.network('http://img5.mtime.cn/mt/2018/10/22/104316.77318635_180X260X4.jpg',fit:BoxFit.cover),
new Image.network('http://img5.mtime.cn/mt/2018/10/10/112514.30587089_180X260X4.jpg',fit:BoxFit.cover),
new Image.network('http://img5.mtime.cn/mt/2018/11/13/093605.61422332_180X260X4.jpg',fit:BoxFit.cover),
new Image.network('http://img5.mtime.cn/mt/2018/11/07/092515.55805319_180X260X4.jpg',fit:BoxFit.cover),
new Image.network('http://img5.mtime.cn/mt/2018/11/21/090246.16772408_135X190X4.jpg',fit:BoxFit.cover),
new Image.network('http://img5.mtime.cn/mt/2018/11/17/162028.94879602_135X190X4.jpg',fit:BoxFit.cover),
new Image.network('http://img5.mtime.cn/mt/2018/11/19/165350.52237320_135X190X4.jpg',fit:BoxFit.cover),
new Image.network('http://img5.mtime.cn/mt/2018/11/16/115256.24365160_180X260X4.jpg',fit:BoxFit.cover),
new Image.network('http://img5.mtime.cn/mt/2018/11/20/141608.71613590_135X190X4.jpg',fit:BoxFit.cover),
],
)
),
);
}
}
// childAspectRatio:宽高比
布局
第01节水平布局Row的使用
Flutter中的row控件是水平控件,可以让Row子元素进行水平排列
Row控件可以分为灵活排列和非灵活排列两种
不灵活水平布局:根据Row子元素的大小,进行布局,如果子元素不足,会留有空间,如果子元素超出,会警告
//按钮
new RaisedButton(
onPressed: (){
},
color:Colors.orangeAccent,
child: new Text('黄色按钮'),
),
灵活布局:使用Expanded来解决空隙问题,在按钮外边加入Expanded就可以了
Expanded(
child: new RaisedButton(
onPressed: (){},
color: Colors.pinkAccent,
child: new Text('粉色按钮'),
)
)
注意:还可以灵活和不灵活混合使用
第02节:垂直布局Column组件
Column组件就是垂直布局控件,能够将子组件垂直排列
body:Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('I am JSPang'),
Text('my website is jspang.com'),
Text('I love coding')
],
)
// CrossAxisAlignment.star:居左对齐。
// CrossAxisAlignment.end:居右对齐。
// CrossAxisAlignment.center:居中对齐。
主轴和副轴
mainAxisAlignment 主轴对齐方式
- main轴:主轴(根据组件判断主轴,column组件,和Row组件)
- cross轴:副轴
第03节:Stack层叠布局
// 声明MyApp类
class MyApp extends StatelessWidget{
// 接受参数
// final List<String> items;
// MyApp({Key key, @required this.items}):super(key:key);
// 重写build方法
@override
Widget build(BuildContext context){
// 返回一个Material风格的组件
var stack = new Stack(
alignment: const FractionalOffset(0.5, 0.95),
children: <Widget>[
new CircleAvatar(
backgroundImage: new NetworkImage('https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=2247692397,1189743173&fm=5'),
radius: 100.0,
),
new Container(
decoration: new BoxDecoration(
color: Colors.lightGreen
),
padding: EdgeInsets.fromLTRB(20.0,3.0,20.0,3.0),
child: Text('布偶',style: TextStyle(fontSize: 18.0,color: Colors.blueGrey),),
)
],
);
return MaterialApp(
title: '好好学习',
home: Scaffold(
appBar: new AppBar(
title: new Text('水平方向布局'),
),
// 主体中间区域
body: Center(
child:stack
)
),
);
}
}
重叠布局的alignment属性
alignment
属性是控制层叠的位置的,建议在两个内容进行层叠时使用,它有两个值X轴距离和Y轴距离,值是从0到1的,都是从上层容器的左上角开始计算的
alignment: const FractionalOffset(0.5, 0.8),
CircleAvatar组件的使用
CircleAvatar
这个常用来作头像,组件里面有个radius的值可以设置图片的弧度
new CircleAvatar(
backgroundImage: new NetworkImage('https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=2247692397,1189743173&fm=5'),
radius: 100.0,
),
Stack的Position属性
层叠定位组件
Positioned组件的属性
- bottom:距离层叠组件下边的距离
- left:距离层叠组件左边的距离
- right:距离层叠组件右边的距离
- top:距离层叠组件上边的距离
- width:距离层叠组件定位组件的宽度
- height:距离层叠组件定位组件的高度
new Positioned(
bottom:10.0,
right:10.0,
child: new Text('技术胖'),
)
第05节:卡片组件布局
类似与ViewList,但是列表会以物理卡片的形态进行展示
卡片式布局默认撑满整个外部容器,如果想要设置卡片的高度,需要在外部容器就进行制定
var card = new Card(
child: Column(
children: <Widget>[
ListTile(
title: Text('湖南省益阳市',style:TextStyle(fontWeight: FontWeight.w500)),
subtitle: Text('伊:13710145724'),
leading: new Icon(Icons.account_box,color:Colors.lightBlue),
),
new Divider(),
ListTile(
title: Text('湖南省长沙市',style:TextStyle(fontWeight: FontWeight.w500)),
subtitle: Text('伊:13710145724'),
leading: new Icon(Icons.account_box,color:Colors.lightBlue),
),
new Divider(),
ListTile(
title: Text('湖南省常德市',style:TextStyle(fontWeight: FontWeight.w500)),
subtitle: Text('伊:13710145724'),
leading: new Icon(Icons.account_box,color:Colors.lightBlue),
),
new Divider(),
],
),
);
//new Divider()分割线
页面导航和其他
第01节:一般页面导航和返回
RaiseButton按钮组件
基本属性:
- child:可以放入容器,图标,文字
- onPressed:点击事件的响应,一般会调用Navigator组件
Navigator.push和Navigator.pop
-
Navigator.push
:是跳转到下一个页面,他要接受两个参数一个是上下文context
,另一个是要跳转的函数 -
Navigator.pop
:是返回到上一个页面,使用时传递一个context
参数,注意,必须是有上级页面的,也就是说上级页面使用了Navigator.push
import 'package:flutter/material.dart';
void main(){
runApp(MaterialApp(
title:'导航演示1',
home:new FirstScreen()
));
}
class FirstScreen extends StatelessWidget{
@override
Widget build(BuildContext context){
return new Scaffold(
appBar: AppBar(title:Text('导航页面')),
body:Center(
child:RaisedButton(
child:Text('查看商品详情页面'),
onPressed: (){
Navigator.push(context,new MaterialPageRoute(
builder:(context) =>new SecondScreen())
);
},
)
)
);
}
}
class SecondScreen extends StatelessWidget{
@override
Widget build(BuildContext context){
return Scaffold(
appBar:AppBar(title:Text('技术胖商品详情页')),
body:Center(
child:RaisedButton(
child:Text('返回'),
onPressed: (){
Navigator.pop(context);
},
))
);
}
}
第02节:导航参数的传递和接受
Awesome Flutter snippets插件的使用:帮助快速生成常用的Flutter代码片段
声明数据结构类
Dart中可以使用类来抽象一个数据,比如我们模仿一个商品信息,有商品标题和商品描述。定义一个product类,里面有两个字符变量,title和description
- title:商品标题
- description:商品详情描述
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
组件,然后使用路由MaterialPageRouter
传递参数
Navigator.push(
context,
MaterialPageRouter(
builder:(context)=>new ProductDetail(product:products[index])
)
)
第04节:页面跳转并返回数据
异步请求和等待
Dart的异步请求和等待和ES6的方法很像,直接使用async....await就可以实现
// async 是启用异步方法
_navigateToXiaoJieJie(BuildContext context) async{
final result = await Navigator.push(
context,
MaterialPageRouter(
builder:(context)=>Xiaojiejie()
)
);
Scaffold.of(context).showSnackBar(SnackBar(content:Text('$result')));
}
SnackBar的使用
SnackBar
是用户操作后,显示提示信息的一个控件,类似Tost
,会自动隐藏。SnackBar
是以Scaffold
的showSnackBar
方法来进行显示的
Scaffold.of(context).showSnackBar(SnackBar(content:Text('$result')))
返回数据的方式
返回数据其实是特别容易的,只要在返回时带第二个参数就可以了
Navigator.pop(context,'xxx');//xxx就是返回的参数
Demo
import 'package:flutter/material.dart';
void main(){
runApp(MaterialApp(
title: '页面跳转放回数据',
home: FirstPage(),
));
}
class FirstPage extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('找伊大美女要电话号码'),
),
body: Center(
child: RouterButton(),
),
);
}
}
class RouterButton extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return RaisedButton(
child: Text('伊伊是大美女'),
onPressed: (){
_navigateToXiaoJieJie(context);
},
);
}
_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) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('我是小姐姐'),
),
body: Center(
child: Column(
children: <Widget>[
RaisedButton(
child: Text('大长腿小姐姐'),
onPressed: (){
Navigator.pop(context,'大长腿姐姐:13710145724');
},
),
RaisedButton(
onPressed: (){
Navigator.pop(context,'小蛮腰姐姐:13710145724');
},
child: Text('小蛮腰小姐姐'),
)
],
),
),
);
}
}
第05节:静态资源和项目图片的处理
注意:pubspec.yaml文件,配置项目资源文件,在里面声明资源文件
assets:
- images/blogtouxiang.jpg
使用项目图片资源
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'),
);
}
}
第06节:Flutter客户端打包
配置APP的图标
项目根目录/android/app/src/main/res
AndroidManifest.xml文件
这个文件主要用来配置APP的名称,图标和系统权限,所在的目录在
项目根目录/android/app/src/main/AndroidManifest.xml
android:label="flutter_app" //配置APP的名称,支持中文
android:icon="@mipmap/ic_launcher" //APP图标的文件名称
生成keystore
打包详情