我几乎每次都将 Stateless Widget 与 BLoC 一起使用。我错了吗?

2023-12-29

我很难理解如何使用 Flutter 处理某些特定情况下的状态。

例如,假设我需要一个页面,单击按钮即可从 API 获取数据。这样的请求可能需要时间或者可能发生任何类型的问题。因此,我可能会使用 BLoC 模式在请求经历各种“状态”时正确通知用户,例如loading, done, failed等等。

Now,假设我有一个页面使用Timer定期(每 1 秒)更新Text新的小部件elapsed time的值Stopwatch。需要正确停止计时器(timer.cancel())一旦不再使用。出于这个原因,我会使用Stateful小部件并直接在中停止计时器dispose state.

However,当一个页面同时满足以下条件时应该做什么:

  • 从 API 和/或其他需要正确处理状态的服务获取数据。
  • Uses a Timer或任何需要正确取消/关闭/处置的内容(流?)

现在,我有一个进行 API 调用的页面,并且also持有这样一个Timer。该页面涉及 BLoC 模式以及Timer已经让整个事情变得有点乏味了。事实上,创建 BLoC“只是”为了更新Timer感觉有点矫枉过正。

But now,我面临一个问题。如果用户不以“常规”方式浏览页面并决定使用手机的“后退按钮”:我永远无法取消Timer。这反过来会引发以下错误:Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe.

确实,即使在拥有了pop()到上一页,Timer仍在运行并尝试完成其每 1 秒的任务:

Timer.periodic(
      const Duration(seconds: 1),
      (Timer t) {
        context.read<TimerBloc>().add(const Update());
      },
    );
  }

有人可以向我解释一下这种“特定”情况应该如何处理吗?一定有什么东西,一个我不完全理解的小概念,有时我能感觉到它让我放慢了速度。

预先非常感谢您的帮助。


首先,这是固执己见的。

尽管您已经描述了很多,但跟踪您的案例以及您(具体)如何实施它还是有点棘手。但我会尝试描述一些需要考虑的事情。

有几种方法可以处理这个问题。我会尽力回答你的问题。

  1. 始终或仅拥有无状态小部件和块并没有什么问题。
  2. 将 Stateful widget 和 Bloc 结合起来并没有什么问题。
  3. 考虑页面同时包含块和例如的情况。更新该页面上特定文本字段的计时器。为什么一个小部件应该同时处理这两种情况?听起来该页面可能是无状态的(使用块),其中包含文本字段,但该文本字段可以/应该是一个单独的 StatefulWidget,仅保存计时器或等效项。这意味着有时人们会在一个巨大的小部件中承担很多责任,而实际上它应该被分成几个较小的小部件。
  4. 我不明白为什么你会遇到这个错误,在有状态的小部件中同时拥有块和计时器是没有问题的,弹出并使用后退按钮并进行适当的处​​理和重置计时器。请参阅下面的完整代码示例。
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const FirstPage(),
    );
  }
}

class FirstPage extends StatelessWidget {
  const FirstPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('First page'),
      ),
      body: Center(
        child: ElevatedButton(
            onPressed: () => Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => BlocProvider<FetcherCubit>(
                    create: (context) => FetcherCubit(),
                    child: const SecondPage(),
                  ),
                )),
            child: const Text('Second page')),
      ),
    );
  }
}

class SecondPage extends StatefulWidget {
  const SecondPage({super.key});

  @override
  State<SecondPage> createState() => _SecondPageState();
}

class _SecondPageState extends State<SecondPage> {
  late final Timer myTimer;
  int value = 0;

  @override
  void initState() {
    super.initState();
    myTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
      setState(() {
        value++;
      });
    });
  }

  @override
  void dispose() {
    super.dispose();
    myTimer.cancel();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Second page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('Timer value: ${value.toString()}'),
            ElevatedButton(
              onPressed: () => context.read<FetcherCubit>().fetch(),
              child: const Text('Fetch!'),
            ),
            BlocBuilder<FetcherCubit, FetcherState>(
              builder: (context, state) {
                late final String text;
                if (state is FetcherInitial) {
                  text = 'Initial';
                } else if (state is FetcherLoading) {
                  text = 'Loading';
                } else {
                  text = 'Completed';
                }
                return Text(text);
              },
            )
          ],
        ),
      ),
    );
  }
}

class FetcherCubit extends Cubit<FetcherState> {
  FetcherCubit() : super(FetcherInitial());

  Future<void> fetch() async {
    emit(FetcherLoading());
    await Future.delayed(const Duration(seconds: 3));
    emit(FetcherCompleted());
  }
}

@immutable
abstract class FetcherState {}

class FetcherInitial extends FetcherState {}

class FetcherLoading extends FetcherState {}

class FetcherCompleted extends FetcherState {}

如果你构建它,结果是:

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

我几乎每次都将 Stateless Widget 与 BLoC 一起使用。我错了吗? 的相关文章

随机推荐

  • 词集中词的最大交集算法

    背后的故事 我正在使用创建语音控制应用程序x webkit speech这是令人惊讶的好 功能 而不是我的应用程序 但有时用户 我 会有点咕哝 如果单词的某些合理部分与某些合理命令的某些合理部分相匹配 那么接受该命令会很好 所以我寻找名为
  • Numpy 提取子矩阵

    我是新来的numpy我很难理解如何从np array具有定义的列和行的子矩阵 Y np arange 16 reshape 4 4 如果我想提取列 行 0 和 3 我应该 0 3 12 15 我尝试了所有的重塑功能 但不知道如何做到这一点
  • 在没有准备好的语句/SQLite/C++ 的情况下防止 SQL 注入

    我希望得到一些有关此方案针对 SQL 注入攻击的安全性的反馈 在前端 用户输入个人信息 姓名 地址 电话号码 电子邮件和一些自由格式文本 后端是用C 从头开始编码的 没有框架支持 并集成了SQLite C 代码是这样的not使用 SQLit
  • 简单易懂的 Spring 应用程序登录

    我对 Spring 很陌生 今天才开始学习 我一直在寻找一个简单的登录应用程序 它也将解释 Spring 之外的应用程序的流程 当我用谷歌搜索时 我真的找不到其中的几个 http viralpatel net blogs tutorial
  • 将行号添加到 SQL 查询的结果集中

    我有一个简单的选择语句 我想添加一个临时列来表示结果集中的行数 我尝试过这个 declare num int set num 0 select t A t B t C count 1 as number from tableZ as t 它
  • 如何使用 LocationCollection 缩放以适应 WP7 Bing Maps 控件?

    如何在 Windows Phone 7 上将 Microsoft Phone Controls Maps Map 控件缩放到正确的缩放级别 我有一个地理坐标的 LocationCollection 并且我自己计算了中心 但现在如何计算正确的
  • EntityFramework连接问题

    我在 Visual Studio 2008 中有一个包含 3 个项目的解决方案 1 个 Web 应用程序和 2 个类库 实体框架模型位于类库中 起始项目是 Web 应用程序 我曾经遇到过这样的问题 在配置中找不到指定的命名连接 不适合与 E
  • 为什么 use 块不能安全地初始化 var?

    为什么这会出现编译错误 val autoClosable MyAutoClosable var myVar MyType autoClosable use myVar it foo println myVar Error Variable
  • 从两个表中选择单独的行,按日期排序

    I don t想要任何一种JOIN这里 我正在使用 PHP 构建两个表的 RSS 提要 并且我想从两个表中选择所有行 保持行分开 但按公共排序created column 例如 如果我有一张桌子foo id downloads views
  • Windows 中的 cmake 问题

    我正在尝试编译这个 相当复杂 在 Windows 中使用 cmake 和 MinGW 的一段代码 include
  • 可能的堆栈损坏

    参考我之前的问题GDB 未精确定位 SIGSEGV 点 https stackoverflow com q 3971091 191776 我的线程代码如下 void runner void unused do sem wait x if c
  • 如何在 aspnet vnext config.json 文件中配置实体框架拦截器?

    在网络配置文件中 我会这样做
  • 你能解释一下 onCreate 和 Bundles 吗?

    我一直在查找它 但我似乎无法将自己包裹在 onCreate 和 Bundles 中 我知道 onCreate 在程序启动时被调用 但它是如何传递 Bundles 以及它们如何相关的 任何人都可以尝试用简单的英语来表达它 因为我似乎找不到它的
  • Django 结账无法访问:找不到页面(404)

    我正在尝试使用 Django 开发一个电子商务网站 所以我现在 用户可以将商品添加到购物车 但是当我尝试继续结帐时 由于某种原因 我的结帐表单没有显示 而是显示 找不到页面 404 我确保我已经注册了我的模型并运行了迁移 问题是什么 我的观
  • django 1.10 媒体图像不显示

    我通过将以下内容添加到站点 urls py 让 django 媒体图像在现有的 django 1 7 项目中工作 urlpatterns patters url r media P
  • 适用于 VS11 开发者预览版的 MySQL 提供程序

    是否有与 Visual Studio 11 开发人员预览版集成的 MySQL 连接提供程序 我已经尝试过 MySQL 连接器 v6 4 4 但我没有看到从新连接对话框连接到 MySQL 数据库的选项 我调整了 Net Connector 版
  • 如何执行存储在 MySQL 表列中的查询?

    mysql gt select from CT CID MID REPORT QUERY 1 1 select from emp 2 2 select from student 2 rows in set 0 00 sec 我想执行查询RE
  • 在 rgl 中填充轮廓

    以下代码绘制一些点并用线连接它们 我要填写 由选择颜色和 Alpha 的线条包围的区域 我似乎不知道如何使用 rgl 做到这一点 Open plot and add axes open3d decorate3d xlim c 0 4 yli
  • 如何使用 Qt 4 创建 OpenGL 3 上下文?

    我想学习使用 OpenGL 进行图形编程 因为我刚刚开始学习它 所以我决定学习新的 OpenGL3 的做事方式 据我所知 必须为此创建一个 OpenGL 3 上下文 如果我理解正确的话 新的 OpenGL 3 2 中的核心配置文件 嗯 我考
  • 我几乎每次都将 Stateless Widget 与 BLoC 一起使用。我错了吗?

    我很难理解如何使用 Flutter 处理某些特定情况下的状态 例如 假设我需要一个页面 单击按钮即可从 API 获取数据 这样的请求可能需要时间或者可能发生任何类型的问题 因此 我可能会使用 BLoC 模式在请求经历各种 状态 时正确通知用