本篇將為你著重展示:Flutter開(kāi)發(fā)過(guò)程的打包流程、APP包對(duì)比、細(xì)節(jié)技巧與問(wèn)題處理。本篇主要描述的Flutter的打包、在開(kāi)發(fā)過(guò)程中遇到的各類(lèi)問(wèn)題與細(xì)節(jié),算是對(duì)上兩篇的補(bǔ)全。
一、打包
首先我們先看結(jié)果,如下表所示,是 Flutter 與 React Native 、IOS 與 Android 的縱向與橫向?qū)Ρ?/strong> 。
從上表我們可以看到:
- 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)置的原因。
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
這里主要講一些小細(xì)節(jié)
1、AppBar
在 Flutter 中 AppBar 算是常用 Widget ,而 AppBar 可不僅僅作為標(biāo)題欄和使用,AppBar上的 leading
和 bottom
同樣是有用的功能。
- AppBar 的
bottom
默認(rèn)支持TabBar
, 也就是常見(jiàn)的頂部 Tab 的效果,這其實(shí)是因?yàn)?code>TabBar 實(shí)現(xiàn)了PreferredSizeWidget
的preferredSize
。 所以只要你的控件實(shí)現(xiàn)了preferredSize
,就可以放到 AppBar 的bottom
中使用。比如下圖搜索欄,這是TabView下的頁(yè)面又實(shí)用了AppBar。
-
leading
:通常是左側(cè)按鍵,不設(shè)置時(shí)一般是 Drawer 的圖標(biāo)或者返回按鈕。 -
flexibleSpace
:位于bottom
和leading
之間。
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í) TextEditingValue
是 ValueNotifier
,其中 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 下分別是 JIT 和 AOT 模式,而在 DEBUG 下,是支持 Hotload 的,而且十分絲滑。但是需要注意的是:如果開(kāi)發(fā)過(guò)程中安裝了新的第三方包 ,而新的第三方包如果包含了原生代碼,需要停止后重新運(yùn)行哦。
pubspec.yaml
文件下就是我們的包依賴(lài)目錄,其中 ^
代表大于等于,一般情況下 upgrade
和 get
都能達(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下的黃色線 yellow-lines-under-text-widgets-in-flutter:showDialog 中,默認(rèn)是沒(méi)使用 Scaffold ,這回導(dǎo)致文本有黃色溢出線提示,可以使用 Material 包一層處理。
- TabBar + TabView + KeepAlive 的問(wèn)題 可以通過(guò) TabBar + PageView 解決
自此,第三篇終于結(jié)束了!(///▽///)
最后
這里也為想要學(xué)習(xí)Flutter的朋友們準(zhǔn)備了兩份學(xué)習(xí)資料《Flutter Dart語(yǔ)言編程入門(mén)到精通》《Flutter實(shí)戰(zhàn)》,從編程語(yǔ)言到項(xiàng)目實(shí)戰(zhàn),一條龍服務(wù)!!
《Flutter Dart 語(yǔ)言編程入門(mén)到精通》
-
第一章 Dart語(yǔ)言基礎(chǔ)
-
第二章 Dart 異步編程
-
第三章 異步之 Stream 詳解
-
第四章 Dart標(biāo)準(zhǔn)輸入輸出流
-
第五章 Dart 網(wǎng)絡(luò)編程
-
第六章 Flutter 爬蟲(chóng)與服務(wù)端
-
第七章 Dart 的服務(wù)端開(kāi)發(fā)
-
第八章 Dart 調(diào)用C語(yǔ)言混合編程
-
第九章 LuaDardo中Dart與Lua的相互調(diào)用
《Flutter實(shí)戰(zhàn):第二版》
- 第一章:起步
- 第二章:第一個(gè)Flutter應(yīng)用
- 第三章:基礎(chǔ)組件
- 第四章:布局類(lèi)組件
- 第五章:容器類(lèi)組件
-
第六章:可滾動(dòng)組件
-
第七章:功能型組件
-
第八章:事件處理與通知
-
第九章:動(dòng)畫(huà)
-
第十章:自定義組件
-
第十一章:文件操作與網(wǎng)絡(luò)請(qǐng)求
-
第十二章:Flutter擴(kuò)展
-
第十三章:國(guó)際化
-
第十四章:Flutter核心原理
-
第十五章:一個(gè)完整的Flutter應(yīng)用
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-860864.html
有需要學(xué)習(xí)資料的朋友掃描下方二維碼即可免費(fèi)領(lǐng)?。。。?span toymoban-style="hidden">文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-860864.html

到了這里,關(guān)于Flutter完整開(kāi)發(fā)實(shí)戰(zhàn)詳解(三、 打包與填坑篇)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!