入坑Flutter ,棄坑React-Native(簡單體驗)
前言
由于業務需要之前用react-native重寫了公司項目,中間碰到各種坑,莫名其妙紅屏,適配坑,頁面刷新坑,打包坑,熱更新坑.折磨了兩個月終于完成.年初就聽說了Flutter,看一下是用Google的Dart語言,據說是有望代替JavaScript這個一周完成的奇葩.咳咳,看了下國內咸魚團隊已經在項目中使用了Flutter技術,這是他們的社區社區博文深入理解flutter的編譯原理與優化,想不到竟然如此強大.忍不住就參考官方文檔寫了個demo
Flutter官網
https://flutter.io/get-started/install/
克隆Flutter倉庫
git clone -b beta https://github.com/flutter/flutter.git
配置環境變量
export PUB_HOSTED_URL=https://pub.flutter-io.cn //國內用戶需要設置
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn //國內用戶需要設置
export PATH= flutter所在目錄/flutter/bin:$PATH
可直接將環境變量寫入
~/.bash_profile
文件中執行source $HOME/.bash_profile
生效(如果未生效請重啟終端或電腦)
配置命令行工具sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
安裝鏈接設備工具
brew update
brew install --HEAD libimobiledevice
brew install ideviceinstaller ios-deploy cocoapods
pod setup
VScode 配置
搜索dart code
安裝dart 插件
快捷鍵 shift + command + p 輸入 ‘doctor’, 然后選擇 ‘Flutter: Run Flutter Doctor’ 驗證操作
同樣 shift + command + p 輸入 ‘flutter’, 然后選擇 ‘Flutter: New Project’ action 創建一個測試項目 vscode 底部展示附加設備
點擊Debug>Start Debugging即可運行項目
創建
按照之前的方法創建一個模板 shift + command + p 輸入 ‘flutter’, 然后選擇 ‘Flutter: New Project’ action
替換 lib/main.dart. 刪除lib / main.dart中的所有代碼,然后替換為下面的代碼,它將在屏幕的中心顯示“Hello World”
//應用入口
//main函數使用了(=>)符號, 這是Dart中單行函數或方法的簡寫
void main() => runApp(new DemoApp());
class DemoApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Welcome to Flutter',
home: new Scaffold(
appBar: new AppBar(
title: new Text('Welcome to Flutter'),
),
body: new Center(
child: new Text('Royce and Owen'),
),
),
);
}
}
外部包使用
- 您可以 在pub.dartlang.org上找到english_words軟件包以及其他許多開源軟件包
- 將english_words添加到pubspec.yaml文件中(pubspec文件
- 管理Flutter應用程序的assets(資源,如圖片、package等))
- 在終端中運行
flutter packages get
安裝依賴
flutter packages get
Running "flutter packages get" in startup_first... 0.6s
- 導入
import 'package:english_words/english_words.dart';
- 使用 English words 包生成文本來替換字符串“Hello World”.
- 修改代碼
class DemoApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final wordPair = new WordPair.random();
return new MaterialApp(
title: 'Welcome to Flutter',
home: new Scaffold(
appBar: new AppBar(
title: new Text('Welcome to Flutter'),
),
body: new Center(
child: new Text(wordPair.asUpperCase),
),
),
);
}
}
添加一個 有狀態的部件(Stateful widget)
Stateless widgets 是不可變的, 這意味著它們的屬性不能改變 - 所有的值都是最終的.
Stateful widgets 持有的狀態可能在widget生命周期中發生變化. 實現一個 stateful widget 至少需要兩個類:
- 一個 StatefulWidget類。
- 一個 State類。 StatefulWidget類本身是不變的,但是 State類在widget生命周期中始終存在.
- 新建一個組件類RandomWords
- 新建一個State組件類RandomWordsState
class RandomWordsState extends State<RandomWords> {
@override
Widget build(BuildContext context) {
final wordPair = new WordPair.random();
return new Text(wordPair.asPascalCase);
}
}
class RandomWords extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new RandomWordsState();
}
}
- 修改代碼
final wordPair = new WordPair.random();// 刪除此行
class DemoApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Welcome to Flutter',
home: new Scaffold(
appBar: new AppBar(
title: new Text('Welcome to Flutter'),
),
body: new Center(
child: new RandomWords(),
),
),
);
}
}
創建一個無限滾動ListView
擴展(繼承)RandomWordsState類,以生成并顯示單詞對列表。 當用戶滾動時,ListView中顯示的列表將無限增長
- 在RandomWordsState添加一個變量_suggestions(下劃線前綴標識符,會強制其變成私有的)
- 添加一個_biggerFont變量來孔子字體大小
- 新建_buildSuggestions()方法 顯示建議的單詞對
- 在RandomWordsState中添加一個_buildRow函數
- 更新RandomWordsState的build方法以使用_buildSuggestions()
- 更新MyApp的build方法
class RandomWords extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new RandomWordsState();
}
}
class RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _biggerFont =
const TextStyle(fontSize: 18.0, color: Color.fromARGB(1, 234, 111, 22));
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('StartUp'),
),
body: _buildSuggestions());
}
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) {
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
});
}
Widget _buildRow(WordPair pair) {
return new ListTile(
title: new Text(
pair.asPascalCase,
style: _biggerFont,
),
);
}
}
添加交互
- 添加一個 _saved Set(集合) 到RandomWordsState。這個集合存儲用戶喜歡(收藏)的單詞對
- 在 _buildRow 方法中添加 alreadySaved來檢查確保單詞對還沒有添加到收藏夾中。
- 同時在 _buildRow()中, 添加一個心形 ?? 圖標到 ListTiles以啟用收藏功能
- 在 _buildRow中讓心形??圖標變得可以點擊。如果單詞條目已經添加到收藏夾中, 再次點擊它將其從收藏夾中刪除。當心形??圖標被點擊時,函數調用setState()通知框架狀態已經改變。
Widget _buildRow(WordPair pair) {
//判斷是否收藏
final bool alreadySaved = _saved.contains(pair);
return new ListTile(
title: new Text(
pair.asPascalCase,
style: _biggerFont,
),
subtitle: new Text('哈哈哈'),
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);
}
});
},
);
}
導航到新頁面
在Flutter中,導航器管理應用程序的路由棧。將路由推入(push)到導航器的棧中,將會顯示更新為該路由頁面。 從導航器的棧中彈出(pop)路由,將顯示返回到前一個路由。這點類似iOS的導航機制
- 給AppBar的actions屬性添加一個按鈕子組件,由于是復數用 <Widget>[]包裝
- 當用戶點擊導航欄中的列表圖標時,建立一個路由并將其推入到導航管理器棧中。此操作會切換頁面以顯示新路由在MaterialPageRoute的builder屬性中構建,builder是一個匿名函數。
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
actions: <Widget>[
new IconButton(icon: new Icon(Icons.list), onPressed: _pushSaved)
],
title: new Text('嘿嘿嘿',
style: const TextStyle(
fontSize: 18.0, color: Color.fromRGBO(150, 22, 123, 1.0))),
),
body: _buildSuggestions());
}
void _pushSaved() {
Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
//便利以保存的單詞 創建tile 或者叫(cell,item)
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),
);
}));
}
效果
使用主題更改UI
創建ThemeData來定義theme
class DemoApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Startup Name Generator',
//主題
theme: new ThemeData(
primaryColor: Colors.orange,
),
home: new RandomWords(),
);
}
}
ThemeData提供相當多的屬性,自定義程度相當高了,不愧是基于OpenGL構建的UI,比起RN自由度高了很多
Material library中的 Colors類也提供了許多可以使用的顏色常量例如Colors.green
,Color.fromRGBO(255, 111, 233, 1.0)
部分屬性介紹
accentColor → Color 控件的前景色(旋鈕、文本、覆蓋邊緣效果等)。
accentColorBrightness → Brightness
dividerColor → Color 分隔符和彈窗分隔符的顏色,也用于ListTiles和DataTables的行之間
primaryColor → Color 應用程序主要部分的背景顏色(工具欄,標簽欄等)
以上第一flutterdemo完成.
Dart語法非常類似JS,相比較RN但少了</>更加易懂,擁類似state狀態機,熱加載速度很快,C++底層,基于OpenGL,強大的API和文檔支持.