flutter遇到问题及解决方案

目录

1、easy_refresh相关问题

[2、 父子作用域关联问题](#2、 父子作用域关联问题)

[3. 刘海屏底部安全距离](#3. 刘海屏底部安全距离)

[4. 了解保证金弹窗 iOS端闪退 (待优化)](#4. 了解保证金弹窗 iOS端闪退 (待优化))

[5. loading无法消失](#5. loading无法消失)

[6. dialog蒙版问题](#6. dialog蒙版问题)

[7. 倒计时优化](#7. 倒计时优化)

[8. scrollController.offset报错](#8. scrollController.offset报错)

[9. 断点不走](#9. 断点不走)

10.我的出价报红

[11. 竞拍大厅折叠效果与滚动冲突 & 加载完成状态无法上拉加载](#11. 竞拍大厅折叠效果与滚动冲突 & 加载完成状态无法上拉加载)

[12. 加载网络图片失败的页面返回报错(未解决)](#12. 加载网络图片失败的页面返回报错(未解决))

[13. flutter页面进入后快速返回 报错](#13. flutter页面进入后快速返回 报错)

[14. list. first 或 firstWhere报错 List firstWhere Bad state: No element](#14. list. first 或 firstWhere报错 List firstWhere Bad state: No element)

[15. 我的出价页面滑动太灵敏](#15. 我的出价页面滑动太灵敏)

[16. 禁用侧滑](#16. 禁用侧滑)

[17. ListView底部 iOS有间距,但Android没有](#17. ListView底部 iOS有间距,但Android没有)


1、easy_refresh相关问题

a. 仅有下拉刷新,viewModel可以不需要控制刷新状态。不用传controller

b. 传controller,在请求成功和失败后,都需要自行去管理刷新状态

问题一:我的出价多次快速切换按钮,调用下拉刷新,onRefresh方法不回调

解决方案:

Dart 复制代码
controller.callRefresh(duration: const Duration(milliseconds: 1), force: true);

问题二:下拉刷新,添加筛选项,回到列表页,上划列表,请求被取消

原因:ClassicHeader和ClassicFooter 有 triggerOffset, ///触发器任务的偏移量 和 maxOverOffset, ///最大超限偏移,将不再滚动

解决方案:将两个值设置成相同,则会在滑到 "松开刷新" 的状态时,松手的一刻就回调 onRefresh 和 onLoad 方法。

Dart 复制代码
triggerOffset: 100,  ///触发器任务的偏移量
maxOverOffset: 100,  ///最大超限偏移,将不再滚动

问题三:列表请求下一页,去重后数据少于1条,底部显示加载完成,且上拉加载无效,列表下滑一点再上拉才可以。

解决方案:关闭无限滚动 infiniteOffset设为null,默认值70

2、 父子作用域关联问题

问题:AScope创建BScope,BScope中调用A的ViewModel,与AScope的ViewModel,不是同一个。

现象:竞拍大厅使用Scope,筛选后竞拍大厅未同步筛选项

解决方案:

子作用域创建时需将父作用域的container传过来,并实现parent方法,即实现了父子作用域绑定关系

3. 刘海屏底部安全距离

a. ScreenUtil().bottomBarHeight 获取到的值为0,建议不再使用

b. 可以使用 MediaQuery.of(context).padding.bottom, 获取的值正常

c. AppUtil中提供了safeBottom方法

由于调用该方法会监听context,当下个页面存在输入框时,拉起关闭键盘都会重新rebuild页面,导致ui闪动。因此将bottom的获取放到app初始化时,且仅获取一次(判空逻辑改为copyWith应该也行)

4. 了解保证金弹窗 iOS端闪退 (待优化)

a. demo中debug、profile正常运行

b. 经销商app源码接入后,正常运行

c. 经销商app framework接入后,运行闪退

调用路径:webview_flutter => plugin webview_flutter_wkwebview => native wkwebview

暂未发现经销商app影响调用功能的代码。闪退的堆栈中,number类型调用字典取值方法导致的闪退,FLTWebController为三方的plugin方法实现。为不影响上线,iOS改用原生弹窗。

5. loading无法消失

搜索页加载中侧滑返回后,loading无法消失

原因:

Dart 复制代码
  @override
  void dispose() {
    disposed = true;
    CommonRequest().remove(this);
    AppUtil().dismissLoading(); // 优化后代码
    super.dispose();
  }

dispose时移除了请求监听,导致onDone,onError没有监听回调,loading dismiss方法也就不会触发

因此在移除请求监听时,手动dismiss loading即可。

6. dialog蒙版问题

a. flutter无法给底部tab添加蒙版

解决方案:增加plugin方法调用原生

Dart 复制代码
@async void setTabBarMasker(bool hidden);

b. 弹窗后触发引导切换到其他tab, 蒙版无法消失

c. 多级flutter页面 iOS侧滑后,弹窗在前一个页面未dismiss

解决方案:增加flutter plugin :

Dart 复制代码
void dismissSmartDialog();

  /// tab切换 dismiss已弹出的dialog
  @override
  Future<void> dismissSmartDialog() async {
    await SmartDialog.dismiss();
    await SmartDialog.dismiss();
    await SmartDialog.dismiss();
  }

原生切换tab,或flutter页面侧滑时,移除dialog(一般不会超过三个,容错移除三次,简单粗暴)

遇到问题:首次进入app,快速切换底部tab,蒙层不消失

原因一 猜您喜欢请求有滞后,切换到其他tab后,请求才成功并触发原生弹出蒙层。

解决方案:添加pageshow provider,同时监听pageshow provider和引导显示的provider

Dart 复制代码
visible: ref.watch(showGuideOfGuessLikeProvider) && ref.watch(isPageShowProvider),

原因二:生命周期未成对出现。

页面首次创建时并没有执行pageShow,pageShow方法是我们自己添加的渲染帧调度监听触发的。

Dart 复制代码
  @override
  void initState() {
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      if (!mounted) {
        return;
      }
      if (needPageObserver() && BoostNavigator.instance.isTopPage(context)) {
        onPageShow();
      }
      onFirstFrame();
    });
    super.initState();
  }

方案二: 改用全局弹窗,router:custom_dialog,present一个半透明弹窗,可以遮盖tabbar

参考 放弃收购弹窗。

遇到问题一: 弹窗pop后,前一个页面loading卡帧,无法消失

解决方案:push.then 后移除loading

建议使用方案二,但不要认为方案二遇到的问题会比方案一少,需要注意flutter各种奇奇怪怪的问题

7. 倒计时优化

a. 列表使用的全局定时器,widget频繁监听,影响性能

解决方案:pageHide移除监听,列表倒计时结束移除监听。

b. 报错Bad state: Cannot use "ref" after the widget was disposed.

原因: 列表快速滚动,调用ref代码时widget已经被释放了

解决方案:onPageShow调用ref前先判断ref.context.mounted为true。(未从根源上解决问题)

mounted解释说明:

在Flutter中,`mounted`是一个布尔类型的变量,它表示当前State对象是否已经被插入到树中去了。当这个State对象被添加到树中时,`mounted`变量就会被设置为`true`。如果State对象被删除,那么`mounted`变量就会被设置为`false`。

开发者可以使用`mounted`变量来判断当前的State对象是否还有效,如果`mounted`为`true`,则可以安全地调用`setState()`方法进行状态更新。如果`mounted`为`false`,则不应该调用`setState()`,因为这个State对象已经被从树中删除了,状态更新将不会生效。

参考 BidHallItemChildCountDown

Dart 复制代码
@override
void watchGenerateViewModel(WidgetRef ref) {
  super.watchGenerateViewModel(ref);
  remainSeconds = model.awayFromEnd! - DateTime.now().millisecondsSinceEpoch ~/ 1000;
  /// 监听定时器刷新 PageHide不监听
  if (ref.watch(shouldWatchTimerProvider) && remainSeconds > 0) {
    ref.watch(commonTimer);
  }
}

@override
void onPageHide() {
  super.onPageHide();
  ref.read(bidHallItemViewModelProvider).updateShouldWatchTimer(false);
}

@override
void onPageShow() {
  super.onPageShow();
  if (ref.context.mounted){
    ref.read(bidHallItemViewModelProvider).updateShouldWatchTimer(true);
  }
}

@override
bool needPageObserver() {
  return true;
}

根本原因是生命周期未成对出现,渲染帧调度监听添加mounted判断后再触发onPageShow,同6-原因二,优化后onPageShow不需要再判断mounted

8. scrollController.offset报错

a.scrollController加到了easy_refresh的scrollController上,应该是widget的controller

b.列表加了scrollController,但是空视图页面没有加scrollController

9. 断点不走

检查下新加的代码,有可能在断点前面的代码抛异常了

10.我的出价报红

复制代码
报错日志:如下图所示
Dart 复制代码
flutter: The following assertion was thrown:
flutter: Tried to change the number of overrides. This is not allowed -- overrides cannot be removed/added,
flutter: they can only be updated.
flutter: 'package:riverpod/src/framework/container.dart':
flutter: Failed assertion: line 373 pos 7: '_debugOverridesLength == overrides.length'

原因:

You didn't create two separate ProviderScope. The second one updated the first one.

If you want two different scope, give them a different Key

issue 链接: "Tried to change the number of overrides" when running golden tests on multiple widgets with different ProviderScopes · rrousselGit/riverpod · Discussion #2804 · GitHub

解决方案:给ProviderScope添加不同的key

Dart 复制代码
ListView.builder(
    controller: viewModel.scrollController,
    itemCount: itemVMList.length,
    itemBuilder: (BuildContext context, int position) {
      return _buildChildItemProvider(itemVMList, position);
    }),


DealerItemProvider? _buildChildItemProvider( List<DealerItemViewModel> myPriceList, int position) {
  if (widget.myPriceType == Const.BIDING) {  ///竞拍中
    if (myPriceList[position] is ItemBiddingChildVM) {
      return DealerItemProvider(
        key: const Key('ItemBidding'), ///需要设置不同key
        itemOverrides: overrideItemBiddingChildVM(
            model: myPriceList[position] as ItemBiddingChildVM,
            index: position),
        child: ItemBiddingChild(),
      );
    } else if (myPriceList[position] is CommonRecommendTitleViewModel) {
      return DealerItemProvider(
          key: const Key('CommonRecommend'), ///需要设置不同key
          itemOverrides: overrideCommonRecommendTitleViewModel(
              model: myPriceList[position] as CommonRecommendTitleViewModel,
              index: position),
          child: CommonRecommendTitleWidget());
    } else if (myPriceList[position] is BidHallItemViewModel) {
      return DealerItemProvider(
          key: const Key('CommonRecommend'), ///需要设置不同key
          itemOverrides: overrideBidHallItemViewModel(
              model: myPriceList[position] as BidHallItemViewModel,
              index: position),
          child: BidHallItem());
    }
  }
  return null;
}

11. 竞拍大厅折叠效果与滚动冲突 & 加载完成状态无法上拉加载

a. 折叠效果通过NestedScrollView实现,列表刷新数据后滚动到顶部是在list添加scrollController实现,二者相冲突,只有一个生效

b. 列表请求多页数据,筛选第一页数据后,最下面显示加载完成,无法上拉加载。

解决方案: NestedScrollView添加key,触发请求前调用NestedScrollView滚动到顶部的方法

注意:调用innerController.jumpTo(0) 固定收起折叠样式。调用outerController.jumpTo(0); 固定展开折叠样式。因此需判断当前折叠状态。

Dart 复制代码
final GlobalKey<NestedScrollViewState> scrollKey = GlobalKey<NestedScrollViewState>();
/// 触发下拉刷新 手动滚动到顶部
void refreshDataWithAnimate() {
  if (pageState != ViewState.BUSY){
    if (collapsedState){
      scrollKey.currentState?.innerController.jumpTo(0);
    }else{
      scrollKey.currentState?.outerController.jumpTo(0);
    }
  }

  refreshData();
}

c. 偶现 首次初始化列表 折叠区域(豆腐块),一直处于折叠状态,无法展开。

原因:静态常量声明使用了.w, 初始化时,scaleWidth有概率为0。原因是screenUtil初始化落后于静态常量初始化导致的

解决方案: 暂时这里不使用静态常量

根本解决方案:

I. 静态常量不使用.w

II. 更改定义方案,改为变量属性,总之让调用.w晚于screenUtil初始化

12. 加载网络图片失败的页面返回报错(未解决)

Dart 复制代码
​
flutter: ══╡ EXCEPTION CAUGHT BY FLUTTER FRAMEWORK ╞═════════════════════════════════════════════════════════
flutter: The following StateError was thrown:
flutter: Bad state: Failed to load https://pic4.zhimg.com/100/v2-1c0788ea29fe3adaa98ff4ac0.jpg.
flutter:
flutter: When the exception was thrown, this was the stack
flutter: ════════════════════════════════════════════════════════════════════════════════════════════════════
flutter: Another exception was thrown: Bad state: Failed to load https://pic4.zhimg.com/100/v2-1c0788ea29fe3adaa98ff4ac0.jpg.

原因: 加载图片ExtendedImage.network,页面返回时,重新调用了load方法,报错,且会有堆栈上传。

13. flutter页面进入后快速返回 报错

Dart 复制代码
 [SmartDialog] 'package:flutter/src/widgets/routes.dart': 
Failed assertion: line 1579 pos 12: '_scopeKey.currentState != null': 
Tried to add a willPop callback to a route that is not currently in the tree.

原因:withContainer为false时,不新开container,页面创建太快,立刻点击返回,触发willpop,而此时树中还没有这个路由,因此报错(若解释不对请修正)

解决方案:push flutter页面withContainer改为true。

14. list. first 或 firstWhere报错 List firstWhere Bad state: No element

列表为空数组,调用first方法会崩溃。

列表调用firstWhere 找不到数据也会崩溃。

建议使用列表扩展中的safeFirst 和safeFirstWhere方法。

15. 我的出价页面滑动太灵敏

原因:Tabbar->TabBarView->pageController(PageView->controller) 滚动有动画,时间300ms,滑动切换tab后,动画还没停止,上滑的时候,动作依然被pageController接管,导致上滑触发了左右滑动。

解决方案: 使用ExtendedTabBarView

Dart 复制代码
/// 构建我的出价 子页面
Widget _buildChildList() {
  return Container(
    color: themeColors.common_bg2_color,
    padding: EdgeInsets.only(bottom: Platform.isAndroid ? 90.w : 0),
    child: ExtendedTabBarView(
      physics: const LessSpringClampingScrollPhysics(),
      shouldIgnorePointerWhenScrolling:false,
      link: false,
      controller: viewModel.tabController,
      children: _getTabKeepAliveList(),
    ),
  );
}

16. 禁用侧滑

方式一:路由push增加参数 'disablePopGesture': true,

方式二:动态禁用侧滑

Dart 复制代码
final PageInfo? pageInfo = RouteNavigator.getPageInfoByContext(ref.context);
BoostChannel.instance.disablePopGesture(containerId: pageInfo?.uniqueId ?? '');

17. ListView底部 iOS有间距,但Android没有

原因: iOS底部有安全距离,安卓没有,原因代码如下:

解决方案:ListView设置padding为zero

相关推荐
比格丽巴格丽抱10 小时前
flutter项目苹果编译运行打包上线
flutter·ios
SoaringHeart11 小时前
Flutter进阶:基于 MLKit 的 OCR 文字识别
前端·flutter
网络安全-老纪12 小时前
iOS应用网络安全之HTTPS
web安全·ios·https
1024小神14 小时前
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
android·ios·tauri
AiFlutter15 小时前
Flutter通过 Coap发送组播
flutter
lzhdim15 小时前
iPhone 17 Air看点汇总:薄至6mm 刷新苹果轻薄纪录
ios·iphone
安和昂15 小时前
【iOS】知乎日报第四周总结
ios
麦田里的守望者江18 小时前
KMP 中的 expect 和 actual 声明
android·ios·kotlin
_黎明20 小时前
【Swift】字符串和字符
开发语言·ios·swift
ZVAyIVqt0UFji21 小时前
iOS屏幕共享技术实践
macos·ios·objective-c·cocoa