从 Flutter 中的 Stateful Widget 返回数据

Posted

技术标签:

【中文标题】从 Flutter 中的 Stateful Widget 返回数据【英文标题】:Returning data from a Stateful Widget in Flutter 【发布时间】:2019-07-08 16:51:10 【问题描述】:

我之前已经发布过我仍然面临的这个问题 也就是将数据从有状态小部件返回到无状态小部件

我使用的小部件是 DateTimePickerFormField 小部件,我在有状态小部件中将它用作子部件

所以我看过https://flutter.io/docs/cookbook/navigation/returning-data#complete-example

用于从小部件返回数据。但是,返回数据的小部件是无状态小部件......在我的情况下不是

所以代码如下

Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(
        title: Text('Add a Reminder'),

      ),
      body:
      new Container(
        padding: new EdgeInsets.all(20.0),
        child: new Form(
            child: new ListView(
              children: <Widget>[

            new TextFormField(
            keyboardType: TextInputType.text,
              // Use email input type for emails.
              decoration: new InputDecoration(
                hintText: 'Title of reminder',
              ),

            ),
            dateAndTimeWidget(context),
            RaisedButton(
            child: Text('Save'),
        onPressed: () 
          Navigator.pop(context);
        ,
      )
      ],
    ),)
    ,
    )
    ,
    );

  

那个小部件正在调用方法:dateAndTimeWidget,它应该返回 dateTime 有状态小部件并将输出的数据保存在一个变量中:

 dateAndTimeWidget(BuildContext context) async 
    final result = await Navigator.push(
      context,
      MaterialPageRoute(builder: (context)=> dateTime()),
    );


那么这是 dateTime Statful 小部件

class dateTime extends StatefulWidget
    @override
  dateTimeState createState() => dateTimeState();


class dateTimeState extends State<dateTime>

  static DateTime dateT;

  InputType inputType = InputType.both;

  final formats = 
    InputType.both: DateFormat("EEEE, MMMM d, yyyy 'at' h:mma"),
    InputType.date: DateFormat('yyyy-MM-dd'),
    InputType.time: DateFormat("HH:mm"),
  ;

  Widget build(BuildContext context) => Container(
      child: DateTimePickerFormField(
        inputType: InputType.both,
        editable: true,
        format: formats[inputType],
        decoration: InputDecoration(
            labelText: 'Date/Time', hasFloatingPlaceholder: false),
        onChanged: (dt) 
          setState(() => dateT = dt);
          Navigator.of(context).pop(dateT);
        ,

      )


  );



我还没有使用值结果 因为错误是我从来没有进入添加提醒页面,它说结果推送导航方法指向 null

【问题讨论】:

【参考方案1】:

在这种情况下,使用 Navigator 传递数据是不合适的。因为页面和您的dateTime Widget 之间没有页面转换。我建议您实现ValueChanged 回调以在同一屏幕的小部件之间传递数据。

示例代码:

这有点棘手。但是material.dart 的小部件经常使用这种技术。我希望这能帮到您!

class MyHomePage extends StatefulWidget 
  @override
  _MyHomePageState createState() => _MyHomePageState();


class _MyHomePageState extends State<MyHomePage> 
  DateTime dateT;

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(
        title: Text('Add a Reminder'),
      ),
      body: new Container(
        padding: new EdgeInsets.all(20.0),
        child: new Form(
          child: new ListView(
            children: <Widget>[
              new TextFormField(
                keyboardType: TextInputType.text,
                // Use email input type for emails.
                decoration: new InputDecoration(
                  hintText: 'Title of reminder',
                ),
              ),
              dateTime(
                onDateTimeChanged: (newDateTime) 
                  dateT = newDateTime;
                ,
              ),
              RaisedButton(
                child: Text('Save'),
                onPressed: () 
                  Navigator.pop(context);
                ,
              )
            ],
          ),
        ),
      ),
    );
  


class dateTime extends StatefulWidget 
  final ValueChanged<DateTime> onDateTimeChanged;

  dateTime(Key key, this.onDateTimeChanged) : super(key: key);

  @override
  dateTimeState createState() => dateTimeState();


class dateTimeState extends State<dateTime> 
  DateTime dateT;

  InputType inputType = InputType.both;

  final formats = 
    InputType.both: DateFormat("EEEE, MMMM d, yyyy 'at' h:mma"),
    InputType.date: DateFormat('yyyy-MM-dd'),
    InputType.time: DateFormat("HH:mm"),
  ;

  Widget build(BuildContext context) => Container(
        child: DateTimePickerFormField(
          inputType: InputType.both,
          editable: true,
          format: formats[inputType],
          decoration: InputDecoration(
              labelText: 'Date/Time', hasFloatingPlaceholder: false),
          onChanged: (dt) 
            widget.onDateTimeChanged(dt);
          ,
        ),
      );

(顺便说一句,你的dateAndTimeWidget 是一个方法,不是一个小部件。所以如果你将它分配给 Column 的子项(列表),Flutter 框架无法理解。)

【讨论】:

【参考方案2】:

对于从另一个小部件返回值并使用它的更节省时间的解决方案,这是 Flutter Dropdown 的示例:

import 'package:flutter/material.dart';
import 'package:paxi_ride/constant.dart';

class BuildDropdown extends StatefulWidget 
  final ValueChanged<String> onChanged;
  String defaultValue, selectedValue, dropdownHint;
  List<String> itemsList;

  BuildDropdown(
      Key key,
      this.itemsList,
      this.defaultValue,
      this.dropdownHint,
      this.onChanged)
      : super(key: key);

  @override
  _BuildDropdownState createState() => _BuildDropdownState();


class _BuildDropdownState extends State<BuildDropdown> 
  String _value;

  @override
  Widget build(BuildContext context) 
    return Container(
      // height: 40,
      padding: EdgeInsets.symmetric(horizontal: 16, vertical: 0),
      margin: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
      color: Colors.white,
      child: DropdownButton<String>(
        items: widget.itemsList.map(
          (String value) 
            return new DropdownMenuItem<String>(
              value: value,
              child: new Text(value),
            );
          ,
        ).toList(),
        value: _value == null ? widget.defaultValue : _value,
        isExpanded: true,
        onChanged: (String value) 
          setState(() 
            _value = value;
          );
          widget.onChanged(value);
        ,
        hint: Text(widget.dropdownHint),
        style: TextStyle(
          fontSize: 14,
          color: brownColorApp,
        ),
        iconEnabledColor: brownColorApp,
        iconSize: 14,
        underline: Container(),
      ),
    );
  

从另一个小部件调用此小部件,您希望在其中获取选定值,如下所示:

String selectedValue; //class field

BuildDropdown(
          itemsList: ['Option 1','Option 2'],
          defaultValue: 'Option 1',
          dropdownHint: 'Select Required Option',
          onChanged: (value) 
            selectedValue = value;
          ,
        ),

在需要的地方使用这个值,它会随着下拉选择的改变而改变。

【讨论】:

以上是关于从 Flutter 中的 Stateful Widget 返回数据的主要内容,如果未能解决你的问题,请参考以下文章

Flutter Stateful Widget 状态未初始化

Flutter: Stateful 挂件 vs Stateless 挂件

Flutter控件篇(Stateful widget)——ListView

Flutter - Stateful(有状态) 和 stateless(无状态) widgets

跨多个屏幕使用的 Flutter Stateful Widget 正在重建

Flutter Stateful Widget 重新创建 State