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

相关推荐
笑尘pyrotechnic7 小时前
DocC的简单使用
ios·objective-c
谈吐大方的鹏sir8 小时前
SwiftUI-Text组件学习
ios
你听得到118 小时前
Flutter - 手搓一个日历组件,集成单日选择、日期范围选择、国际化、农历和节气显示
前端·flutter·架构
不自律的笨鸟9 小时前
iOS 26,双版本更新来了
ios·iphone
RaidenLiu13 小时前
Flutter Shader预热技术解析与实践指南
flutter·前端框架
归辞...14 小时前
「iOS」————消息传递和消息转发
ios
他们都不看好你,偏偏你最不争气1 天前
iOS —— 天气预报仿写总结
ios
ITfeib1 天前
Flutter基础
flutter
RaidenLiu1 天前
RepaintBoundary是什么?怎么用?
flutter
白玉cfc1 天前
【iOS】网易云仿写
ui·ios·objective-c