Flutter Provider 嵌套导航

Jos*_*elm 20 dart flutter

我在提供程序和导航方面有问题。

我有一个HomeScreen对象列表。当您单击一个对象时,我会导航到DetailScreen带有选项卡的导航。这DetailScreen是用 ChangenotifierProvider 包装的,它提供了一个ViewModel

现在,当我浏览到另一个屏幕,Navigator.of(context).push(EditScreen)我无法访问ViewModel内的EditScreen 异常下列错误

???????? Exception caught by gesture ???????????????????????????????????????????
The following ProviderNotFoundException was thrown while handling a gesture:
Error: Could not find the correct Provider<ViewModel> above this EditScreen Widget
Run Code Online (Sandbox Code Playgroud)

这是我尝试实现的目标的简单概述

Home Screen
 - Detail Screen (wrapped with ChangeNotifierProvider)
   - Edit Screen
     - access provider from here
Run Code Online (Sandbox Code Playgroud)

我知道问题是什么。我正在堆栈上推送一个新屏幕,但更改通知程序不再可用。我想在我的应用程序之上创建一个 Detail Repository,它包含 DetailView 的所有 ViewModel。

我知道我可以将 ChangeNotifier 包裹在我的 MaterialApp 周围,但我不想要,或者不能这样做,因为我不知道我需要哪个 Detail-ViewModel。我想要列表中每个项目的 ViewModel

我真的不知道解决这个问题的最佳方法是什么。谢谢大家的帮助

这是一个快速示例应用程序:

这是图像树的图片

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Center(
            child: RaisedButton(
      child: Text("DetailView"),
      onPressed: () => Navigator.of(context).push(MaterialPageRoute(
          builder: (context) => ChangeNotifierProvider(
              create: (_) => ViewModel(), child: DetailScreen()))),
    )));
  }
}

class DetailScreen extends StatelessWidget {
  const DetailScreen({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Center(
      child: RaisedButton(
        child: Text("EditScreen"),
        onPressed: () => Navigator.of(context)
            .push(MaterialPageRoute(builder: (context) => EditScreen())),
      ),
    ));
  }
}

class EditScreen extends StatelessWidget {
  const EditScreen({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: RaisedButton(
            child: Text("Print"),
            onPressed: () =>
                Provider.of<ViewModel>(context, listen: false).printNumber()),
      ),
    );
  }
}

class ViewModel extends ChangeNotifier {
  printNumber() {
    print(2);
  }
}
Run Code Online (Sandbox Code Playgroud)

Nic*_*gen 21

聚会有点晚了,但我认为这就是问题正在寻找的答案:

(基本上将 ViewModel 传递到下一个导航器页面。)

class DetailScreen extends StatelessWidget {
  const DetailScreen({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final viewModel = Provider.of<ViewModel>(context); // Get current ViewModel
    return Scaffold(
        body: Center(
      child: RaisedButton(
        child: Text("EditScreen"),
        onPressed: () => Navigator.of(context).push(
          // Pass ViewModel down to EditScreen
          MaterialPageRoute(builder: (context) {
            return ChangeNotifierProvider.value(value: viewModel, child: EditScreen());
          }),
        ),
      ),
    ));
  }
}

Run Code Online (Sandbox Code Playgroud)

  • 这个答案比其他答案更合适。MaterialApp 上面的提供者意味着该提供者将始终“活动”并消耗资源。这不是你通常想要的。按照这个答案,对于那些推送命名路由的人,您可以将提供程序作为参数传递,并在目标屏幕中调用 `ChangeNotifierProvider&lt;SomeProvider&gt;.value()`。需要将其作为参数传递并不好,但我还没有找到任何其他看起来比这个解决方案更合适的方法。 (2认同)

dlo*_*ani 15

为了能够跨导航访问提供者,您需要在 MaterialApp 之前提供它,如下所示

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => ViewModel(),
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Center(
            child: RaisedButton(
      child: Text("DetailView"),
      onPressed: () => Navigator.of(context).push(
        MaterialPageRoute(
          builder: (context) => DetailScreen(),
        ),
      ),
    )));
  }
}

class DetailScreen extends StatelessWidget {
  const DetailScreen({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Center(
      child: RaisedButton(
        child: Text("EditScreen"),
        onPressed: () => Navigator.of(context)
            .push(MaterialPageRoute(builder: (context) => EditScreen())),
      ),
    ));
  }
}

class EditScreen extends StatelessWidget {
  const EditScreen({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: RaisedButton(
            child: Text("Print"),
            onPressed: () =>
                Provider.of<ViewModel>(context, listen: false).printNumber()),
      ),
    );
  }
}

class ViewModel extends ChangeNotifier {
  printNumber() {
    print(2);
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 非常感谢你的帮助。我知道这个教程,但它对我的情况没有帮助,如果我想按照我在问题中提到的方式来做,也会遇到同样的问题。Remi 的回答只是提到我需要将提供商包含在我的路线中,但没有提到它是如何完成的。这实际上是我的问题:/我知道我可以稍微不同地设计我的应用程序,但其他所有方式对我来说都不自然,因此我的问题 (3认同)
  • 您找到这个问题的答案吗@JosefWilhelm? (3认同)
  • 我和@JosefWilhelm 也有同样的问题。将provider/multiprovider放在MaterialApp之前并不是我们想要的,尽管它可以工作。 (3认同)
  • 感谢您的回答。我知道这可行,但我不能这样做,因为我不仅有一个 ViewModel,而且每个 DetailScreen 都有一个 ViewModel。 (2认同)
  • 尝试来自同一开发人员的新 [riverpod](https://pub.dev/packages/riverpod) 状态管理,完全重写提供程序 (2认同)