前端小弟一個(gè),F(xiàn)lutter初學(xué)者,問(wèn)題都是項(xiàng)目中遇到的,所以記錄了下來(lái)。
解決方案來(lái)自于日??偨Y(jié)及各路大佬。
1、Waiting for another flutter command to release the startup lock...
打開(kāi)新的項(xiàng)目或者使用Flutter Packages get時(shí)出現(xiàn): Waiting for another flutter command to release the startup lock...
解決方案:
先打開(kāi)任務(wù)管理器,結(jié)束掉所有dart.exe即可,如果依然提示就打開(kāi)你的flutter安裝文件夾,找到\bin\cache中的lockfile文件刪除。之后重啟項(xiàng)目。
2、The Gradle failure may have been because of AndroidX incompatibilities in this Flutter app
Android dependency 'androidx.core:core' has different version for the compile (1.0.0) and runtime (1.0.1) classpath. You should manually set the same version via DependencyResolution
解決方案一:
設(shè)法通過(guò)添加這樣的代碼片段來(lái)修復(fù)錯(cuò)誤
在項(xiàng)目 android > build.gradle 文件中 buildscript { } 中添加此代碼片段
subprojects {
project.configurations.all {
resolutionStrategy.eachDependency { details ->
if (details.requested.group == 'com.android.support'
&& !details.requested.name.contains('multidex') ) {
details.useVersion "27.1.1"
}
if (details.requested.group == 'androidx.core'
&& !details.requested.name.contains('androidx') ) {
details.useVersion "1.0.1"
}
}
}
}
解決方案二:
1.android/gradle/wrapper/gradle-wrapper.properties里面
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
2.android/build.gradle
dependencies { classpath 'com.android.tools.build:gradle:3.3.0' }
3.android/gradle.properties
加入
android.enableJetifier=true
android.useAndroidX=true
4.android/app/build.gradle 修改版本號(hào):
make sure compileSdkVersion and targetSdkVersion are at least 28.
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
5.android/app/build.gradle /defaultConfig加上
multiDexEnabled true
3、Error connecting to the service protocol: HttpException: Connection closed before full header was...
最后是使用安卓系統(tǒng)版本是9.1 Android版本太高,換8.1就好了
查看 Android Studio Sdk Manager 版本
查看項(xiàng)目App目錄下 compileSdkVersion
4、type 'List<dynamic>' is not a subtype of type 'List<Widget>'
// 這里的問(wèn)題是類型推斷以意想不到的方式失敗。解決方案是為map方法提供類型參數(shù)
Wrap(
children:item['casts'].map<Widget>((casts){
return Text('data');
}).toList(),
)
更復(fù)雜的答案是,雖然類型children是List<Widget>,該信息不會(huì)回流到map調(diào)用。這可能是因?yàn)閙ap后面是toList因?yàn)闆](méi)有辦法輸入注釋閉包的返回。
5、chewie插件在ios下播放失敗的問(wèn)題
<!-- 在項(xiàng)目根目錄 > ios > Runner > info.plist 文件中
<dict>尾部
添加視頻播放需要的nsapp傳輸安全的key
-->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
6、snackBar報(bào)錯(cuò)的問(wèn)題無(wú)法彈出,Scaffold.of() called with a context that does not contain a Scaffold.
// 定義一個(gè)GlobarKey
GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey();
//
return new Scaffold(
key: _scaffoldkey,
........
),
.......
)
// 通過(guò)key調(diào)用snackbar
onPressed: (){
_scaffoldKey.currentState.showSnackBar(
SnackBar(
content: Text("這是一個(gè)SnackBar"),
duration: Duration(
milliseconds: 1
),
action: SnackBarAction(
label: "別點(diǎn)我",
onPressed: () {
print("SnackBar上面的按鈕被點(diǎn)擊了");
}
),
)
);
},
)
7、腳手架sliver、scaffold中設(shè)置appbar、tabbar的高度或者背景色
// 尤其是AppBar和TabBar,如果不希望被限制size,需要提供preferredSize或默認(rèn)值
// 想要設(shè)置高度或者背景色,在AppBar外包一層PreferredSize,設(shè)置preferredSize的屬性為想要的高度即可。
SliverPersistentHeader(
pinned: true,
delegate: StickyTabBarDelegate(
child:PreferredSize(
preferredSize: Size.fromHeight(40),
child: Material(
color: Colors.grey[200],
child: TabBar(
labelColor: Colors.black,
controller: this._tabController,
indicatorColor: Colors.black,
tabs: <Widget>[
Tab(text: '評(píng)論'),
Tab(text: '話題區(qū)'),
],
),
),
),
),
),
8、去掉狀態(tài)欄代碼實(shí)現(xiàn)
做Flutter做屏效果顯示的時(shí)候,調(diào)用SystemChrome.setEnabledSystemUIOverlays([]); 這個(gè)方法把狀態(tài)欄和虛擬按鍵隱藏掉
跳轉(zhuǎn)到其他頁(yè)面后需要調(diào)用把狀態(tài)欄顯示出來(lái),調(diào)用SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.top]);
需要一起調(diào)用底部虛擬按鍵(華為系列某些手機(jī)有虛擬按鍵),則SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.top, SystemUiOverlay.bottom]);
9、Image.File 加載圖像時(shí)文件內(nèi)容變化但圖像不變
/* 在Flutter中,我們可以用下面的代碼從文件中加載圖像:
Image.file(File(_fileName));
這個(gè)時(shí)候,當(dāng)_fileName這個(gè)文件名稱和路徑不變,文件內(nèi)容變化時(shí),F(xiàn)lutter并不會(huì)更新顯示。問(wèn)題產(chǎn)生的原因是Flutter自動(dòng)使用了緩存。
Image.file 實(shí)際上會(huì)將 image 設(shè)置為 FileImage 這個(gè) ImageProvider。FileImage 的代碼中,在進(jìn)行 operator 時(shí),只判斷了文件路徑和縮放比例。正是因?yàn)槿绱耍覀兾募窂讲蛔儯s放比例不變時(shí),F(xiàn)lutter會(huì)認(rèn)為我們還是用的原圖,并不會(huì)重新進(jìn)行加載。
于是,我想到了辦法是擴(kuò)展一個(gè)FileImage,將這個(gè) operator 的方式改一下。 */
新創(chuàng)建一個(gè)類
class FileImageEx extends FileImage {
int fileSize;
FileImageEx(File file, { double scale = 1.0 })
: assert(file != null),
assert(scale != null),
super(file, scale: scale) {
fileSize = file.lengthSync();
}
@override
bool operator ==(dynamic other) {
if (other.runtimeType != runtimeType)
return false;
final FileImageEx typedOther = other;
return file?.path == typedOther.file?.path
&& scale == typedOther.scale && fileSize == typedOther.fileSize;
}
}
接下來(lái),直接使用下面的代碼來(lái)加載圖像:
Image(image: FileImageEx(File(_fileName)));
10、Inkell 去掉水波紋的方法
// 設(shè)置highlightColor為透明,同時(shí)設(shè)置radius為0
InkWell(
highlightColor:Colors.transparent,
radius: 0.0,
onTap: (){
},
child: Text('跳過(guò) ${_time}s'),
),
11、打包release版本安卓apk包真機(jī)無(wú)法請(qǐng)求網(wǎng)絡(luò)
// 原因:安卓開(kāi)發(fā)中flutter應(yīng)用沒(méi)有網(wǎng)絡(luò)權(quán)限
在項(xiàng)目目錄android\app\src\profile\AndroidManifest.xml manifest 里添加這段代碼
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
在項(xiàng)目目錄 android/src/main/AndroidManifest.xml 里也有一個(gè) AndroidManifest.xml文件!跟之前的只不過(guò)是文件夾位置不同而已,同樣在manifest標(biāo)簽下加入相同配置就行了,不要放到application里
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
重新打包即可
12、setstate() called after dispose() ,setstate導(dǎo)致的內(nèi)存泄漏
// flutter端請(qǐng)求網(wǎng)絡(luò)時(shí),調(diào)用的是宿主App的網(wǎng)絡(luò)請(qǐng)求。
// flutter通過(guò)消息通道發(fā)送一個(gè)消息,然后await等待消息返回,最終宿主app會(huì)調(diào)用reply.reply(obj)方法返回?cái)?shù)據(jù)。
// 如果在這個(gè)過(guò)程中,flutter頁(yè)面關(guān)閉,就會(huì)出現(xiàn)如下異常,類似Android中的內(nèi)存泄漏。
/* setState() called after dispose(): _DetailCommentsState#5c3a1(lifecycle state: defunct, not mounted)
I/flutter ( 4677): This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
I/flutter ( 4677): The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
I/flutter ( 4677): This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object
after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose(). */
// 我們的錯(cuò)誤原因是異步消息未返回,所以在setState方法之前調(diào)用mouted屬性進(jìn)行判斷即可。具體示例如下:
if(mounted){
setState(() {
_movie = res.data;
_themeColor = paletteGenerator.colors.toList()[1];
_detailThemeColor = paletteGenerator.colors.toList()[0];
});
}
13、xCode打包提示:Could not find included file 'Generated.xcconfig' in search paths (in target 'Runner')
// 嘗試執(zhí)行 flutter build ios 然后重新啟動(dòng)Xcode
14、更新showDialog以及showModalBottomSheet中的狀態(tài)中的內(nèi)容
// 很多人在用showDialog的時(shí)候應(yīng)該都遇到過(guò)這個(gè)問(wèn)題,使用showDialog后,通過(guò)setState()無(wú)法更新當(dāng)前dialog。其實(shí)原因很簡(jiǎn)單,因?yàn)閐ialog其實(shí)是另一個(gè)頁(yè)面,準(zhǔn)確地來(lái)說(shuō)是另一個(gè)路由,因?yàn)閐ialog的關(guān)閉也是通過(guò)navigator來(lái)pop的,所以它的地位跟你當(dāng)前主頁(yè)面一樣。這個(gè)概念一定要明確,因?yàn)闊o(wú)論在Android或iOS中,daliog都是依附于當(dāng)前主頁(yè)面的一個(gè)控件,但是在Flutter中不同,它是一個(gè)新的路由。所以使用當(dāng)前主頁(yè)面的setState()來(lái)更新,當(dāng)然沒(méi)法達(dá)到你要的效果。
/* StatefulBuilder
很多人使用StatefulBuilder依然達(dá)不到更新的效果,是因?yàn)槟阌缅e(cuò)了setState()方法。
就像我們上面說(shuō)的那樣,這個(gè)builder構(gòu)建的控件,不會(huì)響應(yīng)老頁(yè)面的任何操作,因?yàn)樗鼈兪莾蓚€(gè)互不影響的路由控制的。 */
// 1、更新showDialog
showDialog(
context: context,
builder: (context) {
String label = 'test';
return StatefulBuilder(
builder: (context, state) {
print('label = $label');
return GestureDetector(
child: Text(label),
onTap: () {
label = 'test8';
print('onTap:label = $label');
// 注意不是調(diào)用老頁(yè)面的setState,而是要調(diào)用builder中的setState。
//在這里為了區(qū)分,在構(gòu)建builder的時(shí)候?qū)etState方法命名為了state。
state(() {});
},
);
},
);
}
);
// 2、更新showModalBottomSheet
showModalBottomSheet(context:context, builder:(BuildContext context){
return StatefulBuilder(
builder:(context1, state) {///這里的state就是setState
return Container(
child:OutlineButton(
onPressed: (){
state(() {///為了區(qū)分把setState改個(gè)名字
btnState=!btnState;
});
},
child:Stack(
children: <Widget>[
Opacity(
opacity: btnState ? 0.0 : 1.0,
child: Text("aa"),
),
Opacity(
opacity: btnState ? 1.0 : 0.0,
child: Text("bb"),
)
],
),
),
),
}
)
})
15、SliverPersistentHeader 組件內(nèi)狀態(tài)更新,但UI未更新
SliverPersistentHeader(
pinned: true,
delegate: SliverBarDelegate(
PreferredSize(
preferredSize: Size.fromHeight(ScreenAdapter.height(80)),
child:_headActions()
),
),
),
// SliverPersistentHeader delegate的是重寫(xiě)的SliverBarDelegate
class SliverBarDelegate extends SliverPersistentHeaderDelegate {
final widget;
SliverBarDelegate(this.widget);
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return Container(
child: widget,
);
}
// 是否需要重新構(gòu)建
// 如果傳遞的這幾個(gè)參數(shù)變化了,那就重寫(xiě)創(chuàng)建
// 如果返回false,則可能不會(huì)重新構(gòu)建報(bào)頭,即使委托的實(shí)例發(fā)生了變化。
// 源代碼中有描述到
@override
bool shouldRebuild(SliverBarDelegate oldDelegate) {
return true;
}
@override
double get maxExtent => widget.preferredSize.height;
@override
double get minExtent => widget.preferredSize.height;
}
16、packages get 慢的解決方案
/*
國(guó)內(nèi)使用 flutter packages get 命令,一直是 This is taking an unexpectedly long time 狀態(tài)
科學(xué)上網(wǎng)無(wú)效
windows解決方案:配置 【環(huán)境變量】 > 【用戶變量】:
變量名:PUB_HOSTED_URL 值:https://pub.flutter-io.cn
變量名:FLUTTER_STORAGE_BASE_URL 值:https://storage.flutter-io.cn
最好重啟下windows電腦,flutter packages get 執(zhí)行
*/
具體環(huán)境變量的值 需要看該網(wǎng)址 [Using Flutter in China](https://flutter.dev/community/china)

image

image
17、如何將String類型的顏色轉(zhuǎn)化為Color所需要的int類型的顏色
// 完整的顏色是8位,如果是RGB是6位顏色值可以直接添加
Color(int.parse('0xff'+color)
18、is a SingleTickerProviderStateMixin but multiple tickers were created.
// 報(bào)錯(cuò)日志:
I/flutter ( 4025): A SingleTickerProviderStateMixin can only be used as a TickerProvider once. If a State is used for
I/flutter ( 4025): multiple AnimationController objects, or if it is passed to other objects and those objects might
I/flutter ( 4025): use it more than one time in total, then instead of mixing in a SingleTickerProviderStateMixin, use
I/flutter ( 4025): a regular TickerProviderStateMixin.
大概翻譯意思:?jiǎn)蝹€(gè)TickerProviderStateMixin,但創(chuàng)建了多個(gè)Ticker,單個(gè)statemixin 只能供一個(gè)TickerProvider使用,如果一個(gè)狀態(tài)用于多個(gè)對(duì)象,它可能被傳遞給其他對(duì)象。
// 解決方案:將SingleTickerProviderStateMixin換成TickerProviderStateMixin
19、動(dòng)畫(huà)釋放報(bào)錯(cuò)
// 報(bào)錯(cuò)日志:
e following assertion was thrown while finalizing the widget tree:
I/flutter ( 3776): _CustomScrollFooterState#3df1f(ticker active) was disposed with an active Ticker.
I/flutter ( 3776): _CustomScrollFooterState created a Ticker via its SingleTickerProviderStateMixin, but at the time
I/flutter ( 3776): dispose() was called on the mixin, that Ticker was still active. The Ticker must be disposed before
I/flutter ( 3776): calling super.dispose(). Tickers used by AnimationControllers should be disposed by calling
I/flutter ( 3776): dispose() on the AnimationController itself. Otherwise, the ticker will leak.
I/flutter ( 3776): The offending ticker was: Ticker(created by _CustomScrollFooterState#3df1f(lifecycle state:
I/flutter ( 3776): created))
// 解決方案:
controller.dispose()放在了 super.dispose()的后面:
@override
void dispose() {
//先調(diào)用controller.dispose釋放了動(dòng)畫(huà)資源,再調(diào)用super
controller.dispose();
super.dispose();
}
20、Could not resolve all files for configuration ':app:lintClassPath'.
// 報(bào)錯(cuò)日志:
* What went wrong:
Execution failed for task ':app:lintVitalRelease'.
> Could not resolve all files for configuration ':app:lintClassPath'.
> Could not download groovy-all.jar (org.codehaus.groovy:groovy-all:2.4.12)
> Could not get resource 'https://jcenter.bintray.com/org/codehaus/groovy/groovy-all/2.4.12/groovy-all-2.4.12.jar'.
> Could not GET 'https://jcenter.bintray.com/org/codehaus/groovy/groovy-all/2.4.12/groovy-all-2.4.12.jar'.
> Remote host closed connection during handshake
// 解決方案:
在app的build.gradle中的android部分添加如下代碼塊即可
lintOptions {
checkReleaseBuilds false
abortOnError false
}
21、設(shè)置沉浸式狀態(tài)欄,取消android狀態(tài)欄陰影
if (Platform.isAndroid) {
// 以下兩行 設(shè)置android狀態(tài)欄為透明的沉浸。寫(xiě)在組件渲染之后,是為了在渲染后進(jìn)行set賦值,覆蓋狀態(tài)欄,寫(xiě)在渲染之前MaterialApp組件會(huì)覆蓋掉這個(gè)值。
SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle(statusBarColor: Colors.transparent);
SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
}
22、更改文件名或刪除名稱時(shí)報(bào)錯(cuò),were declared as an inputs, but did not exist. Check the definition of target:kernel_snapshot for errors
// 報(bào)錯(cuò)日志
C:\Users\Admin\Desktop\flutter_jahn_douban\lib\pages\tabs\book_movie\movie\movieTop\movieTopAll\movie_top_all.dart, C:\Users\Admin\Desktop\flutter_jahn_douban\lib\pages\tabs\book_movie\movie\movieTop\movieTopAll\movie.dart were declared as an inputs, but did not exist. Check the definition of target:kernel_snapshot for errors
#0 Node.computeChanges (package:flutter_tools/src/build_system/build_system.dart:777:7)
<asynchronous suspension>
#1 _BuildInstance._invokeInternal (package:flutter_tools/src/build_system/build_system.dart:517:20)
<asynchronous suspension>
#2 _BuildInstance.invokeTarget.<anonymous closure> (package:flutter_tools/src/build_system/build_system.dart:481:35)
// 解決方案:
刪除.dart_tool文件夾,然后重新運(yùn)行即可。
23、修改版本號(hào)不生效
Flutter的App版本號(hào)設(shè)置在pubspec.yaml中,+號(hào)前面是版本名稱,后面是版本號(hào),在此修改會(huì)自動(dòng)應(yīng)用到Android和IOS項(xiàng)目對(duì)應(yīng)版本號(hào)中,修改完安裝發(fā)現(xiàn)并未生效,解決方法:
// 解決方案:
// 修改后執(zhí)行
1、flutter get
2、flutter clean
重新 build ios 安裝就能生效了
24、Fluro中傳遞中文參數(shù)失敗
/* The following ArgumentError was thrown while handling a gesture:
I/flutter ( 6034): Invalid argument(s): Illegal percent encoding in URI
I/flutter ( 6034): When the exception was thrown, this was the stack:
I/flutter ( 6034): #0 _Uri._uriDecode (dart:core/uri.dart:2951:11)
I/flutter ( 6034): #1 Uri.decodeComponent (dart:core/uri.dart:1120:17) */
無(wú)效的參數(shù):URI中的非法編碼百分比
// 解決方案:
通過(guò)Uri.encodeComponent(Text)轉(zhuǎn)化
25、TextField組件,報(bào)錯(cuò)解決:The following assertion was thrown while handling a gesture
/* 在沒(méi)有正常的取消輸入框焦點(diǎn)的時(shí)候,就先清空輸入框組件,整體渲染順序是不正確的。 */
// 解決方案:
// 保證在組件build的第一幀時(shí)才去觸發(fā)取消清空內(nèi)容
WidgetsBinding.instance.addPostFrameCallback((_) => controller.clear());
26、TabController的監(jiān)聽(tīng)在點(diǎn)擊Tab后會(huì)執(zhí)行兩次
/* 每點(diǎn)擊一次,監(jiān)聽(tīng)都有兩次回調(diào) */
// 解決方案:
// 對(duì)indexIsChanging進(jìn)行判斷
_tabController.addListener((){
if(_tabController.indexIsChanging){
}
});
27、判斷頁(yè)面渲染完畢
/* 在flutter中,我們獲取大小也必須在元素渲染完成的時(shí)候才行,而有些應(yīng)用場(chǎng)景要求在第一時(shí)間獲取到這個(gè)元素的大小。那么怎么在第一時(shí)間判斷元素渲染完成呢?flutter中的WidgetsBuiding這個(gè)類就可以用來(lái)判斷: */
/* Schedule a callback for the end of this frame.
This callback is run during a frame, just after the persistent frame callbacks (which is when the main rendering pipeline has been flushed).
Post-frame callbacks cannot be unregistered. They are called exactly once.
*/
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback(_onAfterRendering);
}
// 渲染完成之后獲取某個(gè)元素的大小
void _onAfterRendering(Duration timeStamp){
RenderBox renderBox = _notUsed.currentContext.findRenderObject();
double width = renderBox.size.width;
double height = renderBox.size.height;
double x = renderBox.localToGlobal(Offset.zero).dx;
setState(() {
_currentHeight = height;
_currentWidth = width;
_currentLeft = x;
_currentIndex = 1;
});
}