环境配置
1. java环境
2. flutter环境
下载flutter安装包 解压到你的文件夹 配置环境变量
PUB_HOSTED_URL=http://pub.flutter-io.cn
FLUTTER_STORAGE_BASE_URL=http://storage.flutter-io.cn
把flutter的bin目录配置到path,就能在任何位置使用flutter命令
工具
1. androidStudio
安装androidStudio,配置安卓模拟器
2. VSCode
安装VSCode,安装插件flutter,dart会一并安装
3. idea
安装idea,安装插件flutter,dart会一并安装 检查环境和创建项目
- 使用flutter doctor检查环境是否安装完成
- 使用flutter create myapp创建项目(idea/androidstudio创建直接点击创建flutter项目就好了)
- 启动安卓模拟器(从androidstudio启动,vscode右下角启动)
- 在项目根目录使用flutter run运行项目(vscode使用调试->启动调试可以弹出手动热加载重启的按钮,idea运行就不说了)
- 使用按键R热加载,P显示网格,O切换android和ios的预览模式,Q退出调试预览模式(vscode如果使用调试->启动调试运行的项目,可以有按钮点击,idea可以按钮,热加载是默认保存自动热加载)
基础语法
1. 主入口
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 World'),
),
),
);
}
}
2. StatefulWidget和StatelessWidget
StatefulWidget : 具有可变状态的窗口部件,也就是你在使用应用的时候就可以随时变化,比如我们常见的进度条,随着进度不断变化。 StatelessWidget:不可变状态窗口部件,也就是你在使用时不可以改变,比如固定的文字
常用组件
1. Text组件
Text组件通常使用的是
Text(“我是text组件”), TextAlign属性 center: 文本以居中形式对齐,这个也算比较常用的了。 left:左对齐,经常使用,让文本居左进行对齐,效果和start一样。 right :右对齐,使用频率也不算高。 start:以开始位置进行对齐,类似于左对齐。 end: 以为本结尾处进行对齐,不常用。有点类似右对齐. maxLines属性 设置最多显示的行数 overflow属性 设置文本溢出时的处理 clip:直接切断,剩下的文字就没有了,感觉不太友好,体验性不好。 ellipsis:在后边显示省略号,体验性较好,这个在工作中经常使用。 fade: 溢出的部分会进行一个渐变消失的效果,当然是上线的渐变,不是左右的。
style属性
Text(“我是text组件”,style: TextStyle(fontSize: 12.0,color: Colors.red))
2. Container容器
相当于html的
,react-native里的 alignment属性:container内child的对齐方式 width,height,color属性 padding属性:内边距和css里的一样,和child的距离 margin属性:外边距和css里的一样,和parent的距离 decoration属性:边框背景之类的修饰器
Widget build(BuildContext context ){
return MaterialApp(
title:'Text widget',
home:Scaffold(
body:Center(
child:Container(
child:new Text('Hello JSPang',style: TextStyle(fontSize: 40.0),),
alignment: Alignment.topLeft,
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:new BoxDecoration(
gradient:const LinearGradient(
colors:[Colors.lightBlue,Colors.greenAccent,Colors.purple]
),
border:Border.all(width:2.0,color:Colors.red)
),
),
),
),
);
}
3. Image图片组件
- Image.asset:本地资源图片(相对路径)
- Image.network:网络资源图片
- Image.file:本地图片(绝对路径,和包无关)
- Image.memory:(加载Uint8List资源图片)
- fit属性
- BoxFit.fill:拉伸充满容器
- BoxFit.contain:原比例
- BoxFit.cover:充满容器,不变形
- BoxFit.fitWidth:宽度充满
- BoxFit.fitHeight:高度充满
- BoxFit.scaleDown:和contain差不多,和原图比可小不可大 colorBlendMode属性:和color搭配,混合配色
- repeat图片重复:
- ImageRepeat.repeat:横纵重复,铺满画布
- ImageRepeat.repeatX:横向重复
- ImageRepeat.repeatY:纵向重复
4. ListView列表组件
//在主体的中间区域,添加一个hello world 的文本
ListView(children: <Widget>[
ListTile(
leading: Icon(Icons.access_time),
title: Text('access_time'))
]),
ListView的子组件是children,是个数组,可以手动加多个任意组件 ListView的属性scrollDirection默认是Axis.vertical,垂直列表 可以使用Axis.horizontal水平列表 以上。。。。。是简单的列表使用,现在dart支持在组件中使用for和if了,没有这个之前,怎么办呢,其实dart提供了动态列表
ListView.builder(
itemCount:items.length,
itemBuilder:(context,index){
return ListTile(
title: Text('${items[index]}'),
);
}
)
items是传入的数组,index数组下标
5. Row,Column组件
Row组件的子组件是数组的,可以接受多个子组件
Row(
children: <Widget>[
RaisedButton(
onPressed: (){
},
color:Colors.redAccent,
child: Text('红色按钮')
),
RaisedButton(
onPressed: (){
},
color:Colors.orangeAccent,
child: Text('黄色按钮'),
),
RaisedButton(
onPressed: (){
},
color:Colors.pinkAccent,
child: Text('粉色按钮')
)
],
)
这样三个按钮是水平依次排列,但是没有充满一行,用Expanded包住其子组件,可以灵活布局,而Expanded的属性flex,支持按比例分配空间,而且可以混用, Row的两个重要属性 crossAxisAlignment:副轴对齐方式 CrossAxisAlignment.start,子组件上边界对齐 CrossAxisAlignment.end,子组件下边界对齐 CrossAxisAlignment.center,居中对齐 mainAxisAlignment:主轴对齐方式 MainAxisAlignmen.start,从左向右排列 MainAxisAlignmen.center居中排列 MainAxisAlignmen.end从右向左排列 Column组件和Row组件类似
- Stack层叠布局组件
alignment属性是控制层叠位置,从容器左上角开始算 positioned属性,也是组件 bottom:距离层叠组件下边的距离 left:距离层叠组件左边的距离 top:距离层叠组件上边的距离 right:距离层叠组件右边的距离 width:层叠定位组件的宽度 height:层叠定位组件的高度
7. Card卡片组件
默认撑满外部容器,如需设置宽高,需要设置外部容器
8. CircleAvatar组件
经常用来作头像,内部radius可以设置图片的弧度 小知识点 页面跳转 Navigator.push Navigator.pop返回到上级界面 在Navigator的pushNamed方法可以用argument跳转页面并传参,接收参数就是在跳转的页面定义
Navigator.of(context)
.push(MaterialPageRoute(builder: (BuildContext context) {
return SupplierDetailsPage(id:1);
}));
Navigator.of(context)
.pushNamed("/xxxx",arguments: {"id": 1});
pop返回上级也可以带参数返回 Navigator有很多方法,不细说,
静态文件的使用 在pubspec.yaml中配置
flutter:
assets:
- images/
images就是根目录的文件夹
经常使用的组件
Scaffold
基本上使用的是appBar属性,也就是app的header,使用AppBar组件来定义详细的东西 body:app中间的内容,header下面的 bottomNavigationBar是app底部的Bar
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
iconSize: 20.0,
currentIndex: _selectedIndex,
selectedItemColor: Color.fromRGBO(0, 204, 221, 1),
unselectedItemColor: Color.fromRGBO(151, 151, 151, 1),
onTap: _onBarItem,
items: [
BottomNavigationBarItem(
title: Text(
"home",
style: TextStyle(
color: Color.fromRGBO(151, 151, 151, 1),
),
),
icon: Icon(Icons.person),
),
BottomNavigationBarItem(
title: Text(
"item1",
style: TextStyle(
color: Color.fromRGBO(151, 151, 151, 1),
),
),
icon: Icon(Icons.person),
),
BottomNavigationBarItem(
title: Text(
"item2",
style: TextStyle(
color: Color.fromRGBO(151, 151, 151, 1),
),
),
icon: Icon(Icons.computer),
),
BottomNavigationBarItem(
title: Text(
"item3",
style: TextStyle(
color: Color.fromRGBO(151, 151, 151, 1),
),
),
icon: Icon(Icons.computer),
),
],
),
其他还有Align组件,加上垂直就是Column,水平就是Row Padding组件,里面就一个padding属性,只是做布局用的 GestureDetector组件,类似react-native的TouchableOpacity,有很多个手势监听事件
GestureDetector({
Key key,
this.child,
this.onTapDown,
this.onTapUp,
this.onTap,
this.onTapCancel,
this.onDoubleTap,
this.onLongPress,
this.onLongPressStart,
this.onLongPressMoveUpdate,
this.onLongPressUp,
this.onLongPressEnd,
this.onVerticalDragDown,
this.onVerticalDragStart,
this.onVerticalDragUpdate,
this.onVerticalDragEnd,
this.onVerticalDragCancel,
this.onHorizontalDragDown,
this.onHorizontalDragStart,
this.onHorizontalDragUpdate,
this.onHorizontalDragEnd,
this.onHorizontalDragCancel,
this.onForcePressStart,
this.onForcePressPeak,
this.onForcePressUpdate,
this.onForcePressEnd,
this.onPanDown,
this.onPanStart,
this.onPanUpdate,
this.onPanEnd,
this.onPanCancel,
this.onScaleStart,
this.onScaleUpdate,
this.onScaleEnd,
this.behavior,
this.excludeFromSemantics = false,
this.dragStartBehavior = DragStartBehavior.start,
- padding,margin定位使用EdgeInsets.all() EdgeInsets.only()中有个left,top,right,bottom单个设置,EdgeInsets.fromLTRB()left,top,right,bottom必须都设置值 一些需要注意的
- 在dart语法中,定义方法时,非必传参数用{}括起来,在传参时也需要定义传哪个参数,例如:EdgeInsets.only(left:12.0)
- dart中的private就是方法名或者变量前加下划线,例如:int _status = 0 void _getData(){},默认是public的
- 组件中,可以包含子组件的有时候是多个children,例如Row,Column,ListView之类,有些是只能有一个子组件child,例如Container, GestureDetector,Padding,Center等
- 还有就是,在dart中,参数数值大部分都是double类型,
- dart中,如果使用async await使方法异步执行,那么返回值被包在Future中的,可以使用.then
- 在dart之前版本,使用组件需要new,现在不需要new了,需要注意
- 分割线组件Divider(height:1.0)
- MediaQuery.of(context).padding.top;获取状态栏高度单位dp
- MediaQuery.of(context).size.width;获取设备宽度,height设备高度单位dp
- 也可以使用dart:ui中的window.physicalSize获取屏幕宽高,单位是px,方法属性比较多,就不一一说了
- flutter中的颜色使用Color.fromRGBO(255,255,255,1);Colors.red; Color(0xff2D4ED1)
- SingleChildScrollView里面不能用Expanded,因为Expanded是撑满空间,不能滚动
- LinearGradient渐变色,两种颜色及以上默认比例1:1:1。。。
- Spacer:Row和Column中的占位组件,默认flex:1
- 设置appbar高度(在AppBar外套一层PreferredSize就可以设置了)
- 使用DefaultTabController,可以设置tabbar,就不需要写控制器了,但是要注意length
DefaultTabController(
length: 3,
child: Column(
children: <Widget>[
TabBar(
labelStyle: TextStyle(
fontSize: 16.0,
),
indicatorSize: TabBarIndicatorSize.label,
unselectedLabelColor: Color.fromRGBO(151, 151, 151, 1),
labelColor: Color.fromRGBO(0, 204, 221, 1),
indicatorColor: Color.fromRGBO(0, 204, 221, 1),
tabs: <Widget>[
Tab(text: "xx"),
Tab(text: "xx"),
Tab(text: "xx"),
],
),
Expanded(
child: Container(
child: TabBarView(
children: <Widget>[
Center(child: Text("xxx")),
Center(child: Text("xxx")),
SupplierTrade(),
],
),
),
)
],
),
),
- ListView没有和AppBar一起使用时,ListView头部会有一个空白,它是一个padding, 可以使用MediaQuery.removePadding去掉
return MediaQuery.removePadding(
removeTop: true,
context: context,
child: Container(
child: ListView(
- flutter中,状态栏默认有一个黑色半透明遮罩,这个是为了android4.4以下的机型没有沉浸式状态栏的,我们可以在主入口的静态组件中设置去掉遮罩
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hzt_purchase/home_page.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
//statusBar设置为透明,去除半透明遮罩
final SystemUiOverlayStyle _style =
SystemUiOverlayStyle(statusBarColor: Colors.transparent);
@override
Widget build(BuildContext context) {
//将style设置到app
SystemChrome.setSystemUIOverlayStyle(_style);
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primaryColor: Color.fromRGBO(0, 204, 221, 1),
),
home: HomePage(),
);
}
}
- 动态组件 with AutomaticKeepAliveClientMixin重写方法,设置保留状态
@override
bool get wantKeepAlive => true;
- 组件的生命周期
- 初始化initState()方法
- 销毁dispose()方法
- 更新didUpdateWidget()方法
- setState((){});方法,会触发更新组件
调试打包
app打包步骤
1. 设置app的名字
AndroidManifest.xml文件的 android:lable=”flutter_app” android:icon:”@mipmap/ic_launcher” /android/app/src/main/res中app替换图标
2. 生成秘钥
找到javajdk中的文件keytool 使用 keytool -genkey -v -keystore ~/key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key 其中keytool命令可以直接指定目录到文件执行,/key.jks名字自定义,保存目录自定义,目录中的名字有空格,目录名需要用引号扩上,.jks文件私密自己留存
3. 配置项目的key.properties
在android文件夹下创建文件key.properties。里面写代码 storePassword= //创建KEY时输入的 密钥库 密码 keyPassword= //创建KEY时输入的 密钥 密码 keyAlias=key storeFile=<E:/key.jks> //key.jks的存放路径 key.propterties文件私密自己留存
4. 配置build.grandle
找到/android/app/build.grandle文件,在前加入代码
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
}
}