Flutter 问题收集及解决方案 持续更新中...

前端小弟一个,Flutter初学者,问题都是项目中遇到的,所以记录了下来。

个人前端博客.

解决方案来自于日常总结及各路大佬。

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<dynamic>' is not a subtype of type 'List<Widget>'

// 这里的问题是类型推断以意想不到的方式失败。解决方案是为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)
image
image

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

   }
});     

27、判断页面渲染完毕

/* 在flutter中,我们获取大小也必须在元素渲染完成的时候才行,而有些应用场景要求在第一时间获取到这个元素的大小。那么怎么在第一时间判断元素渲染完成呢?flutter中的WidgetsBuiding这个类就可以用来判断: */

/* 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);
  }
  // 渲染完成之后获取某个元素的大小
  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;
        });
  }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345