1. 前言
Flutter是一個由谷歌開發的開源移動應用軟件開發工具包,用於為Android和iOS開發應用,同時也將是Google Fuchsia下開發應用的主要工具。其官方編程語言為Dart。
同為跨端開發的react-native的語言是JavaScript,RN是通過原生之間橋接來實現,而flutter是通過dart虛擬機直接編譯。
這篇就不和 React-Native 進行詳細對比 ,而是從前端開發的角度來看flutter。
2. Dart簡介
Dart 比 JAVA 簡單,易於理解,比 JavaScript 更加規范,更加工程化,兼具靜態和動態語言的一些特性。
- 強類型,可以直接生命變量類型,也可以類型推斷;支持可選類型,用戶可以像JavaScript一樣寫弱類型的定義,也可以確定類型。你可以寫出動態語言風格的代碼,也可以寫出類似於傳統靜態風格的代碼。Dart的類型推導使用final,var,const,dynamic關鍵字;
var
修飾變量;final
表示不可變的,修飾內置數據類型,值不可變;修飾對象表示引用不可變,使用到的頻率很高;const
是編譯時常量,他表示始終不可變,無論修飾內置類型還是對象,或者是數據結構;dynamic
是任意類型,有點像java里面的Object
,Kotlin中的Any
。
- 單線程異步事件模型, 獨特的隔離區( Isolate ),可以實現多線程;Dart是基於單線程模型的語言。但是在開發當中我們經常會進行耗時操作比如網絡請求,這種耗時操作會堵塞我們的代碼,所以在Dart也有並發機制,名叫isolate。APP的啟動入口
main
函數就是一個類似Android主線程的一個主isolate。和Java的Thread不同的是,Dart中的isolate無法共享內存,類似於Android中的多進程。
- DartVM,具有極高的運行效率和優秀的代碼運行優化;
- 面向對象編程,一切數據類型均派生自 Object ;
- 運算符重載,泛型支持;
- 異步API: Future (async/await)和 Stream(異步事件流) 模型,可以簡單實現高效的代碼;
- Minix 特性,可以更好的實現方法復用;
- 在語法上,Dart 提供了很多便捷的操作,可以明顯減少代碼量。比如字符連接,可以直接
"my name is $name, age is $age"
,無需+
號拼接,也無需做類型轉換(相當於ES6的模板字符串)。
更多參見官方文檔
3. 從前端的角度看Flutter
對於前端來說,頁面就分為 HTML+CSS+JS
頁面結構和樣式 Flutter for Web
對於習慣了html+css結合chrome devtools 完成UI效果的前端開發來說,剛開始用這種方式極其不習慣。
本來簡單的一個結構和一個樣式,加個背景色,漸變這種,css輕松搞定,然而,用widget來就要嵌套好多層,只能說,習慣習慣就好了。。
3.1 先來認識一下Flutter
Hello word入門
lib/main.dart
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); // StatelessWidget和StatefulWidget兩種 // 兩者的區別在於狀態的改變,StatelessWidget面向那些始終不變的UI控件;而StatefulWidget則是面向可能會改變UI狀態的控件。 class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Welcome to Flutter', // Title 是用來定義任務管理窗口界面所看到應用名字的 home: Scaffold( appBar: AppBar( // 導航欄 title: Text('Welcome to Flutter'), ), body: Center( child: Text('Hello World'), ), ), ); } }
3.2 路由跳轉傳參
在前端傳參很簡單,是直接攜帶攜帶參數(params和query)跳轉路由頁面的
flutter里面的路由可以分成兩種,一種是直接注冊,不能傳遞參數。另一種要自己構造實例,可以傳遞參數。
分兩種方法 push 和 pushNamed
3.2.1 靜態路由的注冊
return new MaterialApp( title: 'Flutter Demo', theme: new ThemeData( primarySwatch: Colors.blue, ), home: new MyHomePage(title: 'Flutter實例'), routes: <String, WidgetBuilder> { // 這里可以定義靜態路由,不能傳遞參數 '/login': (BuildContext context) => new SecondPage(), '/register': (BuildContext context) => new RouterHomePage(), }, );
3.2.2 靜態路由的使用
onPressed: () { Navigator.of(context).pushNamed("/register"); // or Navigator.pushNamed(context, '/register') },
3.2.3 動態路由的使用
Navigator.of(context).push(
MaterialPageRoute(builder: (_) { return new SecondPage(title: '傳參過去'); }));
3.2.4 關閉頁面
Navigator.pop(context); // or Navigator.of(context).pop(); Navigator.pop(context,"攜帶參數"); // 可攜帶參數
3.2.5 push , pushNamed 傳參
Pass arguments to a named route
https://flutter.dev/docs/cookbook/navigation/navigate-with-arguments
用 ModalRoute.of()封裝路由及傳參方法
push方法封裝,有一個缺點就是不能只在主頁面定義導入跳轉的頁面,略有代碼重復
用pushNamed封裝參數傳遞方法,就比較方便,不用再每一個頁面import將要跳轉的頁面
main.dart
import 'package:flutter/material.dart'; import 'package:app/pages/home/app.dart'; import 'package:app/pages/login/login.dart'; import 'package:app/pages/login/register.dart'; import 'package:app/pages/login/forget.dart'; import 'package:app/pages/home/search_recommend.dart'; import 'package:app/pages/home/search_result.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'myapp', routes: { "/register": (context) => RegisterPage(), "/login": (context) => LoginPage(), "/forget": (context) => ForgetPage(), "/search": (context) => SearchRecommendPage(), "/searchresult": (context) => SearchResultPage(), }, theme: new ThemeData(primarySwatch: Colors.red), home: AppPage(), ); } }
從login跳轉到register的時候,攜帶參數,並在register頁面顯示相應參數
login.dart
import 'package:app/pages/login/register.dart'; // 用push方法的話就要用到這個 onPressed: () => { // 點擊按鈕跳轉事件 Navigator.push( // push方法--支持StatelessWidget和StatefulWidget context, MaterialPageRoute( builder: (context) => RegisterPage(), settings:RouteSettings(arguments: {'title': '手機號注冊'}))) // or pushNamed 方法 // Navigator.pushNamed(context, '/register', // arguments: {'title': '手機號注冊'}) },
register.dart 接收參數
class _RegisterPageState extends State<RegisterPage> { @override Widget build(context) { // 參數的接收顯示 final Map args = ModalRoute.of(context).settings.arguments; return Scaffold( appBar: AppBar( title: Text(args['title']), ), body: Center( child: Text('register'), ), } }
3.3 事件觸發
3.3.1 觸發按鈕的點擊事件
https://book.flutterchina.club/chapter3/buttons.html
幾種按鈕,觸發事件都是 onPressed 點擊事件
3.3.2 手勢識別GestureDetector
https://book.flutterchina.club/chapter8/gesture.html
常見事件:點擊、雙擊、長按
3.3.3 toast , alert 等彈窗
toast可直接安裝 fluttertoast 包進行使用 https://pub.dev/packages/fluttertoast
alert, confirm 彈窗使用
部分代碼
import 'package:flutter/material.dart'; onTap: () => showDialog( context: context, builder: (_) => _generateAlertDialog("我是彈框的內容")), ) _generateAlertDialog(String contents) { return AlertDialog( title: Text('這是標題'), content: Text(contents), actions: <Widget>[ FlatButton( child: Text('取消'), onPressed: () { Navigator.of(context).pop(); // 點擊取消關掉彈窗 }, ), FlatButton( child: Text('確認'), onPressed: () { Navigator.of(context).pop(); // 點擊確認關掉彈窗再跳轉到別的頁面 Navigator.of(context).pushNamed('/register'); }, ), ], ); }
4. 異步請求
flutter中的請求方式
- Dart 原生的網絡請求
HttpClient
- 庫
http
- Flutter中文網發布的
dio
4.1 HttpClient
4.2 使用官方 http 庫
https://flutter.dev/docs/cookbook/networking/fetch-data
4.3 使用 dio http請求框架
https://book.flutterchina.club/chapter10/dio.html
dio
比較流行的一個請求包,它支持Restful API、FormData、攔截器、請求取消、Cookie管理、文件上傳、文件下載等
先看簡單的請求
import 'dart:async'; import 'package:dio/dio.dart'; var dio = new Dio(); void getHttp() async { try { Response response = await dio.get("http://www.google.com"); print(response); } catch (e) { print(e); } }
可以封裝一個通用請求模板,利用攔截器一並處理通用模塊-比如請求前加loading, 結束關閉loading, 統一網絡請求錯誤處理等
utils文件夾下的 net.dart
import 'dart:async'; import 'package:dio/dio.dart'; Dio dio; class NetRequest { static Future<Dio> instance() async{ if (dio == null) { dio = new Dio(); } //添加攔截器 dio.interceptors.add( InterceptorsWrapper( onRequest: (RequestOptions options) { print("請求之前"); // Do something before request is sent return options; //continue }, onResponse: (Response response) { print("響應之前"); // Do something with response data return response; // continue }, onError: (DioError e) { print("錯誤之前"); // Do something with response error return e; //continue }, ), ); return dio; } static Future get(String url, Map<String, dynamic> params) async { var response = await (await instance()).get(url, queryParameters: params); print(response.data.toString()); return response.data; } static Future post(String url, Map<String, dynamic> params) async { var response = await (await instance()).post(url, data: params); print(response.data.toString()); return response.data; } }
發起請求
import 'package:flutter/material.dart'; import 'package:app/utils/net.dart'; class MyFriends extends StatefulWidget { MyFriends({Key key}) : super(key: key); @override _MyFriendsState createState() => _MyFriendsState(); } class _MyFriendsState extends State<MyFriends> { void _getInfo() async { Map list = await NetRequest.get(url,{}); print(list); // 發起get請求之后獲得的數據 } @override void initState() { super.initState(); _getInfo(); } Widget build(BuildContext context) { return Scaffold( body: Container( child: Text('friends'), ), ); } }
post 請求同理
4. 開發者工具
https://flutter.github.io/devtools
Dart DevTools還是預覽版,目前功能也還比較簡單
VsCode Dart: Open DevTools命令打開
5. 后記
剛開始寫flutter各種不習慣,和js的模式不一樣,更偏向於后端語言,作為跨端語言的新秀,又有Google這顆大樹,設計之初就是作為跨多終端來實現的,這兩年的發展也很快,國內很多團隊有些已經在使用了。
官方發布版本也很勤,隨着生態的逐漸完善,還是有很大的發展空間的,值得期待。
參考: