Flutter 簡介(事件、路由、異步請求)


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關鍵字;
  1. var修飾變量;
  2. final表示不可變的,修飾內置數據類型,值不可變;修飾對象表示引用不可變,使用到的頻率很高;
  3. const是編譯時常量,他表示始終不可變,無論修飾內置類型還是對象,或者是數據結構;
  4. 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

(1)RaisedButton 漂浮按鈕 (2)FlatButton 扁平按鈕 (3) OutlineButton  (4) IconButton 

  幾種按鈕,觸發事件都是 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這顆大樹,設計之初就是作為跨多終端來實現的,這兩年的發展也很快,國內很多團隊有些已經在使用了。

官方發布版本也很勤,隨着生態的逐漸完善,還是有很大的發展空間的,值得期待。

 

參考:

Flutter 官網

科普Dart語言

Flutter 中文網


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM