Не удалось найти правильный провайдер над этим виджетом

У меня проблема с использованием провайдера 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(),
    ),
  );
}
Другие вопросы по тегам