dart BlocProvider.值是否冗余?

9lowa7mx  于 2023-02-27  发布在  其他
关注(0)|答案(1)|浏览(87)

我正在学习Flutter中用于状态管理和路由的Bloc Pattern。我正在参加一门在线课程。根据课程,我们必须将现有的BlocCubit提供给新创建的子树(在导航部分Navigator.push...中)。我阅读了文档,发现必须使用BlocProvider.value,因为它实际上是这样说的。BlocProvider Doucmentation
然而,我可以不使用BlocProvider.value而将Cubit提供给新创建的子树。我做错了什么?或者我应该说,我做对了什么却不知道:)?
main.dart路由

routes: {
          "/": (context) => const MyHomePage(title: 'Flutter Demo Home Page'),
          "/second": (context) => const SecondScreen(title: "Second screen"),
          "/third": (context) => const ThirdScreen(title: "Third screen")
        },

主屏幕

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: BlocConsumer<CounterCubit, CounterState>(
        listener: (context, state) {
          if (state.isIncremented) {
            ScaffoldMessenger.of(context)
                .showSnackBar(const SnackBar(content: Text("Incremented")));
          } else {
            ScaffoldMessenger.of(context)
                .showSnackBar(const SnackBar(content: Text("Decremented")));
          }
        },
        builder: (context, state) {
          return Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Text(
                  'You have pushed the button this many times:',
                ),
                BlocBuilder<CounterCubit, CounterState>(
                  builder: (context, state) {
                    return Text(
                      '${state.counterValue}',
                      style: Theme.of(context).textTheme.headlineMedium,
                    );
                  },
                ),
                Row(
                  children: [
                    FloatingActionButton(
                      heroTag: UniqueKey(),
                      onPressed: () {
                        BlocProvider.of<CounterCubit>(context).decrement();
                      },
                      tooltip: 'Increment',
                      child: const Icon(Icons.text_decrease),
                    ),
                    FloatingActionButton(
                      heroTag: UniqueKey(),
                      onPressed: () {
                        BlocProvider.of<CounterCubit>(context).increment();
                      },
                      tooltip: 'Increment',
                      child: const Icon(Icons.add),
                    ),
                  ],
                ),
                MaterialButton(
                  onPressed: () {
                    Navigator.pushNamed(context, '/second');
                  },
                  color: Colors.blue,
                  child: const Text("Navigate to Second Screen"),
                ),
                MaterialButton(
                  onPressed: () {
                    Navigator.pushNamed(context, '/third');
                  },
                  color: Colors.blue,
                  child: const Text("Navigate to Third Screen"),
                ),
              ],
            ),
          );
        },
      ),
    );
  }
}

第二个屏幕

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

class SecondScreen extends StatefulWidget {
  const SecondScreen({super.key, required this.title});

  final String title;

  @override
  State<SecondScreen> createState() => _SecondScreenState();
}

class _SecondScreenState extends State<SecondScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: BlocConsumer<CounterCubit, CounterState>(
        listener: (context, state) {
          if (state.isIncremented) {
            ScaffoldMessenger.of(context)
                .showSnackBar(const SnackBar(content: Text("Incremented")));
          } else {
            ScaffoldMessenger.of(context)
                .showSnackBar(const SnackBar(content: Text("Decremented")));
          }
        },
        builder: (context, state) {
          return Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Text(
                  'You have pushed the button this many times:',
                ),
                BlocBuilder<CounterCubit, CounterState>(
                  builder: (context, state) {
                    return Text(
                      '${state.counterValue}',
                      style: Theme.of(context).textTheme.headlineMedium,
                    );
                  },
                ),
                Row(
                  children: [
                    FloatingActionButton(
                      heroTag: UniqueKey(),
                      onPressed: () {
                        BlocProvider.of<CounterCubit>(context).decrement();
                      },
                      tooltip: 'Increment',
                      child: const Icon(Icons.text_decrease),
                    ),
                    FloatingActionButton(
                      heroTag: UniqueKey(),
                      onPressed: () {
                        BlocProvider.of<CounterCubit>(context).increment();
                      },
                      tooltip: 'Increment',
                      child: const Icon(Icons.add),
                    ),
                  ],
                )
              ],
            ),
          );
        },
      ),
    );
  }
}

第三个屏幕

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

class ThirdScreen extends StatefulWidget {
  const ThirdScreen({super.key, required this.title});

  final String title;

  @override
  State<ThirdScreen> createState() => _ThirdScreenState();
}

class _ThirdScreenState extends State<ThirdScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: BlocConsumer<CounterCubit, CounterState>(
        listener: (context, state) {
          if (state.isIncremented) {
            ScaffoldMessenger.of(context)
                .showSnackBar(const SnackBar(content: Text("Incremented")));
          } else {
            ScaffoldMessenger.of(context)
                .showSnackBar(const SnackBar(content: Text("Decremented")));
          }
        },
        builder: (context, state) {
          return Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Text(
                  'You have pushed the button this many times:',
                ),
                BlocBuilder<CounterCubit, CounterState>(
                  builder: (context, state) {
                    return Text(
                      '${state.counterValue}',
                      style: Theme.of(context).textTheme.headlineMedium,
                    );
                  },
                ),
                Row(
                  children: [
                    FloatingActionButton(
                      heroTag: UniqueKey(),
                      onPressed: () {
                        BlocProvider.of<CounterCubit>(context).decrement();
                      },
                      tooltip: 'Increment',
                      child: const Icon(Icons.text_decrease),
                    ),
                    FloatingActionButton(
                      heroTag: UniqueKey(),
                      onPressed: () {
                        BlocProvider.of<CounterCubit>(context).increment();
                      },
                      tooltip: 'Increment',
                      child: const Icon(Icons.add),
                    ),
                  ],
                )
              ],
            ),
          );
        },
      ),
    );
  }
}
qco9c6ql

qco9c6ql1#

我会说,您需要了解更多关于InheritedWidgetNavigator子树、BuildContext在Flutter中的工作方式以及这与您的主题有何关联。
在Flutter中,MaterialApp是应用中最高级别的组件,对吗?
因此,通过使用BlocProvider Package 它,您就提供了具有该块/肘的整个应用程序,因此,我将使用箭头结构进行解释,以使您了解发生了什么。

  • 我将把“BlocProvider”称为BlocA示例的BlocProvider
  • 我将使用SubTreeOne引用导航器第一个屏幕子树。
  • 我将使用SubTreeTwo引用导航器第二个屏幕子树。

当你用一个BlocProvider Package MaterialApp时:

-> SubTreOne
                                         |
BlocProvider -> MaterialApp(Navigator) ->   
                                         |
                                          -> SubTreeTwo

在此示例中,当您尝试从任何屏幕访问BlocA时(子树),你不必用BlocProvider.value传递它,因为BlocA总是可以通过你的widget-tree存在的上下文来查找和使用,所以通过调用Navigator.push(),一个新的子树将被作为Navigator的子树放置,并且当尝试从该屏幕/路由使用BlocA时,它将使用BuildContext从它存在的地方(在该情况下,Navigator的上下文)查找它,这意味着从那里查找,它将找到BlocA,因为它在使用BlocProvider的应用的最顶层。
使用此方法,您可以保证BlocA仅在应用程序中初始化一次,在整个应用程序中可用,并且不会在所有应用程序周期中关闭,直到您关闭应用程序或使用close()方法手动关闭块。因此,您可能需要此方法来处理应用程序中用户的身份验证状态,但不适用于在单个屏幕或小部件中使用区块(至少,如果你真的关心应用程序的性能和运行时内存)。
现在,假设您有一个屏幕,其中显示了一个简单的计数器,您希望每次在小部件树中使用和推迟该屏幕时重新初始化该计数器,然后您希望能够选择将该计数器值传递到另一个屏幕,这里您将拥有如下结构:

-> BlocProvider -> SubTreeOne
                         |
MaterialApp(Navigator) ->   
                         |
                          -> SubTreeTwo

现在在这个例子中,BlocASubTreeOne中是可访问的,并且工作得非常好,并且每次您Navigator.pop(context)Navigator.push(...SubTreeOne())时,块将再次被重新初始化,正如我们所期望的,对吗?但是当我们现在尝试导航到SubTreeTwo()时,您将注意到BlocA在这个点不存在于子树中,您的应用中将抛出最常见的Bloc异常:

BlocProvider.of<BlocA>() called with a context that does not contain a Bloc of type BlocA.

No ancestor could be found starting from the context that was passed to BlocProvider.of<BlocA>().
//...

这是因为,正如我之前所说,推送新路由时,将打开一个新的子树作为Navigator小工具的子项,它将从context查找该BlocA,但无法找到,因为在它上面只有一个MaterialApp,而没有BlocA
在这里,您将了解BlocProvider的值,以及它如何帮助您解决此类问题,方法是在导航到SubTreeTwo时将BlocA传递给它,手动将该块传递给该子树并使其在那里可用,这样就解决了问题。

相关问题