错误:<TodoModel>在此构建器小组件上方找不到正确的提供程序



我正在开发一个小的待办事项列表应用程序,我是Provider包的新手。我得到这个错误:

错误:无法找到正确的提供程序构建器小部件。这是因为你使用了一个BuildContext不包括您选择的提供程序。

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"]);
},
),
),
));
});
});
}),
);
}
}

这里是todommodel。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提升到足以使其子HomePage类使用Provider.of<TodoModel>(content).xxx。为什么它仍然弹出没有找到正确的提供程序上面的生成器小部件的错误?

请使用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()的开头声明您的公共类,然后在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"))
],
);
}
}

最后,当提供公共类到小部件树时,有时您可能会遇到导航到其他屏幕的问题,并收到Provider not found错误。

Providers作用域因此,如果它位于小部件树内,则只有它的后代才能访问它。你应该把它移到MaterialApp:

上面
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => TodoModel(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
...

最新更新