我正在学习Flutter中用于状态管理和路由的Bloc Pattern。我正在参加一门在线课程。根据课程,我们必须将现有的Bloc
或Cubit
提供给新创建的子树(在导航部分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),
),
],
)
],
),
);
},
),
);
}
}
1条答案
按热度按时间qco9c6ql1#
我会说,您需要了解更多关于
InheritedWidget
、Navigator
子树、BuildContext
在Flutter中的工作方式以及这与您的主题有何关联。在Flutter中,
MaterialApp
是应用中最高级别的组件,对吗?因此,通过使用
BlocProvider
Package 它,您就提供了具有该块/肘的整个应用程序,因此,我将使用箭头结构进行解释,以使您了解发生了什么。BlocA
示例的BlocProvider
。SubTreeOne
引用导航器第一个屏幕子树。SubTreeTwo
引用导航器第二个屏幕子树。当你用一个
BlocProvider
PackageMaterialApp
时:在此示例中,当您尝试从任何屏幕访问
BlocA
时(子树),你不必用BlocProvider.value
传递它,因为BlocA
总是可以通过你的widget-tree存在的上下文来查找和使用,所以通过调用Navigator.push()
,一个新的子树将被作为Navigator
的子树放置,并且当尝试从该屏幕/路由使用BlocA
时,它将使用BuildContext
从它存在的地方(在该情况下,Navigator
的上下文)查找它,这意味着从那里查找,它将找到BlocA
,因为它在使用BlocProvider
的应用的最顶层。使用此方法,您可以保证
BlocA
仅在应用程序中初始化一次,在整个应用程序中可用,并且不会在所有应用程序周期中关闭,直到您关闭应用程序或使用close()
方法手动关闭块。因此,您可能需要此方法来处理应用程序中用户的身份验证状态,但不适用于在单个屏幕或小部件中使用区块(至少,如果你真的关心应用程序的性能和运行时内存)。现在,假设您有一个屏幕,其中显示了一个简单的计数器,您希望每次在小部件树中使用和推迟该屏幕时重新初始化该计数器,然后您希望能够选择将该计数器值传递到另一个屏幕,这里您将拥有如下结构:
现在在这个例子中,
BlocA
在SubTreeOne
中是可访问的,并且工作得非常好,并且每次您Navigator.pop(context)
和Navigator.push(...SubTreeOne())
时,块将再次被重新初始化,正如我们所期望的,对吗?但是当我们现在尝试导航到SubTreeTwo()
时,您将注意到BlocA
在这个点不存在于子树中,您的应用中将抛出最常见的Bloc
异常:这是因为,正如我之前所说,推送新路由时,将打开一个新的子树作为
Navigator
小工具的子项,它将从context
查找该BlocA
,但无法找到,因为在它上面只有一个MaterialApp
,而没有BlocA
。在这里,您将了解
BlocProvider
的值,以及它如何帮助您解决此类问题,方法是在导航到SubTreeTwo
时将BlocA
传递给它,手动将该块传递给该子树并使其在那里可用,这样就解决了问题。