Flutter完整開(kāi)發(fā)實(shí)戰(zhàn)詳解(三才写、 打包與填坑篇)

作為系列文章的第三篇,本篇將為你著重展示:Flutter開(kāi)發(fā)過(guò)程的打包流程奖蔓、APP包對(duì)比赞草、細(xì)節(jié)技巧與問(wèn)題處理,本篇主要描述的 Flutter 的打包吆鹤、在開(kāi)發(fā)過(guò)程中遇到的各類(lèi)問(wèn)題與細(xì)節(jié)厨疙,算是對(duì)上兩篇的補(bǔ)全。

文章匯總地址:

Flutter 完整實(shí)戰(zhàn)實(shí)戰(zhàn)系列文章專(zhuān)欄

Flutter 番外的世界系列文章專(zhuān)欄

一疑务、打包

首先我們先看結(jié)果沾凄,如下表所示,是 Flutter 與 React Native 知允、iOS 與 Android 的縱向與橫向?qū)Ρ?/strong> 撒蟀。

項(xiàng)目 IOS Android
GSYGithubAppFlutter
flutter-ipa
flutter-apk
GSYGithubAppRN
rn-ipa
rn-apk

從上表我們可以看到:

  • Fluuter的 apk 會(huì)比 ipa 更小一些,這其中的一部分原因是 Flutter 使用的 Skia 在Android 上是自帶的温鸽。

  • 橫向?qū)Ρ?React Native 保屯,雖然項(xiàng)目不完全一樣,但是大部分功能一致的情況下涤垫, Flutter 的 Apk 確實(shí)更小一些姑尺。這里又有一個(gè)細(xì)節(jié),rn 的 ipa 包體積小很多蝠猬,這其實(shí)是因?yàn)?javascriptcore 在 ios上 是內(nèi)置的原因股缸。

  • 對(duì)上述內(nèi)容有興趣的可以看看《移動(dòng)端跨平臺(tái)開(kāi)發(fā)的深度解析》

1吱雏、Android 打包

I'm Android

在 Android 的打包上敦姻,筆者基本沒(méi)有遇到什么問(wèn)題瘾境,在android/app/build.grade文件下,配置applicationId镰惦、versionCode迷守、versionName 和簽名信息,最后通過(guò) flutter build app 即可完成編譯旺入。編程成功的包在 build/app/outputs/apk/release 下兑凿。

2、iOS 打包與真機(jī)運(yùn)行

在 iOS 的打包上茵瘾,筆者倒是經(jīng)歷了一波曲折礼华,這里主要講筆者遇到的問(wèn)題。

首先你需要一個(gè) apple 開(kāi)發(fā)者賬號(hào)拗秘,然后創(chuàng)建證書(shū)圣絮、創(chuàng)建AppId,創(chuàng)建配置文件雕旨、最后在info.plist文件下輸入相關(guān)信息扮匠,更詳細(xì)可看官方的《發(fā)布的IOS版APP》的教程。

但由于筆者項(xiàng)目中使用了第三方的插件包如 shared_preferences 等凡涩,在執(zhí)行 Archive 的過(guò)程卻一直出現(xiàn)如下問(wèn)題:

在 `Archive` 時(shí)提示找不到
#import <connectivity/ConnectivityPlugin.h>  ///file not found
#import <device_info/DeviceInfoPlugin.h>
#import <flutter_statusbar/FlutterStatusbarPlugin.h>
#import <flutter_webview_plugin/FlutterWebviewPlugin.h>
#import <fluttertoast/FluttertoastPlugin.h>
#import <get_version/GetVersionPlugin.h>
#import <package_info/PackageInfoPlugin.h>
#import <share/SharePlugin.h>
#import <shared_preferences/SharedPreferencesPlugin.h>
#import <sqflite/SqflitePlugin.h>
#import <url_launcher/UrlLauncherPlugin.h>

通過(guò) Android Studio 運(yùn)行到 iOS 模擬器時(shí)沒(méi)有任何問(wèn)題棒搜,說(shuō)明這不是第三方包問(wèn)題。通過(guò)查找問(wèn)題發(fā)現(xiàn)活箕,在 iOS 執(zhí)行 Archive 之前力麸,需要執(zhí)行 flutter build release,如下圖在命令執(zhí)行之后育韩,Pod 的執(zhí)行目錄會(huì)發(fā)現(xiàn)改變克蚂,并且生成打包需要的文件。(ps 普通運(yùn)行時(shí)自動(dòng)又會(huì)修改回來(lái)

文件變化

但是實(shí)際在執(zhí)行 flutter build release 后座慰,問(wèn)題依然存在,最終翻山越嶺(╯‵□′)╯︵┻━┻翠拣,終于找到兩個(gè)答案:

  • Issue#19241 下描述了類(lèi)似問(wèn)題版仔,但是他們因?yàn)槁窂絾?wèn)題導(dǎo)致,經(jīng)過(guò)嘗試并不能解決误墓。

  • Issue#18305 真實(shí)的解決了這個(gè)問(wèn)題蛮粮,居然是因?yàn)?Pod 的工程沒(méi)引入:

open iOS/Runner.xcodeproj

I checked Runner/Pods is empty in Xcode sidebar.

drop Pods/Pods.xcodeproj into Runner/Pods.

"Valid architectures" to only "arm64" (I removed armv7 armv7s) 

最后終于成功打包,心累啊(//////)谜慌。同時(shí)如果希望直接在真機(jī)上調(diào)試 Flutter然想,可以參考 :《Flutter基礎(chǔ)—開(kāi)發(fā)環(huán)境與入門(mén)》 下的 iOS 真機(jī)部分。

二欣范、細(xì)節(jié)

這里主要講一些小細(xì)節(jié)

1变泄、AppBar

在 Flutter 中 AppBar 算是常用 Widget 令哟,而 AppBar 可不僅僅作為標(biāo)題欄和使用,AppBar上的 leadingbottom 同樣是有用的功能妨蛹。

  • AppBar 的 bottom 默認(rèn)支持 TabBar, 也就是常見(jiàn)的頂部 Tab 的效果屏富,這其實(shí)是因?yàn)?code>TabBar 實(shí)現(xiàn)了 PreferredSizeWidgetpreferredSize
    所以只要你的控件實(shí)現(xiàn)了 preferredSize蛙卤,就可以放到 AppBar 的 bottom 中使用狠半。比如下圖搜索欄,這是TabView下的頁(yè)面又實(shí)用了AppBar颤难。
image
  • leading :通常是左側(cè)按鍵神年,不設(shè)置時(shí)一般是 Drawer 的圖標(biāo)或者返回按鈕。

  • flexibleSpace :位于 bottomleading 之間行嗤。

2已日、按鍵

Flutter 中的按鍵,如 FlatButton 默認(rèn)是否有邊距和最小大小的昂验。所以如果你想要無(wú) padding捂敌、margin、border 既琴、默認(rèn)大小 等的按鍵效果占婉,其中一種方式如下:

///
new RawMaterialButton(
        materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
        padding: padding ?? const EdgeInsets.all(0.0),
        constraints: const BoxConstraints(minWidth: 0.0, minHeight: 0.0),
        child: child,
        onPressed: onPressed);

如果在再上 Flex ,如下所示甫恩,一個(gè)可控的填充按鍵就出來(lái)了逆济。

new RawMaterialButton(
        materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
        padding: padding ?? const EdgeInsets.all(0.0),
        constraints: const BoxConstraints(minWidth: 0.0, minHeight: 0.0),
        ///flex
        child: new Flex(
          mainAxisAlignment: mainAxisAlignment,
          direction: Axis.horizontal,
          children: <Widget>[],
        ),
        onPressed: onPressed);

3、StatefulWidget 賦值

這里我們以給 TextField 主動(dòng)賦值為例磺箕,其實(shí) Flutter 中奖慌,給有狀態(tài)的 Widget 傳遞狀態(tài)或者數(shù)據(jù),一般都是通過(guò)各種 controller 松靡。如 TextField 的主動(dòng)賦值简僧,如下代碼所示:


 final TextEditingController controller = new TextEditingController();

 @override
 void didChangeDependencies() {
    super.didChangeDependencies();
    ///通過(guò)給 controller 的 value 新創(chuàng)建一個(gè) TextEditingValue
    controller.value = new TextEditingValue(text: "給輸入框填入?yún)?shù)");
 }

 @override
  Widget build(BuildContext context) {
    return new TextField(
     ///controller
      controller: controller,
      onChanged: onChanged,
      obscureText: obscureText,
      decoration: new InputDecoration(
        hintText: hintText,
        icon: iconData == null ? null : new Icon(iconData),
      ),
    );
  }

其實(shí) TextEditingValueValueNotifier,其中 value 的 setter 方法被重載雕欺,一旦改變就會(huì)觸發(fā) notifyListeners 方法岛马。而 TextEditingController 中,通過(guò)調(diào)用 addListener 就監(jiān)聽(tīng)了數(shù)據(jù)的改變屠列,從而讓UI更新啦逆。

當(dāng)然,賦值有更簡(jiǎn)單粗暴的做法是:傳遞一個(gè)對(duì)象 class A 對(duì)象笛洛,在控件內(nèi)部使用對(duì)象 A.b 的變量綁定控件夏志,外部通過(guò) setState({ A.b = b2}) 更新

4苛让、GlobalKey

在Flutter中沟蔑,要主動(dòng)改變子控件的狀態(tài)湿诊,還可以使用 GlobalKey。 比如你需要主動(dòng)調(diào)用 RefreshIndicator 顯示刷新?tīng)顟B(tài)溉贿,如下代碼所示枫吧。


 GlobalKey<RefreshIndicatorState> refreshIndicatorKey;
  
 showForRefresh() {
    ///顯示刷新
    refreshIndicatorKey.currentState.show();
  }

  @override
  Widget build(BuildContext context) {
    refreshIndicatorKey =  new GlobalKey<RefreshIndicatorState>();
    return new RefreshIndicator(
      key: refreshIndicatorKey,
      onRefresh: onRefresh,
      child: new ListView.builder(
        ///·····
      ),
    );
  }

5、Redux 與主題

使用 Redux 來(lái)做 Flutter 的全局 State 管理最合適不過(guò)宇色,由于Redux內(nèi)容較多九杂,如果感興趣的可以看看 篇章二 ,這里主要通過(guò) Redux 來(lái)實(shí)現(xiàn)實(shí)時(shí)切換主題的效果宣蠕。

如下代碼例隆,通過(guò) StoreProvider 加載了 store ,再通過(guò) StoreBuilder 將 store 中的 themeData 綁定到 MaterialApp 的 theme 下抢蚀,之后在其他 Widget 中通過(guò) Theme.of(context) 調(diào)你需要的顏色镀层,最終在任意位置調(diào)用 store.dispatch 就可實(shí)時(shí)修改主題,效果如后圖所示皿曲。

class FlutterReduxApp extends StatelessWidget {
  final store = new Store<GSYState>(
    appReducer,
    initialState: new GSYState(
      themeData: new ThemeData(
        primarySwatch: GSYColors.primarySwatch,
      ),
    ),
  );

  FlutterReduxApp({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    /// 通過(guò) StoreProvider 應(yīng)用 store
    return new StoreProvider(
      store: store,
      ///通過(guò) StoreBuilder 獲取 themeData
      child: new StoreBuilder<GSYState>(builder: (context, store) {
        return new MaterialApp(
            theme: store.state.themeData,
            routes: {
              HomePage.sName: (context) {
                return HomePage();
              },
            });
      }),
    );
  }
}
主題

6唱逢、Hotload 與 Package

Flutter 在 Debug 和 Release 下分別是 JITAOT 模式,而在 DEBUG 下屋休,是支持 Hotload 的坞古,而且十分絲滑。但是需要注意的是:如果開(kāi)發(fā)過(guò)程中安裝了新的第三方包 劫樟,而新的第三方包如果包含了原生代碼痪枫,需要停止后重新運(yùn)行哦。

pubspec.yaml 文件下就是我們的包依賴(lài)目錄叠艳,其中 ^ 代表大于等于奶陈,一般情況下 upgradeget 都能達(dá)到下載包的作用。但是:upgrade 會(huì)在包有更新的情況下附较,更新 pubspec.lock 文件下包的版本 吃粒。

三、問(wèn)題處理

  • Waiting for another flutter command to release the startup lock :如果遇到這個(gè)問(wèn)題:
  1拒课、打開(kāi)flutter的安裝目錄/bin/cache/ 
  2徐勃、刪除lockfile文件 
  3、重啟AndroidStudio
  • dialog下的黃色線(xiàn)
    yellow-lines-under-text-widgets-in-flutter:showDialog 中捕发,默認(rèn)是沒(méi)使用 Scaffold 疏旨,這回導(dǎo)致文本有黃色溢出線(xiàn)提示很魂,可以使用 Material 包一層處理扎酷。

  • TabBar + TabView + KeepAlive 的問(wèn)題
    可以通過(guò) TabBar + PageView 解決,具體可見(jiàn) 篇章二遏匆。

自此法挨,第三篇終于結(jié)束了谁榜!(//////)

資源推薦

完整開(kāi)源項(xiàng)目推薦:
GSYTech
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市凡纳,隨后出現(xiàn)的幾起案子窃植,更是在濱河造成了極大的恐慌,老刑警劉巖荐糜,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件巷怜,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡暴氏,警方通過(guò)查閱死者的電腦和手機(jī)延塑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)答渔,“玉大人关带,你說(shuō)我怎么就攤上這事≌铀海” “怎么了宋雏?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)务豺。 經(jīng)常有香客問(wèn)我磨总,道長(zhǎng),這世上最難降的妖魔是什么冲呢? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任舍败,我火速辦了婚禮,結(jié)果婚禮上敬拓,老公的妹妹穿的比我還像新娘邻薯。我一直安慰自己,他們只是感情好乘凸,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布厕诡。 她就那樣靜靜地躺著,像睡著了一般营勤。 火紅的嫁衣襯著肌膚如雪灵嫌。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,156評(píng)論 1 308
  • 那天葛作,我揣著相機(jī)與錄音寿羞,去河邊找鬼。 笑死赂蠢,一個(gè)胖子當(dāng)著我的面吹牛堕汞,可吹牛的內(nèi)容都是我干的壹甥。 我是一名探鬼主播骑祟,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼壁拉,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎试溯,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體郊酒,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡遇绞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了燎窘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片试读。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖荠耽,靈堂內(nèi)的尸體忽然破棺而出钩骇,到底是詐尸還是另有隱情,我是刑警寧澤铝量,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布倘屹,位于F島的核電站,受9級(jí)特大地震影響慢叨,放射性物質(zhì)發(fā)生泄漏纽匙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一拍谐、第九天 我趴在偏房一處隱蔽的房頂上張望烛缔。 院中可真熱鬧,春花似錦轩拨、人聲如沸践瓷。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)晕翠。三九已至,卻和暖如春砍濒,著一層夾襖步出監(jiān)牢的瞬間淋肾,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工爸邢, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留樊卓,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓杠河,卻偏偏與公主長(zhǎng)得像碌尔,于是被迫代替她去往敵國(guó)和親赶掖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

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