一位 Android 程序员入坑 Flutter 后整理出一份超详细的学习笔记
如何启动一个 app
Android 需要在 Manfest 里面指定带有 MAIN action 与 LAUNCHER category 的 Activity 声明,而 Flutter 只需要一行。
void main() => runApp(MyApp());
其中 MyApp 就是一个普通的 Widgets(View).
View vs Widgets
Flutter 没有 View,与之对应的是 Widget,并且分为 StatelessWidgets 与 StatefulWidgets,前者是个静态 View,后者是动态通过 Data 来更新的 View。
Stateless
Text(
'I like Flutter!',
);
Stateful
class StatefulText extends StatefulWidget {
@override
State<StatefulWidget> createState() => _TextState();
}
class _TextState extends State<StatefulText> {
// Default placeholder text
String textToShow = "I Like Flutter";
void _updateText() {
setState(() {
// update the text
textToShow = "Flutter is Awesome!";
});
}
@override
Widget build(BuildContext context) {
...invoke _updateText
}
}
实际上是因为 StatefulWidgets 通过调用State
的setState
方法来触发整个 Widgets 树的重绘,并且在重绘之前会调用传进去的(){ ... }
block。
怎么写 Layout, XML 到哪里去了
实际上 Flutter 没有 xml 了, 并且是通过 Widgets 的嵌套来实现一个布局的。
如:
Center
是一个可以把子 View 放置在中央的容器;Row
对应的就是 LinearLayout + Horizontal,Column
对应的就是 LinearLayout + Vertical,他们都具备一个属性叫做crossAxisAlignment
,有点类似gravity
,来控制子 View 相对于父 View 的位置。Expanded
支持一个类似 weight 的属性,叫flex
。Container
是一个具有decoration
属性的容器,可以用来控制背景色,border, margin 等等。Stack
有点像是一个特殊的 RelatetiveLayout 或者 ConstraintLayout,children
属性指定了它的子 View,第一个是 Base View,alignment
属性指定了后面的子 View 相对于 BaseView 的位置,如`alignm
ent: const Alignment(0.6, 0.6)`指定了位于 BaseView 右下角的位置。
ListTile
是一个特殊的 ListItem,有三个属性,分别是左边的 Icon (leading),文字 (title),以及右边的 Icon (trailing)。还有诸如
ListView
,GridView
,Card
等等比较熟悉的 Widgets。
另外有一个类似于我们 Activity 的 Widgets:
叫做
MaterialApp
, 可以指定theme
,?title
, 以及子 View?home
, 还有更重要的页面跳转routes
.
MaterialApp(
title: 'Welcome to Flutter',
home: ...,
routes: <String, WidgetBuilder> ...,
theme: ThemeData(
primaryColor: Colors.white
),
)
还有一个类似于 Fragment 的:
叫做
Scaffold
,中文意思是脚手架
,它包含一个 appBar (ActionBar)与一个 body,appBar 可以指定 title 与 actions (类似于 action button 的点击事件)。
Scaffold(
appBar: AppBar(
title: Text(widget.title),
actions: <Widget>[...],
),
body: ...,
)
如何从父 View 中 Remove 一个元素
答案是没有... 因为在 Flutter 看来吗,Widgets 的树结构是不可以被更改的,但是如果想更改,则是通过 StatefulWidgets 的方法,通过 setState 来更改 Data,触发 Widgets 重绘,从而替换掉之前的 Widgets。
喜欢画 Canvas 的同学怎么办?
Flutter 同样支持,CustomPaint
作为一个 Widgets 就支持传入一个实现CustomPainter
抽象类的参数,而CustomPainter
的抽象方法也类似于 Android View 的onDraw
。
void paint(Canvas canvas, Size size)
bool shouldRepaint(CustomPainter oldDelegate)
如何自定义 View
不用继承,而使用类似 Android ViewGroup 的办法,通过组合(composing)与封装的方法来实现,通过小 Widgets 组合成需要的新 Widgets。
页面跳转怎么办,四大组件之一的 Intent 跑哪里去了
貌似在讲类似于 Activity 的MaterialApp
的时候剧透了...
就是使用Navigator
与Routes
来实现界面跳转,实际上是整个 Widgets 的替换。
routes: <String, WidgetBuilder> {
'/a': (BuildContext context) => MyPage(title: 'page A'),
'/b': (BuildContext context) => MyPage(title: 'page B'),
'/c': (BuildContext context) => MyPage(title: 'page C'),
}
Navigator.of(context).pushNamed('/b');
如何处理外部的 Intent
实际上还是需要在 Flutter App 的 Android 壳子中注册这个 filter,然后在 FlutterActivity 中拿到存下来。
FlutterView 初始化后再通过 Bridge,官方叫MethodChannel
从 Java 里获取,进行下一步逻辑。
可以看个简单的例子:
new MethodChannel(getFlutterView(), "app.channel.shared.data").setMethodCallHandler(
new MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
if (call.method.contentEquals("getSharedText")) {
result.success(sharedText);
sharedText = null;
}
}
});
getSharedText() async {
var sharedData = await platform.invokeMethod("getSharedText");
if (sharedData != null) {
setState(() {
dataShared = sharedData;
});
}
}
常用的 startActivityForResult 怎么办.
这个 Flutter 有完全对应的办法,而且用起来很方便,结合之前说的页面跳转。
Map xxx = await Navigator.of(context).pushNamed('/xxx');
Navigator.of(context).pop({xxx});
异步怎么办, runOnUiThread()哪里去了
Flutter 有点像 JS,是一个单线程模式,所以只是通过模拟来构建简单的异步,关键字就是类似于 kotlin coroutines 一样,通过await
+async
来处理。
如:
loadData() async {
response = await http.get(xxx);
setState(() {xxx});
}
但是由于它的单线程,所以无法做很长的阻塞操作,像 http 请求的延迟正常情况可能都是毫秒级的,但是数据的处理等,可能就得秒级了。
这也是 RN 在线程方面的做 android 程序的一个痛点,Flutter 采用了比较容易想到的曲线救国的办法,提供了一个叫Isolate
的对象,它实际是一个基于 socket 的数据通道,相当于把数据放在一个独立的进程进行处理,然后再通过 socket 发送回程序进程,还记得进程间通信办法之一的管道
吗...
具体 API 可以参考文档[1...](
),[2...](
).
Flutter 替代 OkHttp 的网络库
自带了 http 库,直接http.get(url)
,在线程部分的代码实例里也有涉及。
通过类似 gradle 的文件pubspec.yaml
引入。
dependencies:
...
http: ^0.12
^
表示不升大版本,并取最新版本,比 gradle 的+要范围更小。
常见的 LCE(Loading Content Error)里面的 Loading 怎么 show
Flutter 有一个 widget 叫做ProgressIndicator
,比如我们期望有一个转圈圈的 Loading 界面在数据加载出来之前。
我们就可以通过 StatefulWidgets,根据数据,或者 List Widgets 的个数 (如果是显示一个 List 的话)来判断是否显示 Loading,使用子类CircularProgressIndicator
,来替换页面的 Widgets。
当然也是通过 setState(() {...})来触发界面刷新的,可以在 initState()内触发加载数据的异步操作。
不同分辨率的图片资源怎么放
这个有点像 iOS 了, 即有 1x,2x,3x:
images/my_icon.png // Base: 1.0x image
images/2.0x/my_icon.png // 2.0x image
images/3.0x/my_icon.png // 3.0x image
不一样的一点还需要添加到类似 gradle 的文件pubspec.yaml
里。
assets:
images/my_icon.jpeg
字符串怎么存储
Flutter 没有像 Android 的string.xml
的东西,目前来说最好的就就是存成静态字符串。
class Strings {
static String welcomeMessage = "Welcome To Flutter";
}
Text(Strings.welcomeMessage)
Gradle 变成什么了
前面说网络库,图片资源的时候提到过,提供了一个叫pubspec.yaml
的文件,具体支持的规则可以查看[这个文档](
)。
评论