Flutter 開發(fā)從 0 到 1(二)框架與網(wǎng)絡(luò)

image

框架

之前做 APP 開發(fā)的時候禾锤,我都是擔(dān)任 Android 組 leader,新項目起來,我會做技術(shù)預(yù)研滥搭,如《一套完整的 Android 通用框架》,一般會使用 MVP 模式(現(xiàn)在應(yīng)該是 MVVM 模式)捣鲸,網(wǎng)絡(luò)請求框架使用 Retrofit瑟匆,圖片加載使用 Glide,圖片縮放和裁剪分別使用 PhotoView 和 uCrop 等栽惶,必要時愁溜,我會寫個 sample 放項目里疾嗅,讓同事可以參考。

image

這個也是個新項目冕象,我也需要做下技術(shù)預(yù)研代承,F(xiàn)lutter 網(wǎng)絡(luò)請求框架需要使用什么?圖片加載又使用什么渐扮?文章詳情论悴,我打算使用 Markdown,這 Flutter 能實現(xiàn)嗎墓律?等等膀估,這些都是需要事前做好調(diào)研。

這個項目耻讽,代碼版本管理用 GitHub察纯,首先新建一個 Flutter 項目,GitHub 也新建個私有項目(暫時不公開吧)针肥,用如下命令將本地代碼和遠(yuǎn)程 GitHub 關(guān)聯(lián)起來捐寥。

echo "# andblog" >> README.md
git init
git add README.md
git commit -m "first commit"

git remote add origin https://github.com/WuXiaolong/andblog.git
git push -u origin master

關(guān)聯(lián) OK,后面修改祖驱,就直接使用 Android Studio 自帶的 Git 來提交代碼握恳。

接下來來看看 Flutter 網(wǎng)絡(luò)請求框架使用什么?怎么使用捺僻?

網(wǎng)絡(luò)

數(shù)據(jù)來源

說到網(wǎng)絡(luò)請求框架乡洼,首先要解決數(shù)據(jù)從何而來,我沒有后端(其實我可以開發(fā))匕坯,沒有服務(wù)器束昵,怎么搞?莫急葛峻,都說本系列文章是從零開發(fā) APP锹雏,且能一個人做一個項目,我自然有辦法术奖。

數(shù)據(jù)我使用的 Bmob礁遵,它可以創(chuàng)建你想要的表,支持 RestAPI采记,這可以為做 APP 省去后端開發(fā)成本佣耐,當(dāng)然像 Bmob 提供這樣的服務(wù)有很多,就不一一介紹唧龄,Bmob 如何使用兼砖,也不說了,官方有很詳細(xì)的文檔,你可以點擊文章底部「閱讀原文」注冊個賬號玩玩讽挟。

http

網(wǎng)絡(luò)請求框架的數(shù)據(jù)有了懒叛,可以玩起來了。

以請求文章列表接口示例耽梅,先用 Postman 看下數(shù)據(jù)結(jié)構(gòu):

{
    "results": [
        {
            "content": "文章內(nèi)容測試1",
            "cover": "http://pic1.win4000.com/wallpaper/2020-04-21/5e9e676001e20.jpg",
            "createdAt": "2020-07-05 13:50:58",
            "date": "2020.07.01",
            "objectId": "ct7BGGGV",
            "summary": "摘要1",
            "title": "標(biāo)題測試1",
            "updatedAt": "2020-07-05 13:53:16"
        },
        {
            "content": "文章內(nèi)容測試2",
            "cover": "http://pic1.win4000.com/wallpaper/2020-04-21/5e9e676001e20.jpg",
            "createdAt": "2020-07-05 13:52:37",
            "date": "2020.07.02",
            "objectId": "3L42777G",
            "summary": "摘要2",
            "title": "標(biāo)題測試2",
            "updatedAt": "2020-07-05 13:53:10"
        }
    ]
}

Flutter 提供了網(wǎng)絡(luò)請求框架是 http薛窥,地址:https://pub.flutter-io.cn/packages/http

添加 http 包,在 pubspec.yaml 添加:

dependencies:
  http: ^0.12.1

項目根目錄執(zhí)行命令flutter pub get安裝軟件包褐墅。

新建 blog_list_page.dart 用來展示文章列表拆檬,blog.dart 是文章列表的結(jié)構(gòu)表洪己,把入口 main.dart 直接加載文章列表妥凳,詳細(xì)代碼如下。

main.dart:

import 'package:flutter/material.dart';

import 'andblog/list/blog_list_page.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'AndBlog',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: new BlogListPage(),
    );
  }
}

blog_list_page.dart:

import 'package:flutter/material.dart';
import 'package:flutter_andblog/andblog/http/http_common.dart';
import 'package:http/http.dart' as http;
import 'blog.dart';

class BlogListPage extends StatefulWidget {
  @override
  BlogListPageState createState() => new BlogListPageState();
}

class BlogListPageState extends State<BlogListPage> {
  List<Blog> blogList = [];

  @override
  void initState() {
    super.initState();
    //一進(jìn)頁面就請求接口
    getBlogListData();
  }

  //網(wǎng)絡(luò)請求
  getBlogListData() async {
    var response = await http.get(HttpCommon.blog_list_url, headers: HttpCommon.headers());
    if (response.statusCode == 200) {
      // setState 相當(dāng)于 runOnUiThread
      setState(() {
        blogList = Blog.decodeData(response.body);
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('AndBlog'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

把網(wǎng)絡(luò) url 都放在 HttpCommon答捕,詳細(xì)代碼在 http_common.dart:

class HttpCommon{

  static var blog_list_url = 'https://api2.bmob.cn/1/classes/ArticleTable/';

  static Map<String, String> headers(){
    //設(shè)置header
    Map<String, String> headers = new Map();
    headers["X-Bmob-Application-Id"] = "bmob Application-Id";
    headers["X-Bmob-REST-API-Key"] = "bmob REST-API-Key";
    headers["Content-Type"] = "application/json";
    return headers;
  }

}

網(wǎng)絡(luò)請求數(shù)據(jù)解析放在 blog.dart:

import 'dart:convert';

class Blog{
  final String content;
  final String cover;
  final String date;
  final String objectId;
  final String summary;
  final String title;

  //構(gòu)造函數(shù)
  Blog({
    this.content,
    this.cover,
    this.date,
    this.objectId,
    this.summary,
    this.title,
  });

  static List<Blog> decodeData(String jsonData) {
    List<Blog> blogList = new List<Blog>();
    var data = json.decode(jsonData);
    var results = data['results'];
    print('results='+results[0]['content']);
    for (int i = 0; i < results.length; i++) {
      blogList.add(fromMap(results[i]));
    }
    return blogList;
  }

  static Blog fromMap(Map<String, dynamic> map) {

    return new Blog(
      content: map['content'],
      cover: map['cover'],
      date: map['date'],
      objectId: map['objectId'],
      summary: map['summary'],
      title: map['title'],
    );
  }
}

我習(xí)慣性打印print('results='+results[0]['content']);看看數(shù)據(jù)解析對不對逝钥,多次嘗試最后打印文章內(nèi)容測試1,達(dá)到了預(yù)期拱镐。

json_serializable

在寫文章列表的結(jié)構(gòu) blog.dart 需要手動一個個敲字段艘款,然后解析,F(xiàn)lutter 有沒有像 GsonFormat 這樣自動解析的插件沃琅,當(dāng)然是有哗咆,是 json_serializable,使用 json_serializable益眉,你需要一個常規(guī)依賴晌柬,以及兩個 dev 依賴:

dependencies:
  flutter:
    sdk: flutter
  json_annotation: ^3.0.1


dev_dependencies:
  flutter_test:
    sdk: flutter
  build_runner: ^1.10.0
  json_serializable: ^3.3.0

項目根目錄執(zhí)行命令flutter pub get安裝軟件包。

以文章詳情結(jié)構(gòu)體示例:

{
    "content": "文章內(nèi)容測試1",
    "cover": "http://pic1.win4000.com/wallpaper/2020-04-21/5e9e676001e20.jpg",
    "createdAt": "2020-07-05 13:50:58",
    "date": "2020.07.01",
    "objectId": "ct7BGGGV",
    "summary": "摘要1",
    "title": "標(biāo)題測試1",
    "updatedAt": "2020-07-05 13:53:16"
}

根據(jù) json 創(chuàng)建實體類 detail.dart:

import 'package:json_annotation/json_annotation.dart';

//為了使實體類文件找到生成文件郭脂,需要 part 'detail.g.dart'
part 'detail.g.dart';

@JsonSerializable()
class Detail{
  final String content;
  final String cover;
  final String date;
  final String objectId;
  final String summary;
  final String title;

  //構(gòu)造函數(shù)
  Detail({
    this.content,
    this.cover,
    this.date,
    this.objectId,
    this.summary,
    this.title,
  });
}

剛寫完 detail.g.dart 會報錯年碘,這是正常的!因為我們還沒生成解析文件展鸡。

接下來解析屿衅,項目根目錄執(zhí)行命令flutter packages pub run build_runner build

會發(fā)現(xiàn)生成一個 detail.g.dart 文件:

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'detail.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

Detail _$DetailFromJson(Map<String, dynamic> json) {
  return Detail(
    content: json['content'] as String,
    cover: json['cover'] as String,
    date: json['date'] as String,
    objectId: json['objectId'] as String,
    summary: json['summary'] as String,
    title: json['title'] as String,
  );
}

Map<String, dynamic> _$DetailToJson(Detail instance) => <String, dynamic>{
      'content': instance.content,
      'cover': instance.cover,
      'date': instance.date,
      'objectId': instance.objectId,
      'summary': instance.summary,
      'title': instance.title,
    };

然后把這兩個方法放到 detail.dart:

factory Detail.fromJson(Map<String, dynamic> json) => _$DetailFromJson(json);

Map<String, dynamic> toJson() => _$DetailToJson(this);

接下來就可以調(diào)用 fromJson 方法解析網(wǎng)絡(luò)請求的數(shù)據(jù):

var data = json.decode(response.body);
detail = Detail.fromJson(data);
print('results='+detail.title);

這樣看下來,使用 json_serializable 并沒有方便多少莹弊,只是把解析字段省了涤久,最煩沒有把添加字段步驟自動化,比 GsonFormat 弱爆了忍弛。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拴竹,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子剧罩,更是在濱河造成了極大的恐慌栓拜,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異幕与,居然都是意外死亡挑势,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進(jìn)店門啦鸣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來潮饱,“玉大人,你說我怎么就攤上這事诫给∠憷” “怎么了?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵中狂,是天一觀的道長凫碌。 經(jīng)常有香客問我,道長胃榕,這世上最難降的妖魔是什么盛险? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮勋又,結(jié)果婚禮上苦掘,老公的妹妹穿的比我還像新娘。我一直安慰自己楔壤,他們只是感情好鹤啡,可當(dāng)我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蹲嚣,像睡著了一般递瑰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上端铛,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天泣矛,我揣著相機與錄音,去河邊找鬼禾蚕。 笑死您朽,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的换淆。 我是一名探鬼主播哗总,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼倍试!你這毒婦竟也來了讯屈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤县习,失蹤者是張志新(化名)和其女友劉穎涮母,沒想到半個月后谆趾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡叛本,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年沪蓬,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片来候。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡跷叉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出营搅,到底是詐尸還是另有隱情云挟,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布转质,位于F島的核電站园欣,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏峭拘。R本人自食惡果不足惜俊庇,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一狮暑、第九天 我趴在偏房一處隱蔽的房頂上張望鸡挠。 院中可真熱鬧,春花似錦搬男、人聲如沸拣展。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽备埃。三九已至,卻和暖如春褐奴,著一層夾襖步出監(jiān)牢的瞬間按脚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工敦冬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留辅搬,地道東北人。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓脖旱,卻偏偏與公主長得像堪遂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子萌庆,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 這是系列文章的第三篇溶褪,為了更快上車,建議按照順序践险,將前兩篇文章通讀一遍猿妈。從原生開發(fā)到Flutter教程(一)認(rèn)識F...
    KavinZhou閱讀 3,674評論 8 32
  • 跨平臺開發(fā)之Flutter 主要內(nèi)容: 跨平臺框架對比 Flutter介紹 Flutter快速入門向?qū)?必備插件介...
    大于于閱讀 777評論 1 0
  • 久違的晴天吹菱,家長會。 家長大會開好到教室時彭则,離放學(xué)已經(jīng)沒多少時間了毁葱。班主任說已經(jīng)安排了三個家長分享經(jīng)驗。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,523評論 16 22
  • 創(chuàng)業(yè)是很多人的夢想贰剥,多少人為了理想和不甘選擇了創(chuàng)業(yè)來實現(xiàn)自我價值倾剿,我就是其中一個。 創(chuàng)業(yè)后蚌成,我由女人變成了超人前痘,什...
    亦寶寶閱讀 1,812評論 4 1
  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友担忧。感恩相遇芹缔!感恩不離不棄。 中午開了第一次的黨會瓶盛,身份的轉(zhuǎn)變要...
    迷月閃星情閱讀 10,567評論 0 11