flutter,哎没办法,也是拉完了兄弟,学点跨端,指不准还能有一口饭吃。

state

widget树类似于这个dom树。所以的flutter画面都是由widget组成的。我是感觉flutter和compose react真的很像。与其说是节点,不如说我这个都是由组件组成,不同的组件拆成不同的颗粒度而已。

1
2
3
4
5
6
7
8
9
10
11
12
13
// React 的 JSX
<div>
<Text>hello</Text>
<Button onClick={handleClick}>click</Button>
</div>

// Flutter 的 Widget
Column(
children: [
Text('hello'),
ElevatedButton(onPressed: handleClick, child: Text('click')),
],
)

然后这里也是类似于 React 的 useState / Compose 的 remember + mutableStateOf,flutter有一个StatefulWidget,也是记录状态的,而StatelessWidget就是不适用state记录的静态网页。
具体而言,就是你写了这个StatefulWidget,它你这里用到这些值,它都会监听状态的,改变了就会重建。
然后这里一个细节,就是它这里很像这个mvvm,viewmodel的感觉,它这里有个state,用来让数据和UI解耦。你这个statefulwidget需要实现一个createState的接口,然后这个state就表示你用这些数据来做什么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Widget 类:不可变的配置(类似 props)
class Counter extends StatefulWidget {
final String title; // 外部传入的参数,不会变
const Counter({super.key, required this.title});

@override
State<Counter> createState() => CounterState();
}

// State 类:可变的状态 + 构建 UI
class CounterState extends State<Counter> {
int count = 0; // 可变状态

@override
Widget build(BuildContext context) {
return Column(
children: [
Text(widget.title), // 通过 widget.xxx 访问 Widget 的参数
Text('$count'),
ElevatedButton(
onPressed: () {
setState(() { // 修改状态必须包在 setState 里
count++;
});
},
child: Text('加一'),
),
],
);
}
}

怎么说呢,就flutter 会频繁地销毁和重建 Widget 对象,但 State 对象会保留。这样状态不会丢失
然后这里的下划线,就是当前文件私有。

provider/reader

这个也是和react很像啊,你一直往下面传值的话,有些值就是要一直用到的,比如我这个可能有个点击是否展开一些设置,这个设置页面是否打开这个bool值,有可能会被它里面的child用到,比如漫画,可能会需要判断某些手势是否需要拦截。这个时候就需要用到这种不用一直传值的,就外面写一个provider,然后里面就能拿到。然后这里的应该是有好几个,reader/select/watch。我用到的就是reader,这里还有很多问题,这里的reader是写的一个拓展函数。拓展函数就很像是这个kotlin的那个了,不过它这个还能命名,能用来分组区分具体干嘛来的。

布局

横向就是row,纵向column,然后好像还有stack,类似于帧布局framelayout,就一层层往上堆叠的这种吧。有一些自带的listview好像,记不太清了。点击是onTap,其他的也是类似,click改成tap。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 按钮自带 onPressed
ElevatedButton(onPressed: () { print('clicked'); }, child: Text('按钮'))

// 给任意 widget 加点击
GestureDetector(
onTap: () { print('tapped'); },
onDoubleTap: () { print('double tapped'); },
onLongPress: () { print('long pressed'); },
child: Text('点我'),
)

// 更底层的触摸事件(不参与手势竞争)
Listener(
onPointerDown: (event) { print('finger down at ${event.position}'); },
onPointerMove: (event) { print('finger moved'); },
onPointerUp: (event) { print('finger up'); },
child: Text('摸我'),
)
MediaQuery

这个东西,感觉是applywindowInsets里面的这个insets,用来专门做这个沉浸式的,能拿到导航栏边距这些。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
dart
// 屏幕尺寸
final size = MediaQuery.sizeOf(context);
print('宽: ${size.width}, 高: ${size.height}');

// 安全区域(刘海、状态栏、底部导航条)
// 类似 Android 的 WindowInsets.systemBars
final padding = MediaQuery.paddingOf(context);
print('状态栏高: ${padding.top}, 底部导航: ${padding.bottom}');

// 系统手势区域(Android 全面屏返回手势占的区域)
// 类似 Android 的 WindowInsets.systemGestures
final gestures = MediaQuery.systemGestureInsetsOf(context);
print('左边缘手势区: ${gestures.left}');

语法糖

这个get啊,就函数前面加一个限定get,它这个东西就形式上,就能当这个属性来用,它不用写函数的括号。我看上去就有点像是这个kotlin的by委托来了。