Riverpod 不必要的重建

2024-04-17

问题是:当我单击其中一个 CharBarButtons 小部件时,所有其他 CharBarButtons 也会被重建,是否有一种方法可以避免这种情况,并在使用 StateNotifier 保存列表时单独进行重建(整数是按钮的位置)

CharBarButtons :(这里我根据 isTapped 的布尔值更改 CharBarButtons 的装饰)


class CharBarButtons extends HookWidget {
  CharBarButtons({required this.index});
  final int index;
  @override
  Widget build(BuildContext context) {
    final isTapped = useProvider(IndexStackProvider).contains(index);
    final _randomBar = useProvider(randomBarProvider);
    final usableWidth = MediaQuery.of(context).size.width -
        (MediaQuery.of(context).padding.left +
            MediaQuery.of(context).padding.right);
    final usableHeight = MediaQuery.of(context).size.height -
        (MediaQuery.of(context).padding.bottom +
            MediaQuery.of(context).padding.top);
    return InkWell(
        onTap: () {
          if (isTapped == false) {
            context.read(IndexStackProvider.notifier).append(index);
          }
        },
        child: AnimatedContainer(
            curve: Curves.easeOutExpo,
            duration: Duration(milliseconds: 125),
            child: Center(
                child: Center(
                    child: Opacity(
              opacity: isTapped == false ? 1 : 0.5,
              child: Text('${_randomBar[index]}',
                  style: TextStyle(
                      color: Colors.black87,
                      fontWeight: FontWeight.bold,
                      fontSize: usableHeight * 0.03)),
            ))),
            width: usableWidth * 0.12,
            height: usableWidth * 0.12,
            decoration: isTapped == false
                ? BoxDecoration(
                    color: const Color.fromRGBO(255, 227, 212, 1),
                    border: Border.all(
                        color: Colors.black.withOpacity(0.6), width: 2),
                    borderRadius: BorderRadius.circular(usableWidth * 0.02),
                    shape: BoxShape.rectangle,
                  )
                : BoxDecoration(
                    color: Color.fromRGBO(199, 177, 165, 1),
                    border: Border.all(
                        color: Colors.black.withOpacity(0.3), width: 2),
                    borderRadius: BorderRadius.circular(usableWidth * 0.02),
                    shape: BoxShape.rectangle,
                  )));
  }
}

CharBarButtons 的祖先:CharBar 小部件是一个简单的小部件,决定应创建多少个 CharBarButton。

class CharBar extends HookWidget {
  const CharBar({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final usableWidth = MediaQuery.of(context).size.width -
        (MediaQuery.of(context).padding.left +
            MediaQuery.of(context).padding.right);
    final usableHeight = MediaQuery.of(context).size.height -
        (MediaQuery.of(context).padding.bottom +
            MediaQuery.of(context).padding.top);
    final _randomChars = useProvider(randomBarProvider);
    return Container(
      height: usableWidth * 0.24,
      width: usableWidth,
      child: _randomChars.length <= 6
          ? Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
              for (var i = 0; i < _randomChars.length; i++)
                CharBarButtons(index: i)
            ])
          : Column(children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  for (var i = 0; i < _randomChars.length ~/ 2; i++)
                    CharBarButtons(index: i)
                ],
              ),
              SizedBox(height: usableHeight * 0.05),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  for (var i = _randomChars.length ~/ 2;
                      i < _randomChars.length;
                      i++)
                    CharBarButtons(index: i)
                ],
              )
            ]),
    );
  }
}

这是 StateNotifier :

class IndexStackState extends StateNotifier<List<int>> {
  IndexStackState() : super([]);
  void append(int index) {
    state = [...state, index];
  }

  void delete() {
    state = [...state.sublist(0, state.length - 1)];
  }

  void reset() {
    state = [];
  }
}

这是提供者:

final IndexStackProvider =
    StateNotifierProvider.autoDispose<IndexStackState, List<int>>(
        (ref) => IndexStackState());

注意:CharBar(祖先)没有任何不必要的重建或另一个祖先。只有 CharBarButtons 进行了不必要的重建。

注 2:我知道 contains() 方法也很昂贵,但我将列表限制为最多 12 个整数元素。所以我认为这应该不是什么太大的问题。

注 3:问题是由于这行代码(代码的第一段)引起的,并且当状态更新时它会重建所有 CharBarButton,我无法以索引方式监听。我在这里监听整个列表。

final isTapped = useProvider(IndexStackProvider).contains(index);

注 4:实际问题是,我们是否可以将重建限制为索引方式,而不是监听整个列表状态


因为你正在使用hooks https://pub.dev/packages/flutter_hooks,这实际上是可能的。

Change:

final isTapped = useProvider(IndexStackProvider).contains(index);

to:

final isTapped = useProvider(indexStackProvider.select((value) => value.contains(index)));

select 语句使我们的小部件仅在函数参数返回的值发生更改时才重建。

我在下面提供了一个完整的示例,供任何想要自己测试的人使用。

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

class IndexStackState extends StateNotifier<List<int>> {
  IndexStackState() : super([]);

  void append(int index) {
    state = [...state, index];
  }

  void delete() {
    state = [...state.sublist(0, state.length - 1)];
  }

  void reset() {
    state = [];
  }
}

final indexStackProvider =
    StateNotifierProvider.autoDispose<IndexStackState, List<int>>((ref) => IndexStackState());

class ExampleWidget extends HookWidget {
  const ExampleWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          children: List.generate(5, (index) => TestWidget(index)),
        ),
      ),
    );
  }
}

class TestWidget extends HookWidget {
  const TestWidget(this.index, {Key? key}) : super(key: key);

  final int index;

  @override
  Widget build(BuildContext context) {
    final isTapped = useProvider(indexStackProvider.select((value) => value.contains(index)));
    debugPrint('Building index: $index');

    return Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        Center(
          child: Text('$index: ${isTapped.toString()}'),
        ),
        SizedBox(width: 16),
        ElevatedButton(
          onPressed: () {
            final notifier = context.read(indexStackProvider.notifier);
            isTapped ? notifier.delete() : notifier.append(index);
          },
          child: Text(isTapped ? 'Delete' : 'Append'),
        )
      ],
    );
  }
}

Docs here https://riverpod.dev/docs/concepts/reading/#useprovider-hooks_riverpod-only.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Riverpod 不必要的重建 的相关文章

  • Flutter - 每次应用程序重新启动后保留变量的值

    在我的一页上 我希望我的用户从一个变量上的默认文本开始 codeDialog 然后我希望他们更改该文本 之后他们编写的文本将成为我的新默认文本 遗憾的是我无法让它发挥作用 现在 当我重新启动应用程序并打开该屏幕时 它会重置为null 我认为
  • flutter stepper 小部件 - 验证各个步骤中的字段

    我正在使用步进器小部件来收集用户信息并对其进行验证 我需要在每个步骤调用 API 因此在每个继续按钮的步骤中验证每个字段 我正在使用表单状态和表单小部件 但是问题是它验证步进器中所有步骤中的整个字段 我如何仅验证步进器中的单个步骤 我浏览了
  • Flutter中如何在弹出屏幕后调用函数更新值?

    屏幕 1 显示带有添加按钮的项目列表 屏幕 2 用于将新项目添加到列表的表单 屏幕 2 gt gt 屏幕 1 在屏幕 2 中调用 navigator pop 时 如何在屏幕 1 中调用方法 setState 更新列表 谁能帮我吗 我不想再次
  • 如何取消配置 flutterfire?

    我们在 CLI 中使用以下命令将 flutter 应用程序与 firebase 连接 flutterfire configure 如文档中所述 https firebase flutter dev docs overview https f
  • Flutter MacOs 访问文件

    我正在尝试在 Flutter 中构建一个自定义桌面应用程序 以便能够加载图片 例如图库 为此 我将要求用户选择一个文件夹 它会自动显示图片 现在 从简单的事情开始 由于这是第一次为 Mac 开发 我只是尝试通过Image file new
  • 使用 flutter 处理 Appcheck 时出错

    我想在 firebase 存储中上传文件 但经过多次研究后出现了 appcheck 错误 我发现我必须在 firebase 上激活 Appcheck 而且还要在我的应用程序上激活它 在 youtube 上的谷歌视频中 我看到我必须在构建我的
  • 如何在flutter中实现字母滚动

    有没有任何插件或方法可以在 flutter 中实现这种滚动 具体来说 右侧的字母列 例如突出显示当前字母表字母 或者如果点击某个字母 则滚动视图会直接转到该字母标题 为了按字母顺序排序 我们可以这样做List sort 对于粘性标题 我们也
  • 如何根据数据值过滤flutter中的Hive数据?

    在颤振中 我使用ValueListenableBuilder用于获取配置单元数据列表的小部件 我正在尝试按数据值过滤数据 例子 Key 1 name value mydata1 des value mydescription1 value
  • 将“Map>”转换为“Map>”

    我想投一个Map
  • Flutter Firebase 身份验证 currentUser() 返回 null

    这是关于 Flutter Firebase 身份验证插件的 我试图在创建新用户后发送验证电子邮件 但 sendEmailVerification 内部使用 currentUser 这对我来说似乎是一个错误 但为了以防万一 我在 stacko
  • 在 dart 的 MultipartRequest 中添加授权标头

    我在用多部分请求 https pub dartlang org documentation http latest http MultipartRequest class html在 Dart 中以便将文件上传到 API 但是我需要在我的请
  • 没有脚手架的 DefaultTabController?

    我正在尝试使用DefaultTabController在一些小部件的中间 所以我的TabBar不能在AppBar并且必须关闭一些小部件 所以我的问题是当我使用时TabBarView它崩溃了 这是一个 Flutter 示例的示例 但没有找到如
  • 如何修复 [错误:flutter/lib/ui/ui_dart_state.cc(166)] 未处理的异常:NoSuchMethodError:在 null 上调用方法 '<='。错误?

    我收到此错误 但由于昨天相同的代码有效 所以我不知道如何修复它 这是一个测试应用程序 使用谷歌地图和 firebase firestore 来保存和查询地图上的标记 我想这可能和我安装的flutter有关系 所以我尝试使用稳定版和测试版 但
  • 如何在flutter中绕过SSL证书验证?

    如何在flutter中绕过SSL证书验证 错误 握手异常 客户端中的握手错误 操作系统错误 CERTIFICATE VERIFY FAILED 自签名证书 handshake cc 345 您需要配置 HttpService 以使用自签名
  • 如何修复 webview_flutter 中的白屏?

    运行 webview flutter 示例应用程序 https github com flutter plugins tree master packages webview flutter example https github com
  • Flutter - 构建失败并出现异常

    当我启动我的应用程序时 我收到此错误消息 自上次运行以来我没有进行任何更改 当时一切都很好 有人知道如何解决这个问题吗 谢谢 FAILURE Build failed with an exception 什么地方出了错 无法确定任务 app
  • 使用 Flutter 基础类型而不使用 Flutter

    我需要在没有 Flutter 的环境中使用 lib package flutter foundation dart 中存在三种类型 他们是 ByteData ReadBuffer and WriteBuffer 是否可以在不依赖整个 Flu
  • 如何在 Flutter Provider 中删除 StreamController 中的数据?

    我正在使用provider来构建我的应用程序 因此数据被添加到StreamController中 每次刷新我的应用程序时 它都会调用API 然后将数据推送到StreamController 问题是如何在替换之前删除数据新的那一个 contr
  • 当我使用 ListView 时,ListTile OnTap 正在工作。但是当我使用 ListWheelScrollView 时它不起作用

    当我使用 ListView 时 ListTile OnTap 正在工作 但是当我使用 ListWheelScrollView 时它不起作用 我的意思是它不会被窃听 观点发生变化 但我似乎无法点击它 我在很多地方和链接中寻找解决方案 但仍然找
  • Flutter 网络图像作为 Google 地图标记

    我想在屏幕上的谷歌地图上添加网络图像作为标记 API确实支持一个功能Bitmapdescriptor fromBytes 但是 我不知道如何将它与网络图像一起使用 BitmapDescriptor fromBytes byteData 首先

随机推荐