我有一个包含按钮的页面。当用户按下按钮时,会出现一个对话框,其中包含一堆文本字段等。该对话框中有一个切换按钮,可以根据用户切换的内容更改
GridView
。
设置为: 在我的
main
课上,我有:
void main() {
Bloc.observer = const GlobalApplicationObserver();
ExpenditureRepository expenditureRepository = ExpenditureRepository();
//runApp(MyApp());
runApp(MaterialApp(
home: MultiBlocProvider(
providers: [
// BlocProvider<OnboardingBloc>(
// create: (BuildContext context) => OnboardingBloc()),
BlocProvider<FinanceBloc>(
create: (BuildContext context) => FinanceBloc(expenditureRepository)),
],
child: OnboardingPageWidget(),
)));
}
在我的
OnboardingPageWidget
(一个stateful
小部件,我有,我有一个按钮):
Center(
child: IconButton(
style: TextButton.styleFrom(
backgroundColor: Colors.black,
foregroundColor: Colors.white),
onPressed: () => showDialog<String>(
context: context,
builder: (BuildContext _) => expenditureForm
.showAddExpenditureDialogBox(context, true),
),
tooltip: 'Click to add an expense!',
icon: const Icon(Icons.add),
))
在我的
showAddExpenditureDialogBox
方法中,它包括:
class ExpenditureForm {
AlertDialog showAddExpenditureDialogBox(
BuildContext context, isFixedExpenditure) {
final amountTextEditingController = TextEditingController();
final IncomeOrExpenseToggle incomeExpense = IncomeOrExpenseToggle(context);
final List<String> list = <String>['Travel', 'Food', 'Bill'];
final nameOfExpenditureTextEditingController = TextEditingController();
String categoryOfExpenditure = "Travel";
return AlertDialog(
title: const Text('Expenditure'),
content: const Text('Input details about it!'),
actions: <Widget>[
Column(
children: [
Container(child: Center(child: incomeExpense)),
Row(
children: [
Expanded(
flex: 3,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: TextField(
controller: nameOfExpenditureTextEditingController,
decoration: InputDecoration(hintText: 'Name of expense'),
),
),
),
Expanded(
flex: 1,
child: Container(
width: MediaQuery.of(context).size.width * 0.3,
child: TextField(
controller: amountTextEditingController,
decoration: InputDecoration(hintText: 'Amount - £'),
),
),
),
],
),
//This is practising drop down menus
DropdownMenu<String>(
initialSelection: list.first,
onSelected: (value) => {categoryOfExpenditure = value!},
dropdownMenuEntries:
list.map<DropdownMenuEntry<String>>((String value) {
return DropdownMenuEntry<String>(value: value, label: value);
}).toList(),
),
// SizedBox(
// child: BlocBuilder<FinanceBloc, FinanceState>(
// buildWhen: (previous, current) => current is TypeToggledState,
// builder: (context, state) {
// if (state is TypeToggledState) {
// if (state.income) {
// return SizedBox(
// width: MediaQuery.of(context).size.width * 1,
// height: MediaQuery.of(context).size.height * 0.5,
// child: GridView.count(
// crossAxisCount: 2,
// children: List.generate(100, (index) {
// return Center(
// child: Text(
// 'Item $index',
// style:
// Theme.of(context).textTheme.headlineSmall,
// ),
// );
// }),
// ),
// );
// } else {
// return SizedBox(
// width: MediaQuery.of(context).size.width * 1,
// height: MediaQuery.of(context).size.height * 0.5,
// child: GridView.count(
// crossAxisCount: 2,
// children: List.generate(23, (index) {
// return Center(
// child: Text(
// 'Item $index',
// style:
// Theme.of(context).textTheme.headlineSmall,
// ),
// );
// }),
// ),
// );
// }
// }
// return SizedBox(
// width: MediaQuery.of(context).size.width * 1,
// height: MediaQuery.of(context).size.height * 0.5,
// child: GridView.count(
// crossAxisCount: 2,
// children: List.generate(100, (index) {
// return Center(
// child: Text(
// 'Item $index',
// style: Theme.of(context).textTheme.headlineSmall,
// ),
// );
// }),
// ),
// );
// }),
// ),
TextButton(
onPressed: () => Navigator.of(context, rootNavigator: true).pop(),
child: const Text('Cancel'),
),
TextButton(
onPressed: () => saveExpenditure(
context,
amountTextEditingController.text,
categoryOfExpenditure,
nameOfExpenditureTextEditingController.text,
incomeExpense.getToogle(),
isFixedExpenditure),
child: const Text('Save'),
)
],
)
],
);
}
void saveExpenditure(
BuildContext context,
String amountOfExpenditure,
String categoryOfExpenditure,
String nameOfExpenditure,
bool isIncome,
bool isFixed) {
Expenditure expenditure = Expenditure(
nameOfExpenditure: nameOfExpenditure,
amountOfExpenditure: amountOfExpenditure,
categoryOfExpenditure: categoryOfExpenditure,
typeOfExpenditure: isIncome == true ? "Income" : "Expense",
isFixedExpenditure: isFixed);
context.read<FinanceBloc>().add(SaveFinanceEvent(expenditure));
//Closing the popup
Navigator.of(context, rootNavigator: true).pop();
}
}
现在,当我取消注释
showAddExpenditureDialogBox
方法中的代码块时,我得到以下结果:
Error: Could not find the correct Provider<FinanceBloc> above this BlocBuilder<FinanceBloc,
FinanceState> Widget
This happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:
- You added a new provider in your `main.dart` and performed a hot-reload.
To fix, perform a hot-restart.
- The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then
other routes will not be able to access that provider.
- You used a `BuildContext` that is an ancestor of the provider you are trying to read.
Make sure that BlocBuilder<FinanceBloc, FinanceState> is under your
MultiProvider/Provider<FinanceBloc>.
我不明白错误是如何或为何发生的。据我所知,块提供程序是在应用程序的入口点创建的,被传递到
OnboardingPageWidget
中,然后通过构造函数传递到 ExpenditureForm
类中。那么我肯定应该能够使用 context
形式的构造函数来使用 BlocBuilder
吗?我认为这可能源于我的 build
类中没有重写 ExpenditureForm
方法的事实?我创建显式类而不扩展有状态/无状态小部件的原因是因为此警报对话框正在我的应用程序中的其他位置使用。
您应该在
BLoc
小部件上方提供 MaterialApp
:
这是因为
BLoC
仅在 home
屏幕上方提供,每当您导航到另一个屏幕时,主屏幕就会从 MaterialApp 中删除,而另一个屏幕取代它的位置,因此提供程序会随 home 一起删除。
因此,将 providers 置于 Material 应用程序之上:
runApp(
MultiBlocProvider(
providers: [
BlocProvider<OnboardingBloc>(
create: (BuildContext context) => OnboardingBloc()),
BlocProvider<FinanceBloc>(
create: (BuildContext context) => FinanceBloc(expenditureRepository)),
],
child: MaterialApp(
home: OnboardingPageWidget(),
),
),
);