Flutter Android 发布 UI 渲染失败

Posted

技术标签:

【中文标题】Flutter Android 发布 UI 渲染失败【英文标题】:Flutter Android release UI render failure 【发布时间】:2021-08-05 13:35:55 【问题描述】:

我已经使用 MobX 作为我的状态管理构建了一个 Flutter 应用程序,但目前遇到了一个奇怪的问题,该问题仅在以发布模式运行的 android 上存在。

我不确定违规者是 MobX、Hive 还是 Android 本身的 Flutter。但是,在我的应用程序的这个特定页面上,观察者只会显示列表中的最后一个条目。存在其他项目,但 UI 将仅显示列表的最后一个索引。当我将手机横向翻转时,列表的全部内容都可见,并且页面完全按照预期显示。当页面已经加载时,有没有办法强制小部件在 MobX 中重新渲染?

我尝试将目标 SDK 降级到 28,降级 gradle 版本,将 shrinkResources & minifyEnabled 设置为 false,启用 proguard。我还确保在我的 main.dart 中调用它;

WidgetsFlutterBinding.ensureInitialized();

还附上了我的颤振医生的输出。

同样,此问题仅存在于我的应用的 Android 版本版本中。它在调试时在 ios 和 Android 上完美运行。

任何帮助将不胜感激。

以下是在发布模式下呈现问题的相关小部件

    Widget _carPackageList() 
    return ListView.builder(
        physics: NeverScrollableScrollPhysics(),
        shrinkWrap: true,
        itemCount: viewModel.customerCars.length,
        itemBuilder: (context, index) => _carListItem(index));
  

  Widget _carListItem(index) 
    var packageDetails = viewModel.getDetailBookingById(
        viewModel.customerCars[index].packageGroupId);
    return Observer(
      builder: (context) 
        if (viewModel.isLoading) 
          return CustomProgressIndiactor();
         else 
          return Padding(
            padding: const EdgeInsets.all(8.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Flexible(
                      child: Column(
                        children: [
                          Text(
                              viewModel.customerCars[index].makeAndModel,
                              maxLines: 2,
                              style: TextStyle(
                                  fontSize: 25,
                                  fontFamily: 'Domus',
                                  color: Color(0xff3c99a0))),
                        ],
                      ),
                    ),
                    IconButton(
                        icon: Icon(Icons.highlight_remove, color: Colors.black),
                        onPressed: () 
                          viewModel.removeCustomerCar(index);
                          viewModel.customerCars.removeAt(index);
                        )
                  ],
                ),
                Card(
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(20.0),
                  ),
                  child: Container(
                    width: double.infinity,
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: <Widget>[
                        Row(
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          children: [
                            Flexible(
                              child: Column(
                                children: [
                                  Padding(
                                    padding: const EdgeInsets.all(15.0),
                                    child: Text(packageDetails.name,
                                        overflow: TextOverflow.ellipsis,
                                        maxLines: 2,
                                        style: TextStyle(
                                            fontSize: 25,
                                            fontFamily: 'Domus',
                                            color: Color(0xff3c99a0))),
                                  ),
                                ],
                              ),
                            ),
                            Padding(
                              padding: const EdgeInsets.all(15.0),
                              child: Observer(
                                builder: (_) 
                                  var groupPrice =
                                      viewModel.calculateCarCost(
                                          viewModel.customerCars[index],
                                          viewModel.customerCars[index]
                                              .packageGroupId);
                                  if (groupPrice == null) 
                                    return Text('£0.00',
                                        style: TextStyle(
                                            color: Color(0xff1A2E35),
                                            fontFamily: 'Aileron',
                                            fontWeight: FontWeight.w700));
                                   else 
                                    return Text(
                                      '£$groupPrice.toStringAsFixed(2)',
                                      style: TextStyle(
                                          color: Color(0xff1A2E35),
                                          fontFamily: 'Aileron',
                                          fontWeight: FontWeight.w700),
                                    );
                                  
                                ,
                              ),
                            ),
                          ],
                        ),
                        Padding(
                          padding: const EdgeInsets.only(
                              left: 15.0, top: 0, right: 15),
                          child: Text(packageDetails.description,
                              style: TextStyle(
                                  color: Color(0xff1A2E35),
                                  fontFamily: 'Aileron',
                                  fontSize: 16,
                                  fontWeight: FontWeight.w300)),
                        ),
                        Padding(
                          padding: const EdgeInsets.all(15.0),
                          child: Row(
                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
                            children: [
                              Text('Optional Extras',
                                  style: GoogleFonts.lato(
                                      fontSize: 16,
                                      color: Colors.black,
                                      fontWeight: FontWeight.bold)),
                              Observer(
                                builder: (_) 
                                  var opExtraPrice = viewModel
                                      .calculateOptionalExtrasPrice(viewModel
                                          .customerCars[index]
                                          .optionalExtras);
                                  if (opExtraPrice == null) 
                                    return Text('£0.00',
                                        style: TextStyle(
                                            color: Color(0xff1A2E35),
                                            fontFamily: 'Aileron',
                                            fontWeight: FontWeight.w700));
                                   else 
                                    return Text(
                                        '£$opExtraPrice.toStringAsFixed(2)',
                                        style: TextStyle(
                                            color: Color(0xff1A2E35),
                                            fontFamily: 'Aileron',
                                            fontWeight: FontWeight.w700));
                                  
                                ,
                              ),
                            ],
                          ),
                        ),
                        Padding(
                            padding: const EdgeInsets.all(15.0),
                            child: ListView.builder(
                              shrinkWrap: true,
                              physics: NeverScrollableScrollPhysics(),
                              itemCount: viewModel.customerCars[index]
                                  .optionalExtras.length,
                              itemBuilder: (context, innerIndex) 
                                var detailedOptionalExtra =
                                    viewModel.getOptinalExtraById(viewModel
                                        .customerCars[index]
                                        .optionalExtras[innerIndex]
                                        .packageItemId);
                                return Text(
                                  detailedOptionalExtra.name,
                                  style: TextStyle(
                                      color: Color(0xff1A2E35),
                                      fontFamily: 'Aileron',
                                      fontSize: 16,
                                      fontWeight: FontWeight.w300),
                                );
                              ,
                            )),
                        Padding(
                          padding: const EdgeInsets.all(15.0),
                          child: Row(
                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
                            children: <Widget>[
                              Text(
                                'Total Cost:',
                                style: TextStyle(
                                    color: Color(0xff1A2E35),
                                    fontFamily: 'Aileron',
                                    fontSize: 16,
                                    fontWeight: FontWeight.w700),
                              ),
                              Observer(
                                  builder: (context) => Text(
                                      '£$viewModel.currentTotalCost.toStringAsFixed(2)',
                                      style: TextStyle(
                                          color: Color(0xff1A2E35),
                                          fontFamily: 'Aileron',
                                          fontWeight: FontWeight.w700))),
                            ],
                          ),
                        )
                      ],
                    ),
                  ),
                ),
              ],
            ),
          );
        
      ,
    );
  

【问题讨论】:

【参考方案1】:

我找到的解决方案是使用ValueListenableBuilder 直接从 Hive 框生成列表,以收听框并在它们到达框后立即将更多元素添加到列表中。我只能假设 MobX 尝试从 Hive 收集盒子中的元素,然后提供给 UI 层时发生了某种竞争情况。我将在下面附上一些示例代码,以供可能遇到类似问题的其他人使用。

  Widget buildList() 
var _box = Hive.box("myBox").listenable();
return ValueListenableBuilder(
    valueListenable: _box,
    builder: (context, box, widget) 
      return ListView.builder(
        itemCount: box.length,
        itemBuilder: (context, index) 
          return Container(
              decoration: BoxDecoration(
                  border: Border(bottom: BorderSide(width: 0.1))),
              child: ListTile(
                title: Text(Hive.box("myBox").getAt(index)),
              ),
            );
        ,
      );
    );

【讨论】:

以上是关于Flutter Android 发布 UI 渲染失败的主要内容,如果未能解决你的问题,请参考以下文章

Flutter UI渲染分析

如何在集成了 Flutter 模块的 Android 应用上运行 UI 测试?

初识跨平台UI框架Flutter

谷歌的开源跨平台移动 UI 框架Flutter

Flutter专题Android Flutter入门笔记技术解析与项目实战

Flutter专题Android Flutter入门笔记技术解析与项目实战