Не удалось найти правильный провайдер над этим виджетом
У меня проблема с использованием провайдера Flutter... Мой поток выглядит следующим образом: После входа в систему идентификатор пользователя передается в новый виджет -> оттуда он преформ сохранить в БД, а затем перенаправляет в новый виджет (панель инструментов).
И это код виджета после входа в систему:
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);
В виджете Dashboard я создаю это:
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));
}
Я знаю, что в этой теме уже решены вопросы, но не смог найти ничего подходящего для моей проблемы.
7 ответов
Попробуйте извлечь часть вашего дерева виджетов, которая находится ниже Scaffold, в отдельный виджет. Контекст, который вы используете сейчас, используется для создания вашего виджета верхнего уровня, который пока не поддерживает 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);
},
...
}
}
Область действия провайдера
MaterialApp
> provider(Screen A)
> Screen B
Если он создан на экране A, он не будет доступен на экране B после
Navigator.push
из A → B.
Почему?
Потому что is an и использует за пределами области Screen A. (См. Подробности ниже.)
Исправить
Переход к общему родительскому элементу позволяет и Screen A, и B унаследовать его состояние / контекст.
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()
),
);
}
}
Подробности
Провайдер
основан на
InheritedWidget
. Только дочерние виджеты могут наследовать состояние родительского виджета.- должен быть корневым виджетом для любого дерева виджетов, которому нужен доступ к вашему «предоставленному» объекту состояния.
Навигатор
- на Экране А не использует экран А.
- Он использует от.
-
Navigator.push(context)
на самом делеNavigator.of(context).push
-
Navigator.of(context)
означает: ищите эту контекстную иерархию, пока не найдете контекст, который создал экземпляр навигатора- По умолчанию создается экземпляр.
- Если вы явно не создаете / не используете другой, вы используете значение по умолчанию.
-
Navigator
вот что из.
- Экран B получит этот контекст, а не контекст экрана A.
- B является родным братом A, а не его дочерним элементом.
- B не наследуется от A, даже несмотря на то, что он кажется «созданным» внутри A.
- Экран B является дочерним элементом
MaterialApp
, а не экрана А. - область действия, если она определена на экране 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 и, следовательно, не имеет доступа к экрану A и его
context
.
См. Этот ответ для примера кода на аналогичный вопрос о
Provider
.
Параметр context
для 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);
);
}),
);
}
Вот как мы использовали несколько провайдеров
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',
),
);
}
}
Я импортировал своего провайдера не по тому пути:
import 'file:///D:/Projects/Flutter/library_app/lib/MyStateManagement/local/temp_management.dart';
И когда я меняю импорт пути, он работает для меня:
import 'package:library_app/MyStateManagement/local/temp_management.dart';
И эта проблема возникла у меня, когда я изменил папку файла Provider.
может ответить в
DetailScreen
.
Не знаю, решили ли вы эту проблему, но я решил только с помощью параметра. Во-первых, моя проблема в том, что у меня есть a, a и a. Я предоставил в
list screen
. как это :
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(),
);
}
}
и мне нужно использовать
listBloc
в
detail screen
с
BlocBuilder
.
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) {}
);
}
}
введите описание изображения здесь. Поскольку я использую виджет navigator.push на своей странице входа в систему, когда я запускаю файл dart, возникает ошибка, в которой сообщение на экране записывается как: Ошибка: не удалось найти правильного поставщика «FavoriteModel над этим потребительским виджетом. Это происходит. потому что вы использовали «BuildContext, который не включает поставщика. Ошибка: не удалось найти правильный Provider «FavoriteModels» над этим виджетом Consumer «FavoriteModels». Это происходит потому, что вы использовали «BuildContext, который не включает». Если кто-нибудь знает об этом, пожалуйста, предложите мне решение.
void main() {
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
),
);
APICallService.getProduct();
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => FavoriteModel(),
),
ChangeNotifierProvider(
create: (context) => CartProvider(),
),
],
child: const App(),
),
);
}