Error: Could not find the correct Provider<TodoModel> above this Builder Widget
Error: Could not find the correct Provider<TodoModel> above this Builder Widget
我正在开发一个微型 to-do 列表应用程序,我是 Provider
包的新手。我收到此错误:
Error: Could not find the correct Provider above this
Builder Widget. This happens because you used a 'BuildContext' that
does not include the provider of your choice.
When the exception was thrown, this was the stack:
#0 Provider._inheritedElementOf (package:provider/src/provider.dart:332:7)
#1 Provider.of (package:provider/src/provider.dart:284:30)
#2 HomePage.build.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:mytodolist/main.dart:57:36)
#3 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:994:20)
#4 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:182:24)
...
Handler: "onTap"
Recognizer: TapGestureRecognizer#9540a
debugOwner: GestureDetector
state: possible
won arena
finalPosition: Offset(308.6, 425.5)
finalLocalPosition: Offset(34.8, 20.3)
button: 1
sent tap down
这是我的 main.dart
:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:mytodolist/TodoModel.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
brightness: Brightness.light,
primaryColor: Colors.blue,
accentColor: Colors.orange,
),
home: ChangeNotifierProvider(
create: (context) => TodoModel(),
child: HomePage(),
)
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("mytodos"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)),
title: Text("Add Todolist"),
content: TextField(
onChanged: (String value) {
Provider.of<TodoModel>(context, listen: false)
.addTitle(value);
},
),
actions: <Widget>[
TextButton(
onPressed: () {
Provider.of<TodoModel>(context, listen: false)
.createTodos();
Navigator.of(context).pop();
},
child: Text("Add"))
],
);
});
},
child: Icon(
Icons.add,
color: Colors.white,
),
),
body: StreamBuilder(
stream: FirebaseFirestore.instance.collection("MyTodos").snapshots(),
builder: (context, snapshots) {
if (snapshots.data == null) return CircularProgressIndicator();
return Consumer<TodoModel>(builder: (context, todo, child) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshots.data.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot documentSnapshot =
snapshots.data.docs[index];
return Dismissible(
onDismissed: (direction) {
Provider.of<TodoModel>(context, listen: false)
.deleteTodos(documentSnapshot["todoTitle"]);
},
key: Key(documentSnapshot["todoTitle"]),
child: Card(
elevation: 4,
margin: EdgeInsets.all(8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)),
child: ListTile(
title: Text(documentSnapshot["todoTitle"]),
trailing: IconButton(
icon: Icon(Icons.delete),
color: Colors.red,
onPressed: () {
/*setState(() {
todos.removeAt(index);
});*/
Provider.of<TodoModel>(context, listen: false)
.deleteTodos(documentSnapshot["todoTitle"]);
},
),
),
));
});
});
}),
);
}
}
这是我的 TodoModel.dart:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
class TodoModel extends ChangeNotifier {
// List todos = [];
String todoTitle = "";
createTodos() {
DocumentReference documentReference =
FirebaseFirestore.instance.collection("MyTodos").doc(todoTitle);
// Map
Map<String, String> todos = {"todoTitle": todoTitle};
documentReference.set(todos).whenComplete(() =>
print("$todoTitle created"));
}
deleteTodos(item) {
DocumentReference documentReference =
FirebaseFirestore.instance.collection("MyTodos").doc(item);
documentReference.delete().whenComplete(() => print("$item deleted"));
}
addTitle(String title) {
todoTitle = title;
}
}
在 main.dart
中,我相信我已经将 ChangeNotifierProvider
提升到足以使其 child、HomePage
class,使用 [=18] =].为什么在Builder Widget上面还提示找不到正确的Provider?
请使用 HomePage
的上下文而不是 showDialog
的构建器。
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) { // <<<<<<< USE THIS CONTEXT
return Scaffold(
appBar: AppBar(
title: Text("mytodos"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext dialogContext) { // <<<<<<< DO NOT USE THIS CONTEXT
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)),
title: Text("Add Todolist"),
content: TextField(
onChanged: (String value) {
// <<<<<<< context = HomePage's BuildContext
// <<<<<<< context != dialogContext
Provider.of<TodoModel>(context, listen: false)
.addTitle(value);
},
),
actions: <Widget>[
TextButton(
onPressed: () {
// <<<<<<< context = HomePage's BuildContext
// <<<<<<< context != dialogContext
Provider.of<TodoModel>(context, listen: false)
.createTodos();
Navigator.of(context).pop();
},
child: Text("Add"))
],
);
});
},
当您使用 showDialog()
方法时,内部 BuildContext
和外部 BuildContext
可能会纠缠在一起,这意味着您的 UI 可能会出现不当行为。为了解决问题,您应该在 build()
的开头声明您的公共 class,然后在 AlertDialog
中重用它(这也是重用代码的最佳实践):
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final todoModel = Provider.of<TodoModel>(context, listen: false);
return Scaffold(
appBar: AppBar(
title: Text("mytodos"),
),
...
// Then use it in the dialog
showDialog(
context: context,
...
content: TextField(
onChanged: (String value) {
todoModel.addTitle(value); // Use it here
},
),
您还可以通过其他方式解决此问题。您可以创建一个单独的小部件来容纳对话框的内容,尤其是当 UI 与 AlertDialog
不相似时,这迫使我们创建一个自定义 UI 小部件:
// This is useful when you cannot use AlertDialog's UI, but I'll put it here for the example
class CustomDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
title: Text("Add Todolist"),
content: TextField(
onChanged: (String value) {
Provider.of<TodoModel>(context, listen: false).addTitle(value);
},
),
actions: <Widget>[
TextButton(
onPressed: () {
Provider.of<TodoModel>(context, listen: false).createTodos();
Navigator.of(context).pop();
},
child: Text("Add"))
],
);
}
}
最后,在向小部件树提供通用 classes 时,有时您可能 运行 遇到导航到其他屏幕的问题并收到 Provider not found
错误。
Providers
是 scoped 因此如果它位于小部件树内,则只有它的后代才能访问它。你应该把它移到上面 MaterialApp
:
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => TodoModel(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
...
我正在开发一个微型 to-do 列表应用程序,我是 Provider
包的新手。我收到此错误:
Error: Could not find the correct Provider above this Builder Widget. This happens because you used a 'BuildContext' that does not include the provider of your choice.
When the exception was thrown, this was the stack:
#0 Provider._inheritedElementOf (package:provider/src/provider.dart:332:7)
#1 Provider.of (package:provider/src/provider.dart:284:30)
#2 HomePage.build.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:mytodolist/main.dart:57:36)
#3 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:994:20)
#4 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:182:24)
...
Handler: "onTap"
Recognizer: TapGestureRecognizer#9540a
debugOwner: GestureDetector
state: possible
won arena
finalPosition: Offset(308.6, 425.5)
finalLocalPosition: Offset(34.8, 20.3)
button: 1
sent tap down
这是我的 main.dart
:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:mytodolist/TodoModel.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
brightness: Brightness.light,
primaryColor: Colors.blue,
accentColor: Colors.orange,
),
home: ChangeNotifierProvider(
create: (context) => TodoModel(),
child: HomePage(),
)
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("mytodos"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)),
title: Text("Add Todolist"),
content: TextField(
onChanged: (String value) {
Provider.of<TodoModel>(context, listen: false)
.addTitle(value);
},
),
actions: <Widget>[
TextButton(
onPressed: () {
Provider.of<TodoModel>(context, listen: false)
.createTodos();
Navigator.of(context).pop();
},
child: Text("Add"))
],
);
});
},
child: Icon(
Icons.add,
color: Colors.white,
),
),
body: StreamBuilder(
stream: FirebaseFirestore.instance.collection("MyTodos").snapshots(),
builder: (context, snapshots) {
if (snapshots.data == null) return CircularProgressIndicator();
return Consumer<TodoModel>(builder: (context, todo, child) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshots.data.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot documentSnapshot =
snapshots.data.docs[index];
return Dismissible(
onDismissed: (direction) {
Provider.of<TodoModel>(context, listen: false)
.deleteTodos(documentSnapshot["todoTitle"]);
},
key: Key(documentSnapshot["todoTitle"]),
child: Card(
elevation: 4,
margin: EdgeInsets.all(8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)),
child: ListTile(
title: Text(documentSnapshot["todoTitle"]),
trailing: IconButton(
icon: Icon(Icons.delete),
color: Colors.red,
onPressed: () {
/*setState(() {
todos.removeAt(index);
});*/
Provider.of<TodoModel>(context, listen: false)
.deleteTodos(documentSnapshot["todoTitle"]);
},
),
),
));
});
});
}),
);
}
}
这是我的 TodoModel.dart:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
class TodoModel extends ChangeNotifier {
// List todos = [];
String todoTitle = "";
createTodos() {
DocumentReference documentReference =
FirebaseFirestore.instance.collection("MyTodos").doc(todoTitle);
// Map
Map<String, String> todos = {"todoTitle": todoTitle};
documentReference.set(todos).whenComplete(() =>
print("$todoTitle created"));
}
deleteTodos(item) {
DocumentReference documentReference =
FirebaseFirestore.instance.collection("MyTodos").doc(item);
documentReference.delete().whenComplete(() => print("$item deleted"));
}
addTitle(String title) {
todoTitle = title;
}
}
在 main.dart
中,我相信我已经将 ChangeNotifierProvider
提升到足以使其 child、HomePage
class,使用 [=18] =].为什么在Builder Widget上面还提示找不到正确的Provider?
请使用 HomePage
的上下文而不是 showDialog
的构建器。
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) { // <<<<<<< USE THIS CONTEXT
return Scaffold(
appBar: AppBar(
title: Text("mytodos"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext dialogContext) { // <<<<<<< DO NOT USE THIS CONTEXT
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)),
title: Text("Add Todolist"),
content: TextField(
onChanged: (String value) {
// <<<<<<< context = HomePage's BuildContext
// <<<<<<< context != dialogContext
Provider.of<TodoModel>(context, listen: false)
.addTitle(value);
},
),
actions: <Widget>[
TextButton(
onPressed: () {
// <<<<<<< context = HomePage's BuildContext
// <<<<<<< context != dialogContext
Provider.of<TodoModel>(context, listen: false)
.createTodos();
Navigator.of(context).pop();
},
child: Text("Add"))
],
);
});
},
当您使用 showDialog()
方法时,内部 BuildContext
和外部 BuildContext
可能会纠缠在一起,这意味着您的 UI 可能会出现不当行为。为了解决问题,您应该在 build()
的开头声明您的公共 class,然后在 AlertDialog
中重用它(这也是重用代码的最佳实践):
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final todoModel = Provider.of<TodoModel>(context, listen: false);
return Scaffold(
appBar: AppBar(
title: Text("mytodos"),
),
...
// Then use it in the dialog
showDialog(
context: context,
...
content: TextField(
onChanged: (String value) {
todoModel.addTitle(value); // Use it here
},
),
您还可以通过其他方式解决此问题。您可以创建一个单独的小部件来容纳对话框的内容,尤其是当 UI 与 AlertDialog
不相似时,这迫使我们创建一个自定义 UI 小部件:
// This is useful when you cannot use AlertDialog's UI, but I'll put it here for the example
class CustomDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
title: Text("Add Todolist"),
content: TextField(
onChanged: (String value) {
Provider.of<TodoModel>(context, listen: false).addTitle(value);
},
),
actions: <Widget>[
TextButton(
onPressed: () {
Provider.of<TodoModel>(context, listen: false).createTodos();
Navigator.of(context).pop();
},
child: Text("Add"))
],
);
}
}
最后,在向小部件树提供通用 classes 时,有时您可能 运行 遇到导航到其他屏幕的问题并收到 Provider not found
错误。
Providers
是 scoped 因此如果它位于小部件树内,则只有它的后代才能访问它。你应该把它移到上面 MaterialApp
:
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => TodoModel(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
...