这有一个例子描述了怎样在屏幕上展示一个Widget,并且给他添加一些padding。
override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(“Sample App”),
),
body: new Center(
child: new MaterialButton(
onPressed: () {},
child: new Text(‘Hello’),
padding: new EdgeInsets.only(left: 10.0, right: 10.0),
),
),
);
}
这里看到其实在Flutter中是没有xml布局文件的存在了,取而代之的是直接在override的build方法中去布局,这其实有点类似RN,这里的build方法就类似RN中的render方法,只不过RN通过JSX使得render方法中通过xml语法来完成布局,而Flutter则是完全通过Dart语法来完成布局。可读性上我个人还是更喜欢Flutter,xml与js混写还是觉得有点别扭。 这里列出了Flutter提供的所有的布局。
小结: 在Flutter中不存在xml的布局形式,Widget的布局在build方法中直接构建。
怎么从布局中添加或者删除一个组件
-
in Android
-
我们可以调用addChild或者removeChild方法去动态的添加或者删除一个ViewGroup中的View。
-
in Flutter
-
因为widget是不可变的所以不能直接的addChild或者removeChild。但是可以传递一个返回Widget的方法给它的Parent,然后通过一个boolean值在该方法中控制要返回的Widget。
下面的代码展示了如何通过点击FloatingActionButton来触发在两个Widget之间切换:
import ‘package:flutter/material.dart’;
void main() {
runApp(new SampleApp());
}
class SampleApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: ‘Sample App’,
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() => new _SampleAppPageState();
}
class _SampleAppPageState extends State {
// Default value for toggle
bool toggle = true;
void _toggle() {
setState(() {
toggle = !toggle;
});
}
_getToggleChild() {
if (toggle) {
return new Text(‘Toggle One’);
} else {
return new MaterialButton(onPressed: () {}, child: new Text(‘Toggle Two’));
}
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(“Sample App”),
),
body: new Center(
child: _getToggleChild(),
),
floatingActionButton: new FloatingActionButton(
onPressed: _toggle,
tooltip: ‘Update Text’,
child: new Icon(Icons.update),
),
);
}
}
代码也比较简单,关键在于_SampleAppPageState的build方法中Center的构造方法中的child参数传的是一个_getToggleChild方法。该方法通过一个toggle变量来决定返回给Center的是一个怎样的Widget。而toggle的赋值同样是由点击FloatingActionButton后调用setState来改变的。也就是说将toggle作为SampleAppPage的状态保存下来,在展示的时候由toggle的值来动态决定要展示的是什么Widget。
小结: 在Flutter中不能直接动态的去添加或者删除一个Widget到Widgets Tree中,因为Flutter中的Widget是不可变的。但我们可以依赖StatefulWidget根据State的不同来灵活的构建不同的Widget。
怎样对一个Widget做动画
- in Android
- 我们可以通过通过xml文件或者调用View.animate()方法创建一个动画。
- in Flutter
- 我们将需要做动画的Widget包裹到一个Transition中来实现。
像Android一样,在Flutter中我们也有AnimationController和Interpolator,Interpolator通过继承Animation类实现,比如下面例子中用到的CurvedAnimation。我们传递AnimationController和Animation到一个Widget中,然后通过AnimationController来启动动画。 下面的例子展示了使用FadeTransition来实现当按下按钮时将展示Logo的FlutterLogo Widget淡出的效果:
import ‘package:flutter/material.dart’;
void main() {
runApp(new FadeAppTest());
}
class FadeAppTest extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: ‘Fade Demo’,
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyFadeTest(title: ‘Fade Demo’),
);
}
}
class MyFadeTest extends StatefulWidget {
MyFadeTest({Key key, this.title}) : super(key: key);
final String title;
@override
_MyFadeTest createState() => new _MyFadeTest();
}
class _MyFadeTest extends State with TickerProviderStateMixin {
AnimationController controller;
CurvedAnimation curve;
@override
void initState() {
controller = new AnimationController(duration: const Duration(milliseconds: 2000), vsync: this);
curve = new CurvedAnimation(parent: controller, curve: Curves.easeIn);
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Container(
child: new FadeTransition(
opacity: curve,
child: new FlutterLogo(
size: 100.0,
)))),
floatingActionButton: new FloatingActionButton(
tooltip: ‘Fade’,
child: new Icon(Icons.brush),
onPressed: () {
controller.forward();
},
),
);
}
}
整个Widget Tree还是跟之前类似,MaterialApp的构造函数中home参数传入的依然是我们自定义的一个继承自StatefulWidget的MyFadeTest,_MyFadeTest是其对应的State,其中定义了AnimationController和CurvedAnimation,AnimationController用于控制动画,CurvedAnimation是一个插值器实现。接着在build方法中通过将我们需要动画的FlutterLogo Widget包裹在一个FadeTransition中来让FlutterLogo Widget产生动画,最后在按下FloatingActionButton的回调中使用AnimationController.forward()方法来触发动画。 这里或者那里查看更多关于动画的具体细节。
小结: 在Flutter中也有AnimationController和插值器,通过AnimationController来控制动画的播放,插值器改变动画播放的加速度。 使用时先构造AnimationController,然后将构造好的AnimationController作为参数构造插值器,最后将构造好的插值器作为参数构造Transition。之后就可以通过AnimationController来控制Transition中包含的Widget的动画执行。
怎样使用Canvas去画内容
- in Android
- 我们可以用Canvas去画一些自定义的图形在屏幕上。
- in Flutter
- CustomPaint和CustomPainter这两个类可以帮助我们在Canvas上作画。
下面的代码实现一个可自由签名的Widget:
import ‘package:flutter/material.dart’;
class SignaturePainter extends CustomPainter {
SignaturePainter(this.points);
final List points;
void paint(Canvas canvas, Size size) {
Paint 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);
}
}
bool shouldRepaint(SignaturePainter other) => other.points != points;
}
class Signature extends StatefulWidget {
SignatureState createState() => new SignatureState();
}
class SignatureState extends State {
List _points = [];
Widget build(BuildContext context) {
return new GestureDetector(
onPanUpdate: (DragUpdateDetails details) {
setState(() {
RenderBox referenceBox = context.findRenderObject();
Offset localPosition =
referenceBox.globalToLocal(details.globalPosition);
_points = new List.from(_points)…add(localPosition);
});
},
onPanEnd: (DragEndDetails details) => _points.add(null),
child: new CustomPaint(painter: new SignaturePainter(_points)),
);
}
}
class DemoApp extends StatelessWidget {
Widget build(BuildContext context) => new Scaffold(body: new Signature());
}
void main() => runApp(new MaterialApp(home: new DemoApp()));
可以看到CustomPaint和CustomPainter搭配使用来实现了向Canvas上绘制自定义内容的目的。首先自定义SignaturePainter继承于CustomPainter,并重写paint方法,在本例中paint方法首先采用链式写法构造一个Paint实例paint,接着遍历points列表的内容来画线。points列表是构造SignaturePainter时传入的,里面保存了触摸屏幕的事件点的信息。其他部分其实与之前的结构都差不多。关键在SignatureState的build方法中返回的是一个GestureDetector,这是一个能够帮助我们捕获手势信息的Widget,在它的构造函数中child参数传入的是一个CustomPaint,CustomPaint的构造函数又传入了一个我们自定义的SignaturePainter,并且将手势捕获事件时捕获到的_points列表在这个时候传递给SignaturePainter。于是CustomPaint就和CustomPainter产生化学反应,相互配合完成在Canvas上作画的效果。
效果如下:
小结: 在Flutter中实现在Canvas上作画需要CustomPaint和CustomPainter相互配合,首先继承CustomPainter自定义一个Painter并重写paint方法实现绘制逻辑。然后将自定义的CustomPainter传递给CustomPaint的构造方法,CustomPaint作为Widget Tree中的一个Widget使用自定义的CustomPainter完成绘制。
怎样构建自定义Widget
- in Android
- 自定义View一般通过继承View或者已经存在的其他组件并重写一些关键方法来实现。
- in Flutter
- 自定义一个Widget不是通过继承而是通过组合其他widgets。
让我们来看一个栗子:
class CustomButton extends StatelessWidget {
final String label;
CustomButton(this.label);
@override
Widget build(BuildContext context) {
return new RaisedButton(onPressed: () {}, child: new Text(label));
}
}
代码很简单,看到CustomButton一样还是继承于StatelessWidget。构造函数接受一个字符串参数并保存在内部成员变量label中。在build方法中返回的是一个RaisedButton(一个Flutter提供的Widget),巧妙的地方是在RaisedButton的构造函数中传入了一个Text(一个Flutter提供的Widget)作为其child参数。这个Text Widget显示的就是CustomButton的label成员中的内容。
在使用CustomButton的时候可以像使用其他Widget一样直接使用:
override
Widget build(BuildContext context) {
return new Center(
child: new CustomButton(“Hello”),
);
}
}
小结: 在Flutter中自定义Widget是通过组合不同的Widget来实现的,自定义的Widget只继承于StatelessWidget或者StatefulWidget,通过build方法中组合其他的Widget来实现自定义Widget。
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
5792219246)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!