如何将数据从子窗口小部件传递到其父窗口

Has*_*sef 21 dart flutter

我有以下自定义小部件,它可以创建Switch并读取其状态(true/false)

然后我将这个添加到我的主应用程序小部件(父级),如何让父级知道交换机的值!

import 'package:flutter/material.dart';

class Switchy extends StatefulWidget{
  Switchy({Key key}) : super(key: key);

  @override
  State<StatefulWidget> createState() => new _SwitchyState();
  }

class _SwitchyState extends State<Switchy> {
  var myvalue = true;

  void onchange(bool value) {
    setState(() {
      this.myvalue = value;      // I need the parent to receive this one!
      print('value is: $value');
    });
  }

  @override
  Widget build(BuildContext context) {
    return             
      new Card(
      child: new Container(
        child: new Row(
          mainAxisAlignment: MainAxisAlignment.end,
          children: <Widget>[
            new Text("Enable/Disable the app in the background",
              textAlign: TextAlign.left,
              textDirection: TextDirection.ltr,),
            new Switch(value: myvalue, onChanged: (bool value) => onchange(value)),
          ],
        ),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

main.dart(父)文件中,我从这开始:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.deepOrange,
      ),
      home: new MyHomePage(title: 'My App settup'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  Widget e = new Switchy();
  //...

}
Run Code Online (Sandbox Code Playgroud)

rmt*_*zie 25

第一种可能性是将回调传递给您的孩子,第二种可能是将of模式用于您的有状态小部件.见下文.

import 'package:flutter/material.dart';

class MyStatefulWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => new MyStatefulWidgetState();

  static MyStatefulWidgetState of(BuildContext context) {
    final MyStatefulWidgetState navigator =
        context.ancestorStateOfType(const TypeMatcher<MyStatefulWidgetState>());

    assert(() {
      if (navigator == null) {
        throw new FlutterError(
            'MyStatefulWidgetState operation requested with a context that does '
            'not include a MyStatefulWidget.');
      }
      return true;
    }());

    return navigator;
  }
}

class MyStatefulWidgetState extends State<MyStatefulWidget> {
  String _string = "Not set yet";

  set string(String value) => setState(() => _string = value);

  @override
  Widget build(BuildContext context) {
    return new Column(
      children: <Widget>[
        new Text(_string),
        new MyChildClass(callback: (val) => setState(() => _string = val))
      ],
    );
  }
}

typedef void StringCallback(String val);

class MyChildClass extends StatelessWidget {
  final StringCallback callback;

  MyChildClass({this.callback});

  @override
  Widget build(BuildContext context) {
    return new Column(
      children: <Widget>[
        new FlatButton(
          onPressed: () {
            callback("String from method 1");
          },
          child: new Text("Method 1"),
        ),
        new FlatButton(
          onPressed: () {
            MyStatefulWidget.of(context).string = "String from method 2";
          },
          child: new Text("Method 2"),
        )
      ],
    );
  }
}

void main() => runApp(
      new MaterialApp(
        builder: (context, child) => new SafeArea(child: new Material(color: Colors.white, child: child)),
        home: new MyStatefulWidget(),
      ),
    );
Run Code Online (Sandbox Code Playgroud)

还有使用InheritedWidget而不是StatefulWidget的替代方法; 如果您希望子窗口小部件重建,如果父窗口小部件的数据发生更改且父窗口不是直接父窗口,则此功能特别有用.请参阅继承的小部件文档

  • 我可以称之为'向父母发送数据'或'向父母发送回调数据',如果这让你开心 - 我知道它没有明确*作为参数传递*.但是在通话之前有一个只有孩子的字符串,之后父母就有了.你可以说它在方法意义上没有被传递*,但对于我们所有不关心语义的人来说,一些信息会移动,因此在更广泛的意义上传递.认为它没有通过,虽然在语义上更正确,但有点适得其反...... (4认同)
  • @Remi看,我想你错过了他问的重点。他有一个孩子的数据,想与父母取得联系。无论是使用回调,使用of还是任何其他方法完成,它都将数据沿树传递。您可以称之为其他名称,但这就是事实。 (3认同)
  • @csguy 而不是删除答案,通常最好只是建议编辑。我已经更新了答案以删除已弃用的功能。 (3认同)
  • @RémiRousselet 基于赞成票并且用户接受了这个答案,我认为你的区别并不重要,并且对这个答案没有任何补充。 (3认同)
  • 是的。但最终,问题是“如何将数据从子级传递给父级”。而答案并没有做到这一点。因此,虽然它是有效的,但解释原因并使用正确的术语也很重要。 (2认同)

Mar*_*cin 20

我认为通知是一个相当文明的解决方案,它们允许非常干净的通信,而无需变量杂耍,并且如果您需要它们,它们就会冒泡:

定义通知:

class SwitchChanged extends Notification {
  final bool val
  SwitchChanged(this.val);
}
Run Code Online (Sandbox Code Playgroud)

在您孩子的事件处理程序中发出通知:

onPressed: () {
  SwitchChanged(true).dispatch(context);
}
Run Code Online (Sandbox Code Playgroud)

最后,用通知监听器包装你的父级:

NotificationListener<SwitchChanged>(
  child: YourParent(...),
  onNotification: (n) {
    setState(() {
      // Trigger action on parent via setState or do whatever you like.
    });
    return true;
  }
)
Run Code Online (Sandbox Code Playgroud)

  • 这可能是向小部件树发送任何数据的最简单方法,无论您需要走多远。 (2认同)

Cop*_*oad 12

您可以将父窗口小部件中定义的回调传递给子窗口小部件,一旦在子窗口小部件中执行操作,就会调用回调。

class ParentWidget extends StatelessWidget {
  // This gets called when the button is pressed in the ChildWidget.
  void _onData(String data) {
    print(data); // Hello World
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ChildWidget(onData: _onData),
    );
  }
}

class ChildWidget extends StatelessWidget {
  final void Function(String) onData;

  ChildWidget({
    super.key,
    required this.onData,
  });

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () {
        // Pass 'Hello World' to parent widget.
        onData('Hello World');
      },
      child: Text('Button'),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

  • OP 询问如何在特定屏幕的小部件层次结构中传递数据,而不是屏幕之间的数据。 (18认同)
  • @PeteAlvin 我认为你误解了它。数据不是使用“Navigator.pop(...)”传递,而是通过回调传递。我已经更新了答案以使其更加清晰。 (2认同)

fzy*_*cjy 9

2020 年,投票最高的答案中的函数被标记为已弃用。所以这是基于该答案的修改后的解决方案。

import 'package:flutter/material.dart';

class MyStatefulWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => new MyStatefulWidgetState();

  // --> NOTE this! <--
  static MyStatefulWidgetState of(BuildContext context) =>
    context.findAncestorStateOfType<MyStatefulWidgetState>();
}

class MyStatefulWidgetState extends State<MyStatefulWidget> {
  String _string = "Not set yet";

  set string(String value) => setState(() => _string = value);

  @override
  Widget build(BuildContext context) {
    return new Column(
      children: <Widget>[
        new Text(_string),
        new MyChildClass(callback: (val) => setState(() => _string = val))
      ],
    );
  }
}

typedef void StringCallback(String val);

class MyChildClass extends StatelessWidget {
  final StringCallback callback;

  MyChildClass({this.callback});

  @override
  Widget build(BuildContext context) {
    return new Column(
      children: <Widget>[
        new FlatButton(
          onPressed: () {
            callback("String from method 1");
          },
          child: new Text("Method 1"),
        ),
        new FlatButton(
          onPressed: () {
            MyStatefulWidget.of(context).string = "String from method 2";
          },
          child: new Text("Method 2"),
        )
      ],
    );
  }
}

void main() => runApp(
      new MaterialApp(
        builder: (context, child) => new SafeArea(child: new Material(color: Colors.white, child: child)),
        home: new MyStatefulWidget(),
      ),
    );
Run Code Online (Sandbox Code Playgroud)

但是,这个问题的答案中提到的方法有一个缺点。从文档

但是,一般而言,请考虑使用触发祖先状态更改的回调,而不是使用此方法隐含的命令式样式。这通常会导致更易于维护和可重用的代码,因为它使小部件彼此分离。

调用此方法的开销相对较大(树的深度为 O(N))。仅当从该小部件到所需祖先的距离已知很小且有界时才调用此方法。