开始 Flutter 之路

2019-05-06

目录:

  1. Flutter
  2. 搭建环境
  3. 实现第一个功能
  4. 遇到的问题
  5. 总结

1. Flutter


Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。 Flutter可以与现有的代码一起工作。在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter是完全免费、开源的。

优点:
[1] 快速开发
我是一个在任务完成上追求速度的人,很喜欢快速完成一个任务时的成就感,所以这是我选择 Flutter 的一个关键原因。毫秒级的热重载,修改后,应用界面会立即更新。这真的是太棒了!亲自体验过,真的超级钟意~

[2] 富有表现力和灵活的UI
一直都很喜欢 Material Design Style , 也一直认为作为一个程序媛应该有美的概念和对美的欣赏,Flutter 提供的 UI 的表现力很强而且很灵活。

[3] 原生性能
在我刚毕业之初,H5 开发很热门,后来微信小程序也占据了一定的热度,但我还是坚持在原生 app 开发的行列,因为我体验过这两种程序,相比较原生,原生的更为流畅,微信小程序还会有一个很严重的问题,就是当小程序卡顿了,会连带微信无响应,提醒用户关闭程序或者等待。然而,Flutter包含了许多核心的widget,如滚动、导航、图标和字体等,这些都可以在iOS和Android上达到原生应用一样的性能

Flutter

#2. 搭建环境

[1] Mac Air 笔记本一台
[2] Android Studio 3.1.1 已安装
[3] Flutter SDK 下载–>安装–>配置环境变量 教程
1.下载 Flutter
image.png

  1. 打开 Terminal
    输入 vi ./.bash_profile,回车;
    回车(查看模式);
    输入“i”,进入insert模式;
    添加环境变量;
    编辑完成,点击“esc键,退出insert模式”, 然后输入“:wq!”,回车,保存成功;
    输入“source ./.bash_profile”,让环境变量生效;
    输入”echo $PATH”,查看环境变量,发现添加成功;
    重新打开终端,环境变量就会生效了。
    输入 flutter -h;
    输入 flutter doctor;

[4] 在 Android Studio 安装 Flutter 插件,如下图所示:
2019-5-30
[5] 创建你的第一个 Flutter Project,开始你的 Flutter 之路。

2019-5-30

首次创建 Flutter Project 有点慢,请耐心等待⌛️,创建好了之后可以直接 run 试试看。
2019-5-30

#3. 实现第一个功能

3.1 需求
文字描述:
单词列表,用户可以查看或者收藏单词,收藏的单词在另外一个界面显示。

具体展示:
2019-5-30

3.2 实现
[1] 在 pubspec.yaml 中添加单词库 english_words: ^3.1.0

1
2
3
4
5
6
7
8
dependencies:
flutter:
sdk: flutter

# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
english_words: ^3.1.0

[2] lib 文件夹下的 main.dart 文件中做如下修改:

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
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 new MaterialApp(
title: 'Startup Name Generator',
theme: new ThemeData(
primaryColor: Colors.white,
),
home: new RandomWords(),
);
}
}

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

class RandomWordsState extends State<RandomWords> {

final _suggestions = <WordPair>[];

final _saved = new Set<WordPair>();

final _biggerFont = const TextStyle(fontSize: 18.0);

//此方法构建显示建议单词对的ListView
Widget _buildSuggestions() {
return new ListView.builder(
padding: const EdgeInsets.all(16.0),
// 对于每个建议的单词对都会调用一次itemBuilder,然后将单词对添加到ListTile行中
// 在偶数行,该函数会为单词对添加一个ListTile row.
// 在奇数行,该函数会添加一个分割线widget,来分隔相邻的词对。
// 注意,在小屏幕上,分割线看起来可能比较吃力。
itemBuilder: (context, i) {
// 在每一列之前,添加一个1像素高的分隔线widget
if (i.isOdd) return new Divider();

// 语法 "i ~/ 2" 表示i除以2,但返回值是整形(向下取整),比如i为:1, 2, 3, 4, 5
// 时,结果为0, 1, 1, 2, 2, 这可以计算出ListView中减去分隔线后的实际单词对数量
final index = i ~/ 2;
// 如果是建议列表中最后一个单词对
if (index >= _suggestions.length) {
// ...接着再生成10个单词对,然后添加到建议列表
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
}
);
}

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

void _pushSaved() {
Navigator.of(context).push(
new MaterialPageRoute(
builder: (context) {
final tiles = _saved.map(
(pair) {
return new ListTile(
title: new Text(
pair.asPascalCase,
style: _biggerFont,
),
);
},
);
final divided = ListTile
.divideTiles(
context: context,
tiles: tiles,
)
.toList();
return new Scaffold(
appBar: new AppBar(
title: new Text('Saved Suggestions'),
),
body: new ListView(children: divided),
);
},
),
);
}

@override
Widget build(BuildContext context) {
//final wordPair = new WordPair.random();
// return new Text(wordPair.asPascalCase);
return new Scaffold (
appBar: new AppBar(
title: new Text('Startup Name Generator'),
actions: <Widget>[
new IconButton(icon: new Icon(Icons.list), onPressed: _pushSaved),
],
),
body: _buildSuggestions(),
);
}
}

###4. 遇到的问题

[1] Mac 配置环境变量,打开终端,输入 source ./.bash_profile

1
2
3
4
5
6
export PATH=/Users/用户名/Documents/flutter/flutter/bin:$PATH
export ANDROID_HOME="/Users/用户名/Documents/android_sdk" //android sdk目录,替换为你自己的即可
export PATH=${PATH}:${ANDROID_HOME}/tools
export PATH=${PATH}:${ANDROID_HOME}/platform-tools
export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn

[2] flutter doctor 检测结果有些是不成功的,出现 ! 或者 ✖️ ,解决方案

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
/Users/sweetgirl/flutter/bin/flutter --no-color doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel beta, v1.5.4-hotfix.2, on Mac OS X 10.12 16A323, locale zh-Hans-CN)
[!] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
! Some Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses
[!] iOS toolchain - develop for iOS devices
✗ Xcode installation is incomplete; a full installation is necessary for iOS development.
Download at: https://developer.apple.com/xcode/download/
Or install Xcode via the App Store.
Once installed, run:
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
✗ Verify that all connected devices have been paired with this computer in Xcode.
If all devices have been paired, libimobiledevice and ideviceinstaller may require updating.
To update with Brew, run:
brew update
brew uninstall --ignore-dependencies libimobiledevice
brew uninstall --ignore-dependencies usbmuxd
brew install --HEAD usbmuxd
brew unlink usbmuxd
brew link usbmuxd
brew install --HEAD libimobiledevice
brew install ideviceinstaller
✗ ios-deploy not installed. To install:
brew install ios-deploy
✗ CocoaPods not installed.
CocoaPods is used to retrieve the iOS platform side's plugin code that responds to your plugin usage on the Dart side.
Without resolving iOS dependencies with CocoaPods, plugins will not work on iOS.
For more info, see https://flutter.dev/platform-plugins
To install:
brew install cocoapods
pod setup
[✓] Android Studio (version 3.1)
[✓] Connected device (1 available)

[3] 分享一个 Flutter UI 库 – Flutter Awesome
[4] 遇到一个问题,尝试过搜索引擎提供的方法都没有解决。

flutter: Unable to load asset: assets/lake.jpg

最后的解决方法如下:
2019-5-30

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

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

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
Widget titleSection = new Container(
padding: const EdgeInsets.all(32.0),
child: new Row(
children: [
new Expanded(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
new Container(
padding: const EdgeInsets.only(bottom: 8.0),
child: new Text(
'牛油果肉丁意粉',
style: new TextStyle(
fontWeight: FontWeight.bold,
),
),
),
new Text(
'广州, 广东',
style: new TextStyle(
color: Colors.grey[500],
),
),
],
),
),
new Icon(
Icons.star,
color: Colors.red[500],
),
new Text('66'),
],
),
);

Column buildButtonColumn(IconData icon, String label) {
Color color = Theme.of(context).primaryColor;

return new Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
new Icon(icon, color: color),
new Container(
margin: const EdgeInsets.only(top: 8.0),
child: new Text(
label,
style: new TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.w400,
color: color,
),
),
),
],
);
}

Widget buttonSection = new Container(
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
buildButtonColumn(Icons.call, 'CALL'),
buildButtonColumn(Icons.near_me, 'ROUTE'),
buildButtonColumn(Icons.share, 'SHARE'),
],
),
);

Widget textSection = new Container(
padding: const EdgeInsets.all(32.0),
child: new Text(
'''
抗氧抗衰老:牛油果含有丰富的甘油酸、蛋白质及维他命,润而不腻,是天然的抗氧衰老剂,不但能软化和滋润皮肤,还能收细毛孔,皮肤表面可以形成乳状隔离层,能够有效抵御阳光照射,防止晒黑晒伤。果仁里提取的牛油果油营养丰富,含丰富的维生素E、镁、亚油酸和必需脂肪酸,有助于强韧细胞膜,延缓表皮细胞衰老的速度。 ''',
softWrap: true,
),
);

return new MaterialApp(
title: 'Flutter Demo',
home: new Scaffold(
body: new ListView(
children: [
new Image.asset(
'images/lake.jpg',
width: 600.0,
height: 240.0,
fit: BoxFit.fitWidth,
),
titleSection,
buttonSection,
textSection,
],
),
),
theme: new ThemeData(
primarySwatch: Colors.blue,
),
);
}
}

热加载后效果如下:
2019-5-30

5. 总结


对于新事物的出现,有人是抵触,有人是接受,有人是欢喜,有人是嗤之以鼻,无论别人如何,做好自己,多学一点不吃亏。换言之,用马原的话来说,一切事物都是发展的,都处在新事物的产生和旧事物的灭亡中。最近喜欢看哲学类的书籍了~

文章是 Android 面向需求开发系列中的一文,更多相关文章请关注。如若有什么问题,也可以通过扫描二维码发消息给我。转载请注明出处,谢谢!

二维码

作者:Emily CH
2019年5月7日