0%

flutter get started

今天尝试了一下flutter,记一下过程

安装和运行

get-started

  1. 首先它说需要安装Git for Windows ,好像没有用到这个,因为我本身就已经有了,所以不知道没装会不会ok,建议安装

  2. 然后是下载 flutter_windows_v0.9.4-beta.zip ,300多M,这个是真的慢,我看了全局vpn依然很慢

  3. 解压出 flutter 文件夹,并且设置环境变量指向 flutter\bin ,有一万多个文件把,解压也是超慢。

  4. dos命令行运行 flutter doctor,检测环境设置,因为已经安装了Android-Studio了,所以jdk,sdk,AS,模拟器 那一块都没有问题,如果打算使用visio studio code 的话需要安装 flutter插件才能被发现。

  5. 如果打算使用AS的话,需要依次搜索安装插件 Dart 和 flutter ,这里我开了全局vpn依然安装不了flutter插件,不知道为什么,根据它错误提示里面的地址,我复制到浏览器里面下载了zip格式的插件然后导入到AS里面。

  6. 使用AS的话,直接创建Flutter项目就行,创建的时候记得勾选offline,不过即使是offline了,创建过程还是不知道为什么卡了很久很久。使用vsc的话,直接ctrl+p ,输入 > ,再选择 Flutter: New Project 就行,直接就创建好了,或者使用命令行:

    1
    2
    flutter create myapp
    cd myapp

    创建后打开 myapp文件夹也行,也很快。

  7. 运行就,点击AS 的run按钮 ,或者vsc的debug(记得先点一下右边的配置小按钮),或者 flutter run,按 R 是实时部署代码。

IDE的选择的话,我本来是倾向于vsc的,dart看起来像是ts一类的语言语法,但是实际打开后发现vsc来做这个的话,内存占用也到了七八百M了,和AS不相上下了。抽空看了看 google io 上的一些 flutter 的介绍视频,里面的程序员也都是用的 AS,所以还是决定用 AS 了。

大概看了下美团和咸鱼的试用报告,了解了下渲染效率和原理,觉得还是不错的。特别是看了使用flutter30分钟实现一个跨平台的IM app之后,觉得有点惊艳。谷歌的firebase和service全家桶套餐原来还有远程数据库的功能。视频里就演示了下双端读取同步数据库来实现IM,有些意思。

Demo

官网文档的演示教程分1,2两部分,第一部分简单介绍了下text,展示了一个列表:

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
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Startup Name Generator',
home: RandomWords(),
);
}
}

class RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];

final _biggerFont = const TextStyle(fontSize: 18.0);

Widget _buildSuggestions() {
return ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: (context, i) {
// Add a one-pixel-high divider widget before each row in theListView.
if (i.isOdd) return Divider();

final index = i ~/ 2;
if (index >= _suggestions.length) {
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
});
}

Widget _buildRow(WordPair pair) {
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Startup Name Generator'),
),
body: _buildSuggestions(),
);
}
}

class RandomWords extends StatefulWidget {
@override
RandomWordsState createState() => new RandomWordsState();
}

通过 void main() => runApp(MyApp()); 来启动一个界面,MyApp则是一个 StatelessWidget 的控件,需要实现的虚方法 build要求返回一个 Widget

对于有状态变化的控件,需要使用 StatefulWidget 控件。继承后虚方法要求返回一个State. 然后新建一个状态类继承 State,把控件的名字作为 泛型参数写进去,State 的子类则需要返回一个 Widget。

可以使用 Scaffold 类来包裹 Widget 作为body参数的值来返回,可以使用ListView.builder(padding,ListTile)来构造一个列表控件。

感觉和rn相比,理解起来更接近客户端的思维。rn写起来则是更接近web的思维,尖括号大括号小括号一起上,还要用 StyleSheet.create 来写css,很不习惯。

第二部分展示了一下路由和页面交互的功能。呃,前端里面的路由意思就是页面跳转,这个也是看rn的时候迷糊了一段才明白。

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or press Run > Flutter Hot Reload in IntelliJ). Notice that the
// counter didn't reset back to zero; the application is not restarted.
primarySwatch: Colors.blue,
),
home: RandomWords(),
);
}
}

class RandomWordState extends State<RandomWords> {
final _suggestions = <WordPair>[];

final _saved = new Set<WordPair>();
final _biggerFont = const TextStyle(fontSize: 18.0);

@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Startup Name Generator'),
actions: <Widget>[
// Add 3 lines from here...
new IconButton(icon: const Icon(Icons.list), onPressed: _pushSaved),
], // ... to here.
),
body: _buildSuggestions(),
);
}

Widget _buildSuggestions() {
return ListView.builder(
padding: const EdgeInsets.all(16.0),
// The itemBuilder callback is called once per suggested word pairing,
// and places each suggestion into a ListTile row.
// For even rows, the function adds a ListTile row for the word pairing.
// For odd rows, the function adds a Divider widget to visually
// separate the entries. Note that the divider may be difficult
// to see on smaller devices.
itemBuilder: (context, i) {
// Add a one-pixel-high divider widget before each row in theListView.
if (i.isOdd) return Divider();

// The syntax "i ~/ 2" divides i by 2 and returns an integer result.
// For example: 1, 2, 3, 4, 5 becomes 0, 1, 1, 2, 2.
// This calculates the actual number of word pairings in the ListView,
// minus the divider widgets.
final index = i ~/ 2;
// If you've reached the end of the available word pairings...
if (index >= _suggestions.length) {
// ...then generate 10 more and add them to the suggestions list.
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
});
}

Widget _buildRow(WordPair pair) {
final bool alreadySaved = _saved.contains(pair);
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
trailing: new Icon(
// Add the lines from here...
alreadySaved ? Icons.favorite : Icons.favorite_border,
color: alreadySaved ? Colors.red : null,
), // ... to here.
onTap: () {
// Add 9 lines from here...
setState(() {
if (alreadySaved) {
_saved.remove(pair);
} else {
_saved.add(pair);
}
});
},
);
}

void _pushSaved() {
Navigator.of(context).push(
new MaterialPageRoute<void>(
// Add 20 lines from here...
builder: (BuildContext context) {
final Iterable<ListTile> tiles = _saved.map(
(WordPair pair) {
return new ListTile(
title: new Text(
pair.asPascalCase,
style: _biggerFont,
),
);
},
);
final List<Widget> divided = ListTile.divideTiles(
context: context,
tiles: tiles,
).toList();

return new Scaffold(
appBar: new AppBar(
title: const Text('Saved suggestions'),
),
body: new ListView(children: divided),
);
},
), // ... to here.
);
}
}

class RandomWords extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return new RandomWordState();
}
}

按钮的话是通过增加一个 onPressed: _pushSaved方法来实现,js里面对于这种突然而然出来的参数已经见怪不怪了。。路由则是通过 Navigator.of(context).push( MaterialPageRoute(builder:Widget) )来实现。其中用到了一些简单的图片,都是内置的。