一位 Android 程序员入坑 Flutter 后整理出一份超详细的学习笔记
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 的位置,如alignment: 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 发送回程序进程,还记得进程间通信办法之一的管道
吗...
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
的文件,具体支持的规则可以查看这个文档。
Fragment 与 Activity 呢?
之前做过类比,如MaterialApp
有点类似于 Activity,而Scaffold
都点类似 Fragment,实际上他们两个都是 Flutter 的 Widgets,也就是说其实只有 View 的概念了。
还有生命周期吗?
Flutter 有一个叫做WidgetsBinding
的可以提供类似生命周期的回调。
四种状态inactive
?(iOS 专用),paused
(相当于 onPause,退后台),resumed
(相当于 onPostResume,到前台),suspending
(android 专用,相当于 onStop)。
一般在 StatefulWidgets 的 State 中注册与反注册。
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
ScrollView vs ListView
Flutter 没有 ScrollView,合并到了 ListView,通过 ListView.builder 创建的 ListView 提供了 View 复用的逻辑。
ListView.builder(
itemCount: widgets.length,
itemBuilder: (BuildContext context, int position) {
return Text(xxx);
}))
其中 itemBuilder 有点像 Android ListView 的 getView,官方文档说它会自动回收 Element 给你,但是事实上每次你都需要根据 position 生成新的 Widgets,所以呢应该是 Flutter 在内部回收了之前的 Widgets 并在你重新创建的时候又用上了。
BTW,通过 ListView 构造来显示就不具备这种特性,所以大量数据需要用 Builder。
Flutter 横竖屏怎么玩
因为它实际上还是借助了 Android 程序的壳子,所以如果 AndroidManifect 定义了android:configChanges="orientation|screenSize"
,则 Flutter 会自己 hanlde。
怎么处理 Gesture
Flutter 提供了GestureDetector
,它相当于一个 Container,将我们期望接收手势的 Widgets 放进去,再实现事件回调就行了。
GestureDetector(
child: FlutterLogo(
size: 200.0,
),
评论