简介
什么是flutter
flutter是谷歌推出的针对移动平台的开发工具包(sdk),可以实现跨品台的开发,即一套代码在Android 和 IOS 上能够实现在原生应用的开发。主要有如下特点:
- 谷歌跨平台的移动SDK
- 提供热重载和热更新功能,能够更快速的开发。
- 内部程序在生产环境运行的时候,会直接编译成ARM机器码,提高程序的运行速度和更自然的UI表现。
- 通过提供的库能够用更少的代码完成开发,并且flutter的生态圈日益强大,提供的插件也非常多。
flutter结构
上层Framework提供了安卓UI库Material 和 IOS UI库 Cupertino,可以由开发人员自行选择。他们的组件库Widgets组成开发界面,并且可以使用Rendering库来自定义、美化界面。还提供动画Animation,绘图Painting的API,手势控件Gestures。最后还提供底层函数Foundation,例如:Unicode、WriteBuffer等
下层引擎由主要由C++ 驱动,Skia是2D渲染系统,Dart是flutter的开发语言,其优点就是能够在开发的时候又更好的开发效率,在生产环境中提供更好的性能,Text即文本渲染。
关于更多信息可以到 flutter官网查看,或者也可到flutter中文社区
安装
环境搭建
- 下载 java jdk
首先需要先下载java的jdk,可以点击这里下载对应的版本,记得在下载之前勾选同意协议。
下载完成后在终端打出 java,能成功输出即可。
-
下载flutter
首先配置环境变量,Flutter的执行是要进行联网的,由于需要访问国外的资源,所以还需要配置环境变量。export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
其次需要下载flutter安装包,同理选择对应的版本,这里由于国外网站网速下载比较慢,这里推荐用git下载,下载速度更快
git clone -b master https://github.com/flutter/flutter.git
./flutter/bin/flutter --version
下载好后把flutter可以放在其他路径下,这里尽可能不要放在存放在C盘,因为后续很多项目依赖都是共同存放在flutter这个文件夹下的,随着项目的壮大,flutter文件夹也会越来越大。
在Flutter安装目录的flutter文件下找到flutter_console.bat,双击运行并启动flutter命令行,接下来,你就可以在Flutter命令行运行flutter命令了。
运行flutter doctor命令来查看其他的依赖是否完成安装
现在你可能看到是上面的界面,不着急,先一步一步的来,首先需要下载Android Studio
- 下载Android Studio
同样附上下载链接
点击下载即可,然后安装过程中直接下一步下一步就ok了。linux用户需要到android-studio/bin目录下找到studio.sh文件运行即可。
由于需要下载一系列android jdk,这里需要设置下代理,可以设置免费的国内镜像http://mirrors.neusoft.edu.cn/
依然下一步下一步,然后就是漫长的下载安装过程了
下载好后安装虚拟机
选择机型,这里我选择的是Nexus 5X
选择系统,这里我为了调试选择的是Android 9.0的。由于现在市面上的Android手机普遍是7.x的版本,如果是上线产品最好选择选择7.x的.
下载好后直接点finish
这里点击运行等待一段时间即可运行起虚拟机。
接下来继续运行flutter doctor
第一个说的是缺少证书,直接按提示运行 flutter doctor --android-licenses,然后同意协议一律y,否则将会不成功。然后再次运行flutter doctor 提示已经成功了。
另外第三项是需要下载flutter的插件,直接下载即可,这里下载了flutter会自动下载dart
重启AndroidStudio再运行flutter doctor即可
- 在VScode中进行开发
首先需要下载插件 flutter 和 dart,同样是下载了flutter会自动下载dart
现在先初始化一个项目
调用 View>Command Palette…( ctrl + shift + p )
输入 ‘flutter’, 然后选择 ‘Flutter: New Project’ action
输入 Project 名称 (如myapp), 然后按回车键
这里需要注意两点 - 不能取一些flutter前期需要的包名,例如(test),要不然会创建不成功。
- 一旦前期取好了工程名,后期就不要去修改。会报一大堆找不到工程的bug。
运行flutter run第一次启动会下些flutter依赖的包,只要设置了代理,等一段时间就好了。后面再次启动就很快了。启动后的界面如下
需要注意的是 r 是 热更新,只会更新你当前页面的状态。R 是 热重载,会重新加载程序。q是退出程序,另外下方还有个网址http://127.0.0.1:59575/,这个页面进去是提供性能测试页面。下面是启动后的页面,可以看到点击按钮,页面上的数字会自增。
另外热更新的启动方式还可以在debug模式下执行运行,这样点击保存即可看到更新后的视图。
编写第一个应用
基本构建
在开发前期,首先先知道我们一开始需要用到一些文件。
main.dart是flutter主要入口文件,名字不可更改。内部有个main函数,这也是dart语言的程序首入栈函数,同样不可更改,runApp是flutter的初始化函数。可以看到这里初始化了一个类。
void main() => runApp(MyApp());
然后是pubspec.yaml 文件,里面有很多声明内容,现在只需要注意下面几点即可
dependencies:开发环境的依赖包以及版本
dev_dependencies:生产环境的包以及版本
这两个一般是dart的开源包,具体可以访问这个网站,这里需要下载依赖项的话需要先在pubspec.yaml这个文件写好需要的包名和版本,然后保存或手动执行flutter packages get即可安装。
另外还需要注意flutter项下的assets,在引入静态图片的时候,需要在下方写好图片的路径。
下方还有个fonts同理。
其他文件夹可以先不用管,这些暂时还用不到,一般我们直接在main.dart文件中撸代码就行了。
状态控件
在flutter中一切皆控件,所以我们先了解flutter两种基本控件,即StatelessWidget(无状态控件)和 StatefullWidget (有状态类控件)。
他们两种控件主要区别就是是否可在内部更改状态。StatelessWidget 仅可用于不可更改数据的控件,StatefulWidget会创建一个State来存储需要的数据,需要更改的时候再调用setState方法进行修改,这里接触过React的同学应该比较熟悉这点。
在main.dart中也可以看到这两个控件。
StatelessWidget:
StatefulWidget:
总之一般需要改变数据状态如请求列表、动画等用StatefullWidget ,其他静态展示的可以用 StatelessWidget , 具体关于这两者的区别可见《Flutter框架基础》
布局
先改一下main.dart里面的代码
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 安卓风格的App控件
return MaterialApp(
title: 'Flutter Demo',
// 主题
theme: ThemeData(
primarySwatch: Colors.blue,
),
// 需要渲染的页面
home: MyHomePage(title: 'hello world'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
// 在build中返回一个控件,Scaffold为基础布局控件
return Scaffold(
// 头部标题
appBar: AppBar(title: Text('title')),
// 主体内容部分
body: Text(widget.title),
);
}
}
appBar 是用来放头部控件类,常规的就用AppBar即可,在头部可以放标题、icon、下拉菜单等。例如:
// 头部标题`
appBar: AppBar(
leading: Icon(Icons.book),
title: Text('title'),
// actions内部是数组,代表右侧可以放多个控件
actions: <Widget>[
IconButton(
icon: Icon(Icons.add),
onPressed: (){},
),
// 给右侧一个10dp的间距
SizedBox(width: 10),
],
),
现在再来加一些内容
// 创建一个列表,会自动生成50个元素
body: ListView.builder(
itemCount: 50,
// 返回生成的元素
itemBuilder: (BuildContext context, int index) {
return Text(index.toString());
},
),
ListView是个列表控件,可以生成一个竖向或横向的列表,并且可滑动。在不知道有多个列的情况下,可以用build方法生成。
这里生成了一个竖向的列表,并且可以竖向滑动。
再稍微包装一下。body内部的代码为:
这里增加一个动态点击效果。即点击的item后面的心形变红
雏形已经做得差不多,现在再根据上面的内容加入路由功能。
路由
在flutter中分两种路由,一种是静态路由,一种是动态路由。静态路由即不依赖其他状态的页面,动态路由则可以传入参数改变状态。
静态路由
在lib目录中先建一个新的页面,名叫activePage.dart,在里面写入如下代码:
import 'package:flutter/material.dart';
class ActivePage extends StatefulWidget {
@override
_activePageState createState() => _activePageState();
}
class _activePageState extends State<ActivePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('favorite page'),
),
body: Text('favorite page'),
);
}
}
然后在main.dart中的MaterialApp模块中加入路由,并引入刚才写的activePage.dart
现在需要点击main.dart的头部的图标“ + ” 进入此页面,改写之前的appBar:
即点击跳转刚刚定义的页面,这里用到material自带的Navigator对象,并在其of方法中传入了一个context。context即现在的渲染的快照,类似于web中的虚拟dom树,可以在其中提取现在的一些数据,例如现在的路由页面。想需要了解flutter的内部渲染机制的可以参考这篇文章 深入了解Flutter界面开发。
现在页面已经可以成功跳转了,可是如果需要把第一个页面的数据传到第二个页面,例如点击的item,就需要用到动态路由了。
动态路由不需要在MaterialApp中定义routes了,需要用到PageRoute这个类
这里需要传递参数,需要现在ActivePage类中定义好需要传入的参数
在main.dart中传入数据
然后在下面的_activePageState类中就可以在widget中访问到这条数据了,可以打印出来
这里必须要要再创建createState之后打印,因为这也是属于状态改变。
得到值后再来做一些界面的修改。
import 'package:flutter/material.dart';
class ActivePage extends StatefulWidget {
// 定义好要传入的参数
ActivePage(this.activeList);
final List<int> activeList;
@override
_activePageState createState() => _activePageState();
}
class _activePageState extends State<ActivePage> {
@override
void initState() {
super.initState();
// 从小到大进行排序
widget.activeList.sort();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('favorite page'),
),
body: ListView.builder(
itemCount: widget.activeList.length,
itemBuilder: (_, int index) {
return Card(
child: ListTile(
title: Text(widget.activeList[ index ].toString()),
),
);
},
),
);
}
}
跳转页面即可看到之前勾选的选项。
控件之间的通信
父 -> 子
父控件到子控件的通信上面已经接触过,就是在引用类的时候把参数放到括号中即可
父控件接收参数
这里用的是匿名传参,父控件需要按照子控件定义的顺序来接收,并且定义的匿名参数都是必传选项。如果需要非必传的话可以像如下
子控件
父控件
这些都是dart定义的语法,具体可见 dart基础
子 -> 父
子控件跟父控件的通信这里可以用传入一个回调方法来改变父控件的状态
然后修改一下下面定义ActivePage的传参
return ActivePage(
activeList,
cb: callbackOfChild
);
callbackOfChild为子控件调用的回调函数,用来改表title中的文字,定义在下方。
void callbackOfChild(value) {
setState(() {
title = value;
});
}
然后修改子控件activePage.dart的传参定义
通过点击子控件的每一栏数字然后返回到main.dart的时候,标题发生改变
以上对于低依赖状态的控件是非常适合的,但是在许多在大型应用,状态往往是关联着非常多的控件的。如果依然用以上方法的话状态往往难以管理,这里就需要一个状态管理工具。
在flutter中,状态管理工具可以用redux,另外还有官方推荐的基于流的bloc。
Http请求
数据序列化
测试
打包
https://flutter.io/docs/deployment/android#review-the-app-manifest