Flutter填坑指南,總有一個遇得到。

jahn發表於2019-11-21

1、Waiting for another flutter command to release the startup lock...

開啟新的專案或者使用Flutter Packages get時出現: Waiting for another flutter command to release the startup lock...

解決方案:

先開啟工作管理員,結束掉所有dart.exe即可,如果依然提示就開啟你的flutter安裝資料夾,找到\bin\cache中的lockfile檔案刪除。之後重啟專案。
複製程式碼

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

解決方案一:

設法通過新增這樣的程式碼片段來修復錯誤  
在專案 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 修改版本號:
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...

最後是使用安卓系統版本是9.1 Android版本太高,換8.1就好了

檢視 Android Studio Sdk Manager 版本 

檢視專案App目錄下 compileSdkVersion
複製程式碼

4、type 'List' is not a subtype of type 'List'

// 這裡的問題是型別推斷以意想不到的方式失敗。解決方案是為map方法提供型別引數
Wrap(
  children:item['casts'].map<Widget>((casts){
  return Text('data');
  }).toList(),
)

更復雜的答案是,雖然型別children是List<Widget>,該資訊不會迴流到map呼叫。這可能是因為map後面是toList因為沒有辦法輸入註釋閉包的返回。
複製程式碼

5、chewie外掛在ios下播放失敗的問題

<!-- 在專案根目錄 > ios > Runner > info.plist 檔案中  
      <dict>尾部
      新增視訊播放需要的nsapp傳輸安全的key 
-->

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSAllowsArbitraryLoads</key>
  <true/>
</dict>
複製程式碼

6、snackBar報錯的問題無法彈出,Scaffold.of() called with a context that does not contain a Scaffold.

// 定義一個GlobarKey
GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey();
   
  // 
return new Scaffold(
    key: _scaffoldkey,
    ........
    ),
    .......
)
  
// 通過key呼叫snackbar
onPressed: (){
  _scaffoldKey.currentState.showSnackBar(
      SnackBar(
        content: Text("這是一個SnackBar"),
        duration: Duration(
            milliseconds: 1
        ),
        action: SnackBarAction(
            label: "別點我",
            onPressed: () {
              print("SnackBar上面的按鈕被點選了");
            }
        ),
      )
    );                
  },
)
複製程式碼

7、腳手架sliver、scaffold中設定appbar、tabbar的高度或者背景色

// 尤其是AppBar和TabBar,如果不希望被限制size,需要提供preferredSize或預設值
// 想要設定高度或者背景色,在AppBar外包一層PreferredSize,設定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: '評論'),
            Tab(text: '話題區'),
          ],
      ),
      ),
    ),
  ),
),
複製程式碼

8、去掉狀態列程式碼實現

做Flutter做屏效果顯示的時候,呼叫SystemChrome.setEnabledSystemUIOverlays([]); 這個方法把狀態列和虛擬按鍵隱藏掉

跳轉到其他頁面後需要呼叫把狀態列顯示出來,呼叫SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.top]);

需要一起呼叫底部虛擬按鍵(華為系列某些手機有虛擬按鍵),則SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.top, SystemUiOverlay.bottom]);
複製程式碼

9、Image.File 載入影象時檔案內容變化但影象不變

/* 在Flutter中,我們可以用下面的程式碼從檔案中載入影象:

Image.file(File(_fileName));

這個時候,當_fileName這個檔名稱和路徑不變,檔案內容變化時,Flutter並不會更新顯示。問題產生的原因是Flutter自動使用了快取。

Image.file 實際上會將 image 設定為 FileImage 這個 ImageProvider。FileImage 的程式碼中,在進行 operator 時,只判斷了檔案路徑和縮放比例。正是因為如此,我們檔案路徑不變,縮放比例不變時,Flutter會認為我們還是用的原圖,並不會重新進行載入。

於是,我想到了辦法是擴充套件一個FileImage,將這個 operator 的方式改一下。 */

新建立一個類

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;
  }

}

接下來,直接使用下面的程式碼來載入影象:

Image(image: FileImageEx(File(_fileName)));
複製程式碼

10、Inkell 去掉水波紋的方法

// 設定highlightColor為透明,同時設定radius為0

InkWell(
  highlightColor:Colors.transparent,
  radius: 0.0,
  onTap: (){
  },
  child: Text('跳過 ${_time}s'),
),
複製程式碼

11、打包release版本安卓apk包真機無法請求網路

// 原因:安卓開發中flutter應用沒有網路許可權

在專案目錄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" />


在專案目錄 android/src/main/AndroidManifest.xml 裡也有一個 AndroidManifest.xml檔案!跟之前的只不過是資料夾位置不同而已,同樣在manifest標籤下加入相同配置就行了,不要放到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導致的記憶體洩漏

// flutter端請求網路時,呼叫的是宿主App的網路請求。
// flutter通過訊息通道傳送一個訊息,然後await等待訊息返回,最終宿主app會呼叫reply.reply(obj)方法返回資料。
// 如果在這個過程中,flutter頁面關閉,就會出現如下異常,類似Android中的記憶體洩漏。

/* 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(). */


// 我們的錯誤原因是非同步訊息未返回,所以在setState方法之前呼叫mouted屬性進行判斷即可。具體示例如下:

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')


// 嘗試執行  flutter build ios  然後重新啟動Xcode
複製程式碼

14、更新showDialog以及showModalBottomSheet中的狀態中的內容

// 很多人在用showDialog的時候應該都遇到過這個問題,使用showDialog後,通過setState()無法更新當前dialog。其實原因很簡單,因為dialog其實是另一個頁面,準確地來說是另一個路由,因為dialog的關閉也是通過navigator來pop的,所以它的地位跟你當前主頁面一樣。這個概念一定要明確,因為無論在Android或iOS中,daliog都是依附於當前主頁面的一個控制元件,但是在Flutter中不同,它是一個新的路由。所以使用當前主頁面的setState()來更新,當然沒法達到你要的效果。

/* StatefulBuilder
很多人使用StatefulBuilder依然達不到更新的效果,是因為你用錯了setState()方法。
就像我們上面說的那樣,這個builder構建的控制元件,不會響應老頁面的任何操作,因為它們是兩個互不影響的路由控制的。 */

// 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');
                  // 注意不是呼叫老頁面的setState,而是要呼叫builder中的setState。
                  //在這裡為了區分,在構建builder的時候將setState方法命名為了state。
                  state(() {});  
              },
          );
      },
    );
  }
);

// 2、更新showModalBottomSheet
showModalBottomSheet(context:context, builder:(BuildContext context){
    return StatefulBuilder(
    builder:(context1, state) {///這裡的state就是setState
      return Container(
        child:OutlineButton(
          onPressed: (){
            state(() {///為了區分把setState改個名字
              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 元件內狀態更新,但UI未更新

SliverPersistentHeader(
  pinned: true,
  delegate: SliverBarDelegate(
    PreferredSize(
      preferredSize: Size.fromHeight(ScreenAdapter.height(80)),
      child:_headActions()
    ),
  ),
),

// SliverPersistentHeader   delegate的是重寫的SliverBarDelegate
class SliverBarDelegate extends SliverPersistentHeaderDelegate {
  final  widget;

  SliverBarDelegate(this.widget);

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return Container(
      child: widget,
    );
  }
  // 是否需要重新構建  
  // 如果傳遞的這幾個引數變化了,那就重寫建立
  // 如果返回false,則可能不會重新構建報頭,即使委託的例項發生了變化。
  // 原始碼中有描述到



  @override
  bool shouldRebuild(SliverBarDelegate oldDelegate) {
    return true;
  }

  @override
  double get maxExtent => widget.preferredSize.height;

  @override
  double get minExtent => widget.preferredSize.height;
}
複製程式碼

16、packages get 慢的解決方案

/* 
  國內使用 flutter packages get 命令,一直是  This is taking an unexpectedly long time 狀態

  科學上網無效

  windows解決方案:配置 【環境變數】 > 【使用者變數】:
  
  變數名:PUB_HOSTED_URL  值:https://pub.flutter-io.cn

  變數名:FLUTTER_STORAGE_BASE_URL  值:https://storage.flutter-io.cn

  最好重啟下windows電腦,flutter packages get 執行

*/

具體環境變數的值 需要看該網址 [Using Flutter in China](https://flutter.dev/community/china)
複製程式碼

Flutter填坑指南,總有一個遇得到。

Flutter填坑指南,總有一個遇得到。

Flutter填坑指南,總有一個遇得到。

17、如何將String型別的顏色轉化為Color所需要的int型別的顏色

  // 完整的顏色是8位,如果是RGB是6位顏色值可以直接新增
  Color(int.parse('0xff'+color)
複製程式碼

18、is a SingleTickerProviderStateMixin but multiple tickers were created.

// 報錯日誌:
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.

大概翻譯意思:單個TickerProviderStateMixin,但建立了多個Ticker,單個statemixin 只能供一個TickerProvider使用,如果一個狀態用於多個物件,它可能被傳遞給其他物件。

// 解決方案:將SingleTickerProviderStateMixin換成TickerProviderStateMixin
複製程式碼

19、動畫釋放報錯

// 報錯日誌:
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() {
//先呼叫controller.dispose釋放了動畫資源,再呼叫super
  controller.dispose();
  super.dispose();
}
複製程式碼

20、Could not resolve all files for configuration ':app:lintClassPath'.

// 報錯日誌:
* 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、設定沉浸式狀態列,取消android狀態列陰影

if (Platform.isAndroid) {
  // 以下兩行 設定android狀態列為透明的沉浸。寫在元件渲染之後,是為了在渲染後進行set賦值,覆蓋狀態列,寫在渲染之前MaterialApp元件會覆蓋掉這個值。
  SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle(statusBarColor: Colors.transparent);
  SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
}
複製程式碼

22、更改檔名或刪除名稱時報錯,were declared as an inputs, but did not exist. Check the definition of target:kernel_snapshot for errors

// 報錯日誌
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資料夾,然後重新執行即可。
複製程式碼

23、修改版本號不生效

Flutter的App版本號設定在pubspec.yaml中,+號前面是版本名稱,後面是版本號,在此修改會自動應用到Android和IOS專案對應版本號中,修改完安裝發現並未生效,解決方法:

// 解決方案:
// 修改後執行
1、flutter get
2、flutter clean
重新 build ios 安裝就能生效了

複製程式碼

24、Fluro中傳遞中文引數失敗

/*  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) */

無效的引數:URI中的非法編碼百分比

// 解決方案:

通過Uri.encodeComponent(Text)轉化

複製程式碼

25、TextField元件,報錯解決:The following assertion was thrown while handling a gesture

/* 在沒有正常的取消輸入框焦點的時候,就先清空輸入框元件,整體渲染順序是不正確的。 */

// 解決方案:
// 保證在元件build的第一幀時才去觸發取消清空內容
WidgetsBinding.instance.addPostFrameCallback((_) => controller.clear());

複製程式碼

26、TabController的監聽在點選Tab後會執行兩次

/* 每點選一次,監聽都有兩次回撥 */

// 解決方案:
// 對indexIsChanging進行判斷
_tabController.addListener((){
   if(_tabController.indexIsChanging){

   }
});  	

複製程式碼

相關文章