在此小部件上方找不到正确的提供者
Could not find the correct provider above this widget
我在使用 Flutter Provider 时遇到问题...
我的流程是这样的:将登录用户 ID 传递给新的小部件后 -> 从那里预制保存到数据库,然后重定向到新的小部件(仪表板)。
这是登录后的小部件代码:
return MaterialApp(
title: title,
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: ListView(
children: <Widget>[
Container(
margin: EdgeInsets.all(8.0),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8.0))),
child: InkWell(
onTap: () {
var user = Provider.of<UserRepository>(context);
user.savePreference(user.user.id, "Something");
user.navigateToNewPage(Dashboard(), context);
print(user.user.id);
},
这个有效:
user.savePreference(user.user.id, "Something");
但这引起了一个问题:
user.navigateToNewPage(Dashboard(), context);
在我创建的仪表板小部件中:
Widget build(BuildContext context) {
var user = Provider.of<UserRepository>(context);
在 UserRepository 中我有这个:
class UserRepository with ChangeNotifier {
User user;
Status _status = Status.Uninitialized;
Status get status => _status;
User get getUser => user;
UserRepository.instance();
Future<void> navigateToNewPage(Widget page, BuildContext context) {
Navigator.push(context, MaterialPageRoute(builder: (context) => page));
}
我知道这个话题已经解决了问题,但是找不到适合我问题的内容。
尝试将位于脚手架下方的部件树的一部分提取到单独的部件中。
您现在使用的上下文用于构建您的顶级小部件,它还没有 Navigator。
生成的代码应如下所示:
return MaterialApp(
title: title,
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: LoginWidget()
class LoginWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
Container(
margin: EdgeInsets.all(8.0),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8.0))),
child: InkWell(
onTap: () {
var user = Provider.of<UserRepository>(context);
user.savePreference(user.user.id, "Something");
user.navigateToNewPage(Dashboard(), context);
print(user.user.id);
},
...
}
}
Provider.of(上下文)的参数 context
必须是您定义的提供程序的子项。
@override
Widget build(BuildContext context) {
// !important here, Scaffold.of(context) returns null
return Scaffold(
appBar: AppBar(title: Text('Demo')),
body: Builder(
builder: (BuildContext context) {
return FlatButton(
child: Text('BUTTON'),
onPressed: () {
// here, Scaffold.of(context) returns the locally created Scaffold
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('Hello.')
));
}
);
}
)
);
}
官方样本:https://api.flutter.dev/flutter/widgets/BuildContext-class.html
这是我的代码:
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [ChangeNotifierProvider<SomeModel>(
create: (context){
return SomeModel();
},
),],
child: Builder(builder: (BuildContext context){
BuildContext rootContext = context;
return Container(
//Here to use rootContext is safe
//Provider.of<SomeModel>(rootContext, listen: false);
);
}),
);
}
提供商范围
MaterialApp
> provider(Screen A)
> Screen B
如果 Provider
在屏幕 A 中实例化,则在 A → B 的 Navigator.push
之后将无法在屏幕 B 中访问。
为什么?
因为 Provider
是一个 InheritedWidget
而 Navigator
在其 Screen A context
范围之外使用 MaterialApp context
。 (请参阅下面的详细信息。)
修复
将 Provider
移动到 common-parent、MaterialApp context
,允许屏幕 A 和 B 继承其 state/context。
provider(MaterialApp)
> Screen A
> Screen B
例子
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
/// wrap MaterialApp in Provider widget
return ChangeNotifierProvider(
create: (context) => ColorModel(), // ← create/init your state model
child: MaterialApp(
home: ScreenA()
),
);
}
}
详情
提供商
Provider
基于 InheritedWidget
。只有 child 小部件可以 继承 parent 小部件的状态。
Provider
需要成为任何想要访问您的“已提供”状态的小部件树的 根小部件 object。
导航器
屏幕 A 上的 Navigator.push(context)
不使用屏幕 A 中的 context
。
- 它使用
MaterialApp
中的 context
。
Navigator.push(context)
实际上是 Navigator.of(context).push
Navigator.of(context)
表示:向上搜索此上下文层次结构,直到找到实例化 Navigator 的上下文
- 默认
Navigator
在 MaterialApp
中实例化。
- 除非您明确 create/use 一个不同的
Navigator
,否则您使用的是默认值。
Navigator
的context
是MaterialApp
的
- 屏幕 B 将获得 that 上下文(属于
MaterialApp
),不是 屏幕 A 的上下文。
- B 是 A 的 兄弟 ,而不是它的 child。
- B 不继承自 A,即使它在
context
A 中出现“实例化”。
- 屏幕 B 是
MaterialApp
context
的 child,而不是屏幕 A context
。
Provider
context
范围,如果在屏幕 A 中定义,则不涵盖屏幕 B
画面A→B
Navigator.push(context, MaterialPageRoute(builder: (context) => ScreenB()))
实际上是:
Navigator.of(context).push(MaterialPageRoute(builder: (context) => ScreenB()))
类似于:
Navigator.of(MaterialApp).push(
MaterialPageRoute(builder: (MaterialAppContext) => ScreenB())
)
因此屏幕 B 在 MaterialApp context
下, 而不是 在屏幕 A context
下,因此无法访问屏幕 A Provider
及其 context
.
有关 Provider
的类似问题请参阅 。
可能会在 DetailScreen
中回答。
我不知道你是否解决了这个问题 problem.but 我只是使用参数解决了。
首先我的问题是,我有一个 list screen
、一个 detail screen
和一个 listBloc
。我在 list screen
中提供了 listBloc
。像这样:
class ListScreen extends StatelessWidget {
static const String routeName = '/list_screen';
ListScreen({Key key}) : super(key: key);
static Route route(Map<String, dynamic> args) {
return MaterialPageRoute(
builder: (_) => ListScreen(),
settings: const RouteSettings(name: routeName),
);
}
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) =>ListBloc(),
)
]
child: DetailScreen(),
);
}
}
而且,我需要在 detail screen
的 BlocBuilder
中使用 listBloc
。
class DetailScreen extends StatelessWidget {
// you need a context
final BuildContext context;
// get context
const DetailScreen({
Key key,
@required this.context,
}) : super(key: key);
@override
Widget build(BuildContext context) {
// get bloc using your context
final bloc = this.context.read<ListBloc>();
return BlocBuilder<ListBloc, ListState>(
// provide your bloc
bloc: bloc,
builder: (context, state) {}
);
}
}
这就是我们使用多个提供商的方式
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => GeneralProvider(),
),
],
child: MaterialApp(
home: InitPage(),
onGenerateRoute: MyRouter.generateRoute,
initialRoute: '/InitPage',
debugShowCheckedModeBanner: false,
title: 'Eray',
),
);
}
}
我从错误的路径导入了我的 Provider:
import 'file:///D:/Projects/Flutter/library_app/lib/MyStateManagement/local/temp_management.dart';
当我更改路径的导入时,它对我有用:
import 'package:library_app/MyStateManagement/local/temp_management.dart';
当我更改 Provider 文件的文件夹时,我遇到了这个问题。
我在使用 Flutter Provider 时遇到问题... 我的流程是这样的:将登录用户 ID 传递给新的小部件后 -> 从那里预制保存到数据库,然后重定向到新的小部件(仪表板)。
这是登录后的小部件代码:
return MaterialApp(
title: title,
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: ListView(
children: <Widget>[
Container(
margin: EdgeInsets.all(8.0),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8.0))),
child: InkWell(
onTap: () {
var user = Provider.of<UserRepository>(context);
user.savePreference(user.user.id, "Something");
user.navigateToNewPage(Dashboard(), context);
print(user.user.id);
},
这个有效:
user.savePreference(user.user.id, "Something");
但这引起了一个问题:
user.navigateToNewPage(Dashboard(), context);
在我创建的仪表板小部件中:
Widget build(BuildContext context) {
var user = Provider.of<UserRepository>(context);
在 UserRepository 中我有这个:
class UserRepository with ChangeNotifier {
User user;
Status _status = Status.Uninitialized;
Status get status => _status;
User get getUser => user;
UserRepository.instance();
Future<void> navigateToNewPage(Widget page, BuildContext context) {
Navigator.push(context, MaterialPageRoute(builder: (context) => page));
}
我知道这个话题已经解决了问题,但是找不到适合我问题的内容。
尝试将位于脚手架下方的部件树的一部分提取到单独的部件中。 您现在使用的上下文用于构建您的顶级小部件,它还没有 Navigator。
生成的代码应如下所示:
return MaterialApp(
title: title,
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: LoginWidget()
class LoginWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
Container(
margin: EdgeInsets.all(8.0),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8.0))),
child: InkWell(
onTap: () {
var user = Provider.of<UserRepository>(context);
user.savePreference(user.user.id, "Something");
user.navigateToNewPage(Dashboard(), context);
print(user.user.id);
},
...
}
}
Provider.of(上下文)的参数 context
必须是您定义的提供程序的子项。
@override
Widget build(BuildContext context) {
// !important here, Scaffold.of(context) returns null
return Scaffold(
appBar: AppBar(title: Text('Demo')),
body: Builder(
builder: (BuildContext context) {
return FlatButton(
child: Text('BUTTON'),
onPressed: () {
// here, Scaffold.of(context) returns the locally created Scaffold
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('Hello.')
));
}
);
}
)
);
}
官方样本:https://api.flutter.dev/flutter/widgets/BuildContext-class.html
这是我的代码:
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [ChangeNotifierProvider<SomeModel>(
create: (context){
return SomeModel();
},
),],
child: Builder(builder: (BuildContext context){
BuildContext rootContext = context;
return Container(
//Here to use rootContext is safe
//Provider.of<SomeModel>(rootContext, listen: false);
);
}),
);
}
提供商范围
MaterialApp
> provider(Screen A)
> Screen B
如果 Provider
在屏幕 A 中实例化,则在 A → B 的 Navigator.push
之后将无法在屏幕 B 中访问。
为什么?
因为 Provider
是一个 InheritedWidget
而 Navigator
在其 Screen A context
范围之外使用 MaterialApp context
。 (请参阅下面的详细信息。)
修复
将 Provider
移动到 common-parent、MaterialApp context
,允许屏幕 A 和 B 继承其 state/context。
provider(MaterialApp)
> Screen A
> Screen B
例子
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
/// wrap MaterialApp in Provider widget
return ChangeNotifierProvider(
create: (context) => ColorModel(), // ← create/init your state model
child: MaterialApp(
home: ScreenA()
),
);
}
}
详情
提供商
Provider
基于InheritedWidget
。只有 child 小部件可以 继承 parent 小部件的状态。Provider
需要成为任何想要访问您的“已提供”状态的小部件树的 根小部件 object。
导航器
-
屏幕 A 上的
Navigator.push(context)
不使用屏幕 A 中的context
。- 它使用
MaterialApp
中的context
。
- 它使用
Navigator.push(context)
实际上是Navigator.of(context).push
Navigator.of(context)
表示:向上搜索此上下文层次结构,直到找到实例化 Navigator 的上下文- 默认
Navigator
在MaterialApp
中实例化。 - 除非您明确 create/use 一个不同的
Navigator
,否则您使用的是默认值。 Navigator
的context
是MaterialApp
的
- 默认
- 屏幕 B 将获得 that 上下文(属于
MaterialApp
),不是 屏幕 A 的上下文。- B 是 A 的 兄弟 ,而不是它的 child。
- B 不继承自 A,即使它在
context
A 中出现“实例化”。 - 屏幕 B 是
MaterialApp
context
的 child,而不是屏幕 Acontext
。 Provider
context
范围,如果在屏幕 A 中定义,则不涵盖屏幕 B
画面A→B
Navigator.push(context, MaterialPageRoute(builder: (context) => ScreenB()))
实际上是:
Navigator.of(context).push(MaterialPageRoute(builder: (context) => ScreenB()))
类似于:
Navigator.of(MaterialApp).push(
MaterialPageRoute(builder: (MaterialAppContext) => ScreenB())
)
因此屏幕 B 在 MaterialApp context
下, 而不是 在屏幕 A context
下,因此无法访问屏幕 A Provider
及其 context
.
有关 Provider
的类似问题请参阅
可能会在 DetailScreen
中回答。
我不知道你是否解决了这个问题 problem.but 我只是使用参数解决了。
首先我的问题是,我有一个 list screen
、一个 detail screen
和一个 listBloc
。我在 list screen
中提供了 listBloc
。像这样:
class ListScreen extends StatelessWidget {
static const String routeName = '/list_screen';
ListScreen({Key key}) : super(key: key);
static Route route(Map<String, dynamic> args) {
return MaterialPageRoute(
builder: (_) => ListScreen(),
settings: const RouteSettings(name: routeName),
);
}
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) =>ListBloc(),
)
]
child: DetailScreen(),
);
}
}
而且,我需要在 detail screen
的 BlocBuilder
中使用 listBloc
。
class DetailScreen extends StatelessWidget {
// you need a context
final BuildContext context;
// get context
const DetailScreen({
Key key,
@required this.context,
}) : super(key: key);
@override
Widget build(BuildContext context) {
// get bloc using your context
final bloc = this.context.read<ListBloc>();
return BlocBuilder<ListBloc, ListState>(
// provide your bloc
bloc: bloc,
builder: (context, state) {}
);
}
}
这就是我们使用多个提供商的方式
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => GeneralProvider(),
),
],
child: MaterialApp(
home: InitPage(),
onGenerateRoute: MyRouter.generateRoute,
initialRoute: '/InitPage',
debugShowCheckedModeBanner: false,
title: 'Eray',
),
);
}
}
我从错误的路径导入了我的 Provider:
import 'file:///D:/Projects/Flutter/library_app/lib/MyStateManagement/local/temp_management.dart';
当我更改路径的导入时,它对我有用:
import 'package:library_app/MyStateManagement/local/temp_management.dart';
当我更改 Provider 文件的文件夹时,我遇到了这个问题。