dart 如何将数据从子控件传递到其父控件

q9yhzks0  于 2023-06-03  发布在  其他
关注(0)|答案(9)|浏览(333)

我有下面的自定义小部件,使Switch和读取其状态(真/假)
然后我将这个添加到我的主应用程序小部件(父),我怎么能让父知道开关的值!

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)),
          ],
        ),
      ),
    );
  }
}

main.dart(parent)文件中,我从以下内容开始:

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();
  //...

}
wlzqhblo

wlzqhblo1#

第一种可能性是将回调传递给子窗口,第二种可能性是对有状态小部件使用of模式。见下文。

import 'package:flutter/material.dart';

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

  // note: updated as context.ancestorStateOfType is now deprecated
  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(),
  ),
);

还有一种方法是使用InheritedWidget而不是StatefulWidget;如果你想让你的子部件在父部件的数据改变并且父部件不是直接的父部件时重建,这是特别有用的。参见inherited widget documentation

8tntrjer

8tntrjer2#

我认为通知是一个相当文明的解决方案,它们允许一个非常干净的沟通,没有变量杂耍,如果你需要它们,它们会冒泡:
定义通知:

class SwitchChanged extends Notification {
  final bool val
  SwitchChanged(this.val);
}

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

onPressed: () {
  SwitchChanged(true).dispatch(context);
}

最后,使用通知侦听器 Package 您的父级:

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

ddarikpa3#

在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(),
      ),
    );

然而,在这个问题的答案中提到的方法有一个缺点。doc
但是,一般来说,考虑使用一个回调来触发祖先中的有状态更改,而不是使用此方法所隐含的命令式样式。这通常会导致更易于维护和可重用的代码,因为它将小部件彼此解耦。
调用这个方法的开销相对较大(树的深度为O(N))。仅当已知此小部件到所需祖先的距离很小且有界时,才调用此方法。

nbysray5

nbysray54#

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

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'),
    );
  }
}
zu0ti5jz

zu0ti5jz5#

使用InheritedWidget-https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html
这使您可以访问所有子对象中父对象的数据

blmhpbnm

blmhpbnm6#

我找到了一个方法来做到这一点,这是相当简单的,我是一个扑菜鸟,所以也许这不是最好的方法。如果有人看到它有什么问题,请随时发表评论。基本上,状态是在父控件中设置的,子控件更新父控件的状态,并且当值更新时,使用状态值的父控件的任何子控件被重新绘制。
父控件:

class MyWidget extends StatefulWidget {
  const MyWidget({Key? key}) : super(key: key);

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

class _MyWidgetState extends State<MyWidget> {

  String _stringToChange = ""; // the string you want to update in child

  // function to update state with changes to term
  _updateStringToChange(String stringToChange) {
    setState(() {
      _stringToChange = stringToChange;

      // Other logic you might want to do as string value changes
    });
  }

  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'title',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: Scaffold(
          appBar: AppBar(
            title: const Center(
              child: Text("app bar title"),
            ),
          ),
          body: Column(children: <Widget>[
            ChildWhichMakesChanges(
                updateStringToChange: _updateStringToChange,
            ),
            Expanded(
                child: Container(
                    padding: const EdgeInsets.fromLTRB(20, 10, 0, 10),
                    child: ChildWhichUsesChanges(
                        stringToChange: _stringToChange,
            )))
          ]),
        ));
  }
}

ChildWhichMakesChanges(此示例使用文本框输入):

class ChildWhichMakesChanges extends StatefulWidget {
  final ValueChanged<String> updateStringToChange;

  const ChildWhichMakesChanges({Key? key, required this.updateStringToChange}) : super(key: key);

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

class _TextInputState extends State<ChildWhichMakesChanges> {
  @override
  Widget build(BuildContext context) {
    return Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        mainAxisSize: MainAxisSize.min,
        children: [
          Padding(
              padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 25),
              child: TextField(
                  decoration: const InputDecoration(
                    border: OutlineInputBorder(),
                    hintText: 'Enter text',
                  ),
                  onChanged: (String stringToChange) {
                    widget.updateStringToChange(stringToChange);
                  })),
        ]);
  }
}

在ChildWhichUsesChanges中使用已更改的字符串值:

class ChildWhichUsesChanges extends StatelessWidget {
  final String stringToChange;

  const ChildWhichUsesChanges(
      {Key? key,
      required this.stringToChange})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(stringToChange)
  }
}
nc1teljy

nc1teljy7#

2022解决方案:很简单的一个。
让它像***接口***一样工作。
你可以通过定义typedef来创建你自己的回调函数。它将只是作为一个接口之间的子父部件。

  • 这是一个IMP函数:*
typedef void GetColor(Color? color, String? string);

以下是Parent Widget:

import 'package:flutter/material.dart';

typedef void GetColor(Color? color, String? string);

class NavigationDialog extends StatefulWidget {
  const NavigationDialog({Key? key}) : super(key: key);

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

class _NavigationDialogState extends State<NavigationDialog> {
  Color? color = Colors.blue[700];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: color,
      appBar: AppBar(
        title: const Text('Navigation Dialog Screen'),
      ),
      body: Center(
        child: ElevatedButton(
            child: const Text('Change Color'),
            onPressed: () {
              _showColorDialog(context, (value, string) {
                setState(() {
                  color = value;
                  print(string);
                });
              });
            }),
      ),
    );
  }

以下是一个子Widget代码:

_showColorDialog(BuildContext context, Function getColor) async {
        color = null;
        await showDialog(
          barrierDismissible: false,
          context: context,
          builder: (_) {
            return AlertDialog(
              title: const Text('Very important question'),
              content: const Text('Please choose a color'),
              actions: <Widget>[
                TextButton(
                    child: const Text('Red'),
                    onPressed: () {
                      color = Colors.red[700];
                      getColor(color, 'Red');// This line of action wil send your data back to parent
                      Navigator.pop(context, color);

                    }),
                TextButton(
                    child: const Text('Green'),
                    onPressed: () {
                      color = Colors.green[700];
                      getColor(color, 'Green');// This line of action wil send your data back to parent
                      Navigator.pop(context, color);

                    }),
                TextButton(
                    child: const Text('Blue'),
                    onPressed: () {
                      color = Colors.blue[700];
                      getColor(color, 'Blue');// This line of action wil send your data back to parent
                      Navigator.pop(context, color);

                    }),
              ],
            );
          },
        );
      }
    }

在这个例子中,我们从子警报对话框小部件中选择颜色,并传递给父小部件。

8e2ybdfx

8e2ybdfx8#

为了将数据从子节点传递到父节点,我想使用我在应用程序中使用的最简单的方法,例如创建自定义按钮和其他回调小部件。所以(1)第一个例子是拥有子对象StatelessWidget,(2)第二个例子是拥有子对象StatefulWidget,并希望将数据传递给父对象。让我们从StatelessWidget开始:(一)

class ChildWidget extends StatelessWidget {
           //Function can pass the data that you need,
    // as final void Function(String) onValueSelected - passing some string value,
    // or final void Function(YourCustomObject) onValueSelected - passing some of yours custom objects, or array, etc.;
    //or without the params as here:
         final void Function() onCustomButtonPressed; 
    
          const ChildWidget({
            Key? key, required this.onCustomButtonPressed,
          }) : super(key: key);
        
          @override
          Widget build(BuildContext context) {
            return Padding(
              padding: const EdgeInsets.symmetric(horizontal: 20),
              child: Column(
                children: [...
        ElevatedButton(
                onPressed: onCustomButtonPressed,
...}}

在父控件中将其用作:

class ParentWidget extends StatelessWidget {
    ...
    @override
      Widget build(BuildContext context) {
        return Scaffold(
    ...
     ChildWidget(
                
                  onCustomButtonPressed: () => navigateToHomePage(),//or with the params, see below
    
                ),
    ChildWidget(
                    onValueSelected: (value) { // use [value] that you passed from the child
})}
    ...}}

(2)现在一个子控件作为StatefulWidget传递一些数据给父控件,像这样。例如,这里是一些DropdownWidget传递选定的字符串,例如:

class CustomDropdownWidget extends StatefulWidget {

  final void Function(String) onValueSelected;

  const CustomDropdownWidget({Key? key, required this.onValueSelected})
      : super(key: key);

  @override
  State<CustomDropdownWidget> createState() =>
      _CustomDropdownWidgetState(this.onValueSelected);
}

class _CustomDropdownWidgetState extends State<CustomDropdownWidget> {
 
  _CustomDropdownWidgetState(onValueSelected);

  @override
  Widget build(BuildContext context) {
... DropdownButton{
 onChanged: (String newValue) {
       
          widget.onValueSelected(newValue);
        
      },
}}

在父Widget中使用它:

class ParentWidget extends StatelessWidget {
    ...
    @override
      Widget build(BuildContext context) {
        return Scaffold(
...
 CustomDropdownWidget(onValueSelected: (value) {
print("passed value is $value");

}
}}

epfja78i

epfja78i9#

将子部件中的值存储在共享首选项中,然后访问父部件中的共享首选项值。

相关问题