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



作者:jahnli
链接:https://www.jianshu.com/p/35b3c5a0c90d
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
方案是为解决特定问题或达成特定目标而制定的一系列计划或步骤。它的作用是提供一种系统性的方法,以有效地应对挑战、优化流程或实现目标。以下是方案的主要作用: 问题解决: 方案的核心目标是解决问题。通过系统性的规划和执行,方案能够分析问题的根本原因,提供可行的解决方案,并引导实施过程,确保问题得到合理解决。 目标达成: 方案通常与明确的目标相关联,它提供了一种达成这些目标的计划。无论是企业战略、项目管理还是个人发展,方案的制定都有助于明确目标并提供达成目标的路径。 资源优化: 方案在设计时考虑了可用资源,以最大化其效用。通过明智的资源分配,方案可以在有限的资源条件下实现最大的效益,提高效率并减少浪费。 风险管理: 方案通常对潜在的风险进行评估,并制定相应的风险管理策略。这有助于减轻潜在问题的影响,提高方案的可行性和可持续性。 决策支持: 方案提供了决策者所需的信息和数据,以便做出明智的决策。这种数据驱动的方法有助于减少不确定性,提高决策的准确性。 团队协作: 复杂的问题通常需要多个人的协同努力。方案提供了一个共同的框架,帮助团队成员理解各自的职责和任务,促进协作并确保整个团队朝着共同的目标努力。 监控与评估: 方案通常括监控和评估的机制,以确保实施的有效性。通过定期的评估,可以及时调整方案,以适应变化的环境或新的挑战。 总体而言,方案的作用在于提供一种有序、有计划的方法,以解决问题、实现目标,并在实施过程最大化资源利用和风险管理。
Flutter 是一个流行的跨平台移动应用程序开发框架,可用于 iOS 和 Android 平台。要实现后台持续定位,你可以使用 Flutter 的 geolocator 插件。 首先,你需要在 pubspec.yaml 文件添加 geolocator 依赖项。 ```yaml dependencies: geolocator: ^5.3.2+2 ``` 然后,在你的代码导入 geolocator 库,创建一个 Geolocator 实例并启动位置流。 ```dart import 'package:geolocator/geolocator.dart'; // 创建一个 Geolocator 实例 final Geolocator geolocator = Geolocator()..forceAndroidLocationManager; // 启动位置流,要求持续定位 StreamSubscription<Position> positionStream = geolocator.getPositionStream( desiredAccuracy: LocationAccuracy.high, distanceFilter: 10, // 10米以上的移动才触发位置更新 ).listen((Position position) { // 处理位置变化 }, onError: (error) { // 处理错误 }); ``` 在应用程序处于后台运行时,你需要在 AndroidManifest.xml 文件添加一些配置,以确保应用程序可以继续接收位置更新。在 \<application> 标记内部添加以下代码: ```xml <service android:name="com.lyokone.location.LocationService" /> <receiver android:name="com.lyokone.location.LocationBroadcastReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.QUICKBOOT_POWERON" /> </intent-filter> </receiver> ``` 最后,在你的代码添加以下代码,以确保应用程序在终止时停止位置流。 ```dart // 停止位置流 positionStream.cancel(); ``` 希望这可以帮助你实现后台持续定位。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值