如何在Flutter的对话框中访问提供者提供者

时间:2019-09-17 06:24:11

标签: flutter flutter-provider inherited-widget

提供者包使用docker run。当我想在对话框中访问提供程序时,这是一个问题。如果我使用

加载对话框
InheritedWidget

我无法使用 showDialog(... builder: (context) => MyDialog); 访问任何内容,因为我的对话框不是主窗口小部件树的一部分。这还意味着我无法访问我的提供商提供商,对吗?

我的问题是:如果对话框不是主应用程序小部件树的一部分,如何在对话框中访问我的提供程序?

InheritedWidget

使用final firebaseAuth = Provider.of<FirebaseAuth>(context); 时遇到相同的问题。如果我尝试通过BLoCs在对话框中检索它们,则它们将失败。我通过在构造函数中传递InheritedWidget来解决此问题,但这似乎违反了BLoC的目的。

8 个答案:

答案 0 :(得分:3)

您可以使用BlocProvider.value,而不是在构造函数中传递BLoC。

https://pub.dev/documentation/flutter_bloc/latest/flutter_bloc/BlocProvider/BlocProvider.value.html

这将使您可以将现有的BLoC实例提供给新路线(对话框)。而且您仍然可以获得InheritedWidget

的所有好处
  // Get the BLoC using the provider
  MyBloc myBloc = BlocProvider.of<MyBloc>(context);

  showDialog(
    context: context,
    builder: (BuildContext context) {
      Widget dialog = SimpleDialog(
        children: <Widget>[
          ... // Now you can call BlocProvider.of<MyBloc>(context); and it will work
        ],
      );

      // Provide the existing BLoC instance to the new route (the dialog)
      return BlocProvider<MyBloc>.value(
        value: myBloc, //
        child: dialog,
      );
    },
  );

.value()也用于ChangeNotifierProvider,ListenableProvider等。 https://pub.dev/documentation/provider/latest/provider/ChangeNotifierProvider/ChangeNotifierProvider.value.html

https://pub.dev/documentation/provider/latest/provider/ListenableProvider/ListenableProvider.value.html

答案 1 :(得分:1)

我在这部分卡住了一段时间。老实说,我不想传递提供程序,在处理复杂的窗口小部件时,也很难将窗口小部件代码解包以获取父上下文(这似乎不是最好的方法)。

这更有意义

class FileViewer extends StatelessWidget {
.
.
Widget build(BuildContext context) {
    //you can enable or disable listen if you logic require so 
    var reportState = Provider.of<ReportState>(context); 
    return Text('${reportState.files.length}');
 }
}

在这种情况下,您的子小部件(即FileViewer)可以使用

    public class Course    {
        public string courseName { get; set; } 
    }

    public class Root    {
        public Course course { get; set; } 
        public int ownerAccId { get; set; } 
    }

答案 2 :(得分:0)

我能够通过将数据集传递到警报对话框中来访问提供者数据。有趣的是,您必须在对话框中调用setState()才能在对话框中查看更改。

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {

    final provider = Provider.of<DataSet>(context);

    return Scaffold(
      body: Container(
        child: RaisedButton(
        child: Text('Show Dialog'),
          onPressed: () {
            showDialog(context: context,
            builder: (context) {
              return DialogContent(dataSet: provider);
            });
          },
        ),
      ),
    );
  }
}

class DialogContent extends StatefulWidget {

  final DataSet dataSet;

  const DialogContent({Key key, this.dataSet}) : super(key: key);

  @override
  _DialogContentState createState() => _DialogContentState();
}

class _DialogContentState extends State<DialogContent> {
  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: Text('Dialog with data'),
      content: Text('${widget.dataSet.pieceOfData}'),
      actions: <Widget>[
        FlatButton(
          child: Text('Increase Data'),
          onPressed: () {
            setState(() {
              widget.dataSet.increaseData();
            });
          },
        ),
      ],
    );
  }
}

class DataSet with ChangeNotifier {
  int pieceOfData = 1;

  increaseData() {
    pieceOfData += 1;
    notifyListeners();
  }
}

答案 3 :(得分:0)

您必须将提供的内容直接传递给对话框构造函数,以便在对话框的新上下文中访问它。如果对话框中有非常深的小部件树,并且您想从更深的地方访问它,也可以将其提供给对话框树顶部的新Provider小部件。

如果您使用的是Bloc,通常会在分配提供程序窗口小部件以清理流控制器/订阅时,告诉Provider调用Bloc的dispose方法。显然,如果要重新为对话框提供块,或者在对话框外使用该块,则可能不希望这样做。

在对话框中使用有状态或无状态窗口小部件完全由您决定,只要您有权访问集团,就可以使用streambuilder并照常收听某些流。

一个例子:

class EditEventDialog extends StatelessWidget {

  final GroupBloc groupBloc;

  EditEventDialog({this.groupBloc})
      : assert(groupBloc != null);

  @override
  Widget build(BuildContext context) {
    return Provider(
      builder: (context) => groupBloc,
      child: Dialog(
        child: Container(
          height: 400.0,
          width: 200.0,
          child: StreamBuilder<StatusStreamType>(
            stream: groupBloc.statusStream,
            builder: (context, snapshot) {
    ....

并称之为:

onPressed: () => showDialog(
                    builder: (newContext) {
                      GroupBloc groupBloc = Provider.of<GroupBloc>(context);
                      return EditEventDialog(
                        groupBloc: groupBloc,
                      );
                    },
                    context: context,
                  )

答案 4 :(得分:0)

尝试一下。创建另一个容纳对话框的有状态窗口小部件,并在调用showDialog()方法时返回该对话框的有状态窗口小部件。下面的例子

julia> function f()
           local a
           for i in 0:2
               if i > 0
                   println(a)
               end
               a = i
           end
       end
f (generic function with 2 methods)

julia> f()
0
1

答案 5 :(得分:0)

我今天也遇到了同样的问题,我可以通过将对话框包装在有状态的生成器中并在新的小部件树中设置状态来解决此问题。

      context: context,
      builder: (context) {
        return StatefulBuilder(builder: (context, setState) {
          return Dialog(
            child: SingleChildScrollView(
              child: Container(
                child: SingleChildScrollView(
                  child: Column(
                    children: <Widget>[
                      Padding(
                        padding: EdgeInsets.symmetric(vertical: height * .05),
                        child: Text('Choose An Avatar'),
                      ),
                      Stack(
                        children: <Widget>[
                          Align(
                            alignment: Alignment.center,
                            child: CircleAvatar(
                              minRadius: width * .09,
                              maxRadius: width * .09,
                              backgroundColor: Colors.brown,
                              backgroundImage: AssetImage(
                                  'assets/profile${appData.avatar}.png'),
                            ),
                          ),
                          Positioned.fill(
                            left: width * .04,
                            child: Align(
                              alignment: Alignment.centerLeft,
                              child: Container(
                                width: width * .18,
                                child: Material(
                                  color: Colors.transparent,
                                  child: InkWell(
                                    child: Icon(Icons.arrow_left,
                                        size: width * .18),
                                    onTap: () {
                                      setState(() {
                                        appData.changeAvatar();
                                      });
                                    },
                                  ),
                                ),
                              ),
                            ),
                          ),
                        ],
                      ),
                    ],
                  ),
                ),
              ),
            ),
          );
        });
      });

答案 6 :(得分:0)

找到这个有点晚了,但是也遇到了同样的挑战并实现了一个解决方案:您需要在showDialog调用之外维护对上下文的引用。默认情况下,我们通常只在showDialog的内部和外部使用“ context”作为上下文的名称,从而屏蔽了外部上下文,使其无法在showDialog中使用。因此,您可以在showDialog中使用其他名称(例如“ c”),然后仍然可以使用“ final firebaseAuth = Provider.of(context);”在showDialog中,它将根据需要从主树中找到FirebaseAuth对象。

这是我正在处理的一些代码的简短摘录,现在可以正常工作:

showDialog(
    context: context,
    builder: (c) {
        final action = Provider.of<ActionType>(context);
        final host = Provider.of<String>(context);
        return AlertDialog(
            title: Text('Action API'),
            actions: [
                FlatButton(
                    onPressed: () {
                        Navigator.pop(c);
                    },

答案 7 :(得分:0)

如果这是您的选择,只需将提供者提升到MaterialApp以上。对于全球唯一的提供商来说,这可能是一个很好的解决方案,例如用户配置或类似配置: enter image description here