在Android中,View是屏幕上显示的所有内容的基础, 按钮、工具栏、输入框等一切都是View。
iOS 构建 UI 的过程中将大量使用 view 对象。这些对象都是 UIView 的实例。它们可以用作容器来承载其他的 UIView,最终构成你的界面布局。
Flutter中,View相当于是Widget。然而,与View相比,Widget有一些不同之处。 首先,Widget仅支持一帧,并且在每一帧上,Flutter的框架都会创建一个Widget实例树(译者语:相当于一次性绘制整个界面)。 相比之下,在Android上View绘制结束后,就不会重绘,直到调用invalidate时才会重绘。
与Android的视图层次系统不同(在framework改变视图),而在Flutter中的widget是不可变的,这允许widget变得超级轻量
Android中,直接改变View来更新视图,
iOS中的 views 在改变时并不会被重新创建。但是与其说 views 是可变的实例,不如说它们被绘制了一次,并且直到使用 setNeedsDisplay() 之后才会被重新绘制。
Flutter的widget是唯一的不变的(使得每一个widget都是非常轻量级),所以flutter不会直接更新,而必须使用Widget的状态(StatefullWidget和StatelessWidget)。
注:StatelessWidget和StatefullWidget,自定义Widget都是选择继承这两者之一。
StatelessWidget面向那些始终不变的UI控件,比如标题栏中的标题;而StatefulWidget则是面向可能会改变UI状态的控件,比如有点击反馈的按钮。
StatefulWidget的创建需要指定一个State,在需要更新UI的时候调用setState(VoidCallback fn),并在VoidCallback中改变一些变量数值等,组件会重新build以达到刷新状态也就是刷新UI的效果。
Android中您通过XML编写布局,
iOS中,与两者都不同,iOS可以通过用 Storyboard 文件来组织 views,并对它们设置约束,或者,你可能在 view controller 中使用代码来设置约束;
Flutter中,您可以使用widget树来编写布局。
注:State的生命周期有四种状态:
created:当State对象被创建时候,State.initState方法会被调用;
initialized:当State对象被创建,但还没有准备构建时,State.didChangeDependencies在这个时候会被调用;
ready:State对象已经准备好了构建,State.dispose没有被调用的时候;
defunct:State.dispose被调用后,State对象不能够被构建
在Android中从父级控件调用addChild或removeChild以动态添加或删除View;
iOS中,在父view中调用addSubView()或在子view中调用removeFromSuperview()来动态的添加或删除子views;
Flutter中widget是唯一不变的,所以widget是只有继承StatefullWidget,并由其父类来根据传入的值进行widget的切换以及状态的改变(同上的更新视图)。
在Android中可以通过View.animate()对视图进行动画处理;
iOS中调用 animate(withDuration:animations:) 方法来给一个 view 创建动画
Futter中通过动画库给widget添加动画;
注:在Android中,您可以通过XML创建动画或在视图上调用.animate()。在Flutter中,您可以将widget包装到Animation中;与Android相似,在Flutter中,您有一个AnimationController和一个Interpolator, 它是Animation类的扩展,例如CurvedAnimation。您将控制器和动画传递到AnimationWidget中,并告诉控制器启动动画。
Android可以用Canvas 绘制自己想要的形状;
iOS 中通过 CoreGraphics 来在屏幕上绘制线条和形状;
Flutter则是通过两个类实现这种绘制画布的,CustomPaint和CustomPainter,它们实现您的算法以绘制到画布。
void paint(Canvas canvas, Size size) {
var paint = new Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeWidth = 5.0;
for (int i = 0; i < points.length - 1; i++) {
if (points[i] != null && points[i + 1] != null)
canvas.drawLine(points[i], points[i + 1], paint);
}
在Android中继承View或已经存在的某个控件,然后覆盖其绘制方法来实现自定义View。
iOS中UIView 的子类,或使用已经存在的 view 来重载并实现方法,以达到特定的功能;
Flutter通常是通过组合其它widget来实现的,而不是继承。例如:构建持有一个label的CustomButton。这是通过将Text与RaisedButton组合来实现的,而不是扩展RaisedButton并重写其绘制方法实现
class CustomButton extends StatelessWidget {
final String label;
CustomButton(this.label);
@override
Widget build(BuildContext context) {
return new RaisedButton(onPressed: () {}, child: new Text(label));
}}
Dart是单线程执行模型,支持Isolates(在另一个线程上运行Dart代码的方式)、事件循环和异步编程。 除非您启动一个Isolate,否则您的Dart代码将在主UI线程中运行,并由事件循环驱动(译者语:和JavaScript一样)。
在Android中,当你想访问一个网络资源时,你通常会创建一个AsyncTask,它将会在主线程之外防止UI线程阻塞,AsyncTask有一个线程池,可以为你管理线程;
Flutter是单线程的,运行一个事件循环(如Node.js),所以您不必担心线程管理或者使用AsyncTasks、IntentServices。要异步运行代码,可以将函数声明为异步函数,并在该函数中等待这个耗时任务
loadData() async {
String dataURL = "https://jsonplaceholder.typicode.com/posts";
http.Response response = await http.get(dataURL);
setState(() {
widgets = JSON.decode(response.body);
});}
这种情况android上用AsyncTask会覆盖三个方法OnPreExecute、doInBackground和onPostExecute,而flutter只需等待(await)一个长时间运行的函数,而Dart的事件循环将负责其余的事情。这种情况只适用于数据量娇小的轻小的情况,当数据量较大的时UI线程可能会挂起;此时flutter可以利用多个CPU内核来执行耗时或计算密集型任务。这是通过使用Isolates来完成的。
loadData() async {
ReceivePort receivePort = new ReceivePort();
await Isolate.spawn(dataLoader, receivePort.sendPort);
// The 'echo' isolate sends it's SendPort as the first message
SendPort sendPort = await receivePort.first;
List msg = await sendReceive(sendPort, "https://jsonplaceholder.typicode.com/posts");
setState(() {
widgets = msg;
});
}
Android用的时OkHttp而Flutter用的是“http” package,它 、抽象出了许多常用的API,可以简单有效的发起网络请求
Android可以直接下载拖入,Gradle文件来添加依赖项;
flutter只需要在pubspec.yaml文件夹中的dependencies下加入对应的sdk名以及对应版本后在终端上输入packege get即可导入
监听生命周期:Android可以覆盖Activity的方法来捕获Activity的生命周期回调;flutter中可以通过挂接到WidgetsBinding观察并监听didChangeAppLifecycleState更改事件来监听生命周期事件
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
setState(() {
_lastLifecyleState = state;
});
}
Android中Linearlayout线性布局可以用flutter中的Row和Column来替代实现;
@override
Widget build(BuildContext context) {
return new Row(或者Column)(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text('One'),
new Text('Two'),
new Text('Three'),
new Text('Four'),
],
Android中的RelativeLayout相对布局,在flutter中可以通过使用Column、Row和Stack的组合来实现RelativeLayout的效果;
Android中的ScrollView和ListView都可以用flutter的ListView来实现其效果;
ListView(
children: <Widget>[
new Text('Row One'),
new Text('Row Two'),
new Text('Row Three'),
new Text('Row Four'),
],
);
Android中通过调用方法setOnClickListener将OnClick绑定到按钮等view上;
iOS 中,给一个 view 添加 GestureRecognizer 来处理点击事件;
Flutter中1.对于本身支持点击的Widget如:Raisedbutton,floatButton等都有onPressed方法;2.对于包含点击事件的Widget可以将其包装到GestureDetector中,并将处理函数传递给onTap参数;
GestureDetector(
child: new FlutterLogo(
size: 200.0,
),
onTap: () {
print("tap");
},
),
注:widget手势:Tap:onTapDown,onTapUp,onTap,onTapCancel
Double tap:onDoubleTap 用户快速连续两次在同一位置轻敲屏幕.
长按:onLongPress
垂直拖动:onVerticalDragStart,onVerticalDragUpdate,onVerticalDragEnd
水平拖拽:onHorizontalDragStart,onHorizontalDragUpdate,onHorizontalDragEnd
在Android studio或Xcode的对应项目中开启权限,
用法:
import 'package:location/location.dart';
var currentLocation = LocationData;
var location = new Location();
// Platform messages may fail, so we use a try/catch PlatformException.try {
currentLocation = await location.getLocation();
} on PlatformException catch (e) {
if (e.code == 'PERMISSION_DENIED') {
error = 'Permission denied';
}
currentLocation = null;
}
var location = new Location();
location.onLocationChanged().listen((LocationData currentLocation) {
print(currentLocation.latitude);
print(currentLocation.longitude);
});
在Android studio或Xcode的对应项目中开启权限,
用法:
import 'package:image_picker/image_picker.dart';
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
File _image;
Future getImage() async {
var image = await ImagePicker.pickImage(source: ImageSource.camera);
setState(() {
_image = image;
});
}