flutter基础知识
# flutter基础知识
java环境的配置(重启后才生效)
http://www.runoob.com/java/java-environment-setup.html (opens new window)
真机测试
[TOC]
# 一、开发环境
# 1.1 开发工具
- Android Studio
- https://develop.android.com/studio/intro
- 安装Flutter和Dart插件
- Android Studio -> Files > Settings > Plugins -> Browse repositories -> Flutter plugin
- 获取Flutter镜像
- Flutter官网 -> Using Flutter in China
- 获取Flutter SDK
# 二、入门基础知识
# 2.1 创建并运行项目
$ flutter create <projectname>
$ flutter run -d 'iPhone X'
1
2
2
# 2.2 Widget
import 'package:flutter/XXX.dart';
1
- 写一个Hello world
import 'package:flutter/material.dart';
void main() {
runApp(
Center(
child: Text(
'Hello, world!',
textDirection: TextDirection.ltr,
),
),
);
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 三、项目结构、资源、依赖和本地化
# 3.1 项目文件结构
- projectname
-
- android - Android部分的工程文件
- build - 项目的构建输出目录
- ios - iOS部分的工程文件
- lib - 项目中的Dart源文件
- assets - 存放静态资源
- models - 存放所有数据类
- navigator - 存放导航相关
- pages - 存放所有的页面
- provider - 存放所有的 Provider(状态管理)
- requests - 存放请求相关
- routes - 存放路由相关
- utils - 存放所有的工具类
- widgets - 存放所有封装好的组件
- src - 包含其他源文件
- main.dart - 项目入口文件
- test - 测试相关文件
- pubspec.yaml - 项目依赖配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 3.2 图片资源
assets -存放任意类型文件,需在pubspec.yaml中声明。
- assets可以被放置到任何属性文件夹--Flutter并没有预先定义的文件结构。所以需要在pubspec.yaml文件中声明assets的位置,然后Flutter会把它们识别出来。
assets:
assets/filename (此行的assets即为文件夹的名字)
1
2
2
- 参考教程:https://flutterchina.club/assets-and-images/
# 3.2 strings资源
# 四、动画Animation
# 4.1 logo放大动画
# 4.1.1 基础版
import 'package:flutter/material.dart';
class ScaleAnimationRoute extends StatefulWidget {
_ScaleAnimationRouteState createState() => new _ScaleAnimationRouteState();
}
// 需要继承TickerProvider,如果有多个AnimationController,则应该使用TickerProviderStateMixin。
class _ScaleAnimationRouteState extends State<ScaleAnimationRoute>
with SingleTickerProviderStateMixin {
Animation<double> animation;
// AnimationController是一个特殊的Animation对象,在屏幕刷新的每一帧,就会生成一个新的值
// 默认情况下,AnimationController在给定的时间段内会线性的生成从0.0到1.0的数字
// 用来控制动画的开始与结束以及设置动画的监听
AnimationController controller;
initState() {
// 执行的是父级的initState()
super.initState();
// vsync参数,存在vsync时会防止屏幕外动画(动画的UI不在当前屏幕时)消耗不必要的资源
// duration 动画的时长,设置的 seconds: 2 为2秒,当然也可以设置毫秒 milliseconds:2000.
controller = new AnimationController(
duration: const Duration(seconds: 1), vsync: this);
// 使用弹性曲线
animation=CurvedAnimation(parent: controller, curve: Curves.bounceIn);
// 图片宽高从0变到300
animation = new Tween(begin: 0.0, end: 300.0).animate(controller)
..addListener(() { // 用来更新UI
// 要改变 widget 的宽高,那么就需要 setState(...) 来让 widget 重绘
setState(() => {});
});
// 启动动画(正向执行)
controller.forward();
}
Widget build(BuildContext context) {
return Container(
color: Colors.blue[50],
child: Center(
child: Image.asset("images/logo.png",
width: animation.value, height: animation.value),
));
}
dispose() {
//路由销毁时需要释放动画资源,防止内存泄漏
controller.dispose();
super.dispose();
}
}
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 4.1.2 升级版
import 'package:flutter/material.dart';
class AnimatedImage extends AnimatedWidget {
AnimatedImage({Key key, Animation<double> animation})
: super(key: key, listenable: animation);
Widget build(BuildContext context) {
final Animation<double> animation = listenable;
return new Center(
child: Image.asset("images/logo.png",
width: animation.value,
height: animation.value
),
);
}
}
class ScaleAnimationRoute extends StatefulWidget {
_ScaleAnimationRouteState createState() => new _ScaleAnimationRouteState();
}
class _ScaleAnimationRouteState extends State<ScaleAnimationRoute>
with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
initState() {
super.initState();
controller = new AnimationController(
duration: const Duration(seconds: 3), vsync: this);
//图片宽高从0变到300
animation = new Tween(begin: 0.0, end: 300.0).animate(controller);
//启动动画
controller.forward();
}
Widget build(BuildContext context) {
return AnimatedImage(animation: animation,);
}
dispose() {
//路由销毁时需要释放动画资源
controller.dispose();
super.dispose();
}
}
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 4.1.3 高级版
import 'package:flutter/material.dart';
class ScaleAnimationRoute extends StatefulWidget {
_ScaleAnimationRouteState createState() => new _ScaleAnimationRouteState();
}
class GrowTransition extends StatelessWidget {
GrowTransition({this.child, this.animation});
final Widget child;
final Animation<double> animation;
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: new Center(
child: new AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget child) {
return new Container(
height: animation.value,
width: animation.value,
child: child);
},
child: child),
));
}
}
class _ScaleAnimationRouteState extends State<ScaleAnimationRoute>
with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
initState() {
super.initState();
controller = new AnimationController(
duration: const Duration(seconds: 3), vsync: this);
//图片宽高从0变到300
animation = new Tween(begin: 0.0, end: 300.0).animate(controller);
//启动动画
controller.forward();
}
Widget build(BuildContext context) {
return GrowTransition(
child: Image.asset("images/logo.png"),
animation: animation,
);
}
dispose() {
//路由销毁时需要释放动画资源
controller.dispose();
super.dispose();
}
}
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
- 动画放大再缩小再放大...(循环动画)
initState() {
super.initState();
controller = new AnimationController(
duration: const Duration(seconds: 3), vsync: this);
//图片宽高从0变到300
animation = new Tween(begin: 0.0, end: 300.0).animate(controller);
animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
//动画执行结束时反向执行动画
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
//动画恢复到初始状态时执行动画(正向)
controller.forward();
}
});
//启动动画
controller.forward();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 4.2 Hero动画
# 五、Provider状态管理
# 尽量在 Model 中使用私有变量“_”。
# 5.1 在子页面中获取状态
# 5.1.1 Provider.of(context)
当状态改变时,会刷新整个页面,不能获取最佳性能。
import 'package:flutter/material.dart';
// ChangeNotifier 用来自动管理所有听众
class CounterModel with ChangeNotifier {
int _count = 0;
int get value => _count;
void increment() {
_count++;
notifyListeners(); // 通知所有听众进行刷新
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
Widget build(BuildContext context) {
final _counter = Provider.of<CounterModel>(context);
final textSize = Provider.of<int>(context).toDouble();
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'value: ${_counter.value}',
style: TextStyle(fontSize: textSize),
),
RaisedButton(
child: Text("animate"),
onPressed: counter.increment
),
]),
));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 5.1.2 Consumer
- 极大缩小控件刷新范围,只会刷新consumer部分。
- 作者只提供到了consume6。
class SecondPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
body: Consumer2<CounterModel,int>(
builder: (context, CounterModel counter, int textSize, _) => Center(
child: Text(
'Value: ${counter.value}',
style: TextStyle(
fontSize: textSize.toDouble(),
),
),
),
),
floatingActionButton: Consumer<CounterModel>(
builder: (context, CounterModel counter, child) => FloatingActionButton(
onPressed: counter.increment,
child: child,
),
child: Icon(Icons.add),
),
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 5.1.3 Selector
- 避免一个 Provider 可能会为不同业务的多个控件提供不同的数据。
// A: 从顶层获取的 Provider 的类型
// S:获取到的 Provider 中真正对我们有用的类型,需要在 selector 中返回该类型,使整个刷新范围从Provider变成了S
Selector<A, S>(
// 储存 selector 过滤后的值,也就是 selector 返回的 S 并拿收到通知之后新的 S 与 cache 的 S 比较,来判断这个 Selector 是否需要刷新,默认 preview != next 就刷新。
// 这里写false则不刷新
shouldRebuild: (pre, next) => false,
// 入参会将获取的顶层 provider 传入,再返回具体关注的那部分 S。
selector: (context, provider) => provider,
// 第一个参数是具体返回Widget的地方,第二个参数是S,第三个参数用于优化一些不用刷新的部分。
builder: (context, provider, child) {
return null;
},
)
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13