簡體   English   中英

將數據從自定義小部件文本字段傳遞到計算器 flutter

[英]Passing data from custom widget textfield to a calculator flutter

我正在嘗試將數據從包含文本字段的自定義小部件傳遞到計算器小部件。 我面臨的問題是我希望利用我的自定義小部件來創建多個輸入,這些輸入可以 go 到計算器(即身高和體重)。 任何人都可以協助使用自定義小部件傳遞數據嗎?

創建了自定義文本字段小部件

import 'package:auto_size_text/auto_size_text.dart';

enum Units { unit1, unit2 }

class InputRow extends StatefulWidget {
  InputRow({this.inputParameter, this.unit1, this.unit2});
  final String inputParameter;
  final String unit1;
  final String unit2;

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

class _InputRowState extends State<InputRow> {
  String newTaskTitle;
  Units selectedUnit;
  String unit;

  @override
  void initState() {
    super.initState();
    setState(() {
      unit = widget.unit1;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      constraints: BoxConstraints(maxWidth: 375, maxHeight: 50),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Container(
            child: AutoSizeText(
              widget.inputParameter,
              textAlign: TextAlign.center,
              style: TextStyle(
                fontSize: 20.0,
              ),
            ),
          ),
          Expanded(
            child: Container(
              decoration: BoxDecoration(
                border: Border.all(
                  color: Colors.red,
                  width: 3,
                ),
                borderRadius: BorderRadius.only(
                  topLeft: Radius.circular(10),
                  bottomLeft: Radius.circular(10),
                ),
              ),
              child: TextField(
                autofocus: true,
                textAlign: TextAlign.center,
                onChanged: (newText) {
                  newTaskTitle = newText;
                },
              ),
            ),
          ),
          Container(
            decoration: BoxDecoration(
              color: Colors.red,
              border: Border.all(
                color: Colors.red,
                width: 3,
              ),
              borderRadius: BorderRadius.only(
                topRight: Radius.circular(10),
                bottomRight: Radius.circular(10),
              ),
            ),
            child: Row(
              children: <Widget>[
                Container(
                  padding: EdgeInsets.all(5),
                  child: Center(
                      child: AutoSizeText(
                    unit,
                    style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
                  )),
                ),
                Container(
                    constraints: BoxConstraints(maxHeight: 50, maxWidth: 60),
                    child: FlatButton(
                      highlightColor: Colors.transparent,
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.center,
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: <Widget>[
                          Icon(
                            Icons.loop,
                            size: 25,
                          ),
                        ],
                      ),
                      onPressed: () {
                        setState(() {
                          selectedUnit = selectedUnit == Units.unit2
                              ? Units.unit1
                              : Units.unit2;
                          if (selectedUnit == Units.unit1) {
                            unit = widget.unit1;
                          } else {
                            unit = widget.unit2;
                          }
                        });
                      },
                    )),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

屏幕調用小部件,並希望將在文本字段中輸入的身高和體重傳遞給計算器




class InputScreen extends StatefulWidget {
  static const String id = 'adjustments';
  @override
  _InputScreenState createState() =>
      _AdjustmentInputScreenState();
}

class AdjustmentInputScreenState
    extends State<AdjustmentInputScreen> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: kActiveButtonColor,
      body: Column(
        children: <Widget>[
          AppBar(
            leading: null,
            actions: <Widget>[
              IconButton(
                  icon: Icon(Icons.close),
                  onPressed: () {
                    Navigator.pop(context);
                  }),
            ],
            title: Text('Dose Adjustment'),
            backgroundColor: Colors.transparent,
            elevation: 0.0,
          ),
          InputRow(
            unit1: 'cm',
            unit2: 'inches',
            inputParameter: 'height',
          ),
          InputRow(unit1: 'lbs', unit2: 'kg', inputParameter: 'weight',),
          RoundedButton(
            title: 'Calculate',
            onPressed: () {
//- code needed to pass the custom textfield widget data
            },
          ),
        ],
      ),
    );
  }
}

計算器大腦

import 'dart:math';

class CalculatorTest {
  CalculatorTest({this.height, this.weight, this.heightUnit, this.weightUnit});

  double height;
  double weight;
  final String heightUnit;
  final String weightUnit;

  double _bmi;

  String calculateBMI() {
    if (weightUnit == 'lbs') {
      weight = weight / 2.2;
    } else {
      weight = weight;
    }

    if (heightUnit == 'inches') {
      height = height / 2.53;
    } else {
      height = height;
    }

    _bmi = weight / pow(height / 100, 2);
    return _bmi.toStringAsFixed(1);
  }
}

第三輪

目標:有能力 select 三個按鈕之一,選擇的按鈕將是不同的顏色(如下 Button2),然后我可以在單擊計算按鈕時打印按鈕的標題(即 Button2)。

選擇 Button2 的示例

目前,除了打印的內容外,一切正常。 我只能獲取有關 Button1 的信息(如果使用 selected.option,我會得到“Option.one”,如果使用 selected.title,我會得到“Button1”),盡管實際選擇了哪個按鈕

我的按鈕代碼

class MyButton extends ValueNotifier<Option> {
  final String _title1;
  final String _title2;
  final String _title3;

  MyButton(
      {Option option = Option.one,
      String title1 = 'A',
      String title2 = 'B',
      String title3 = 'C'})
      : _title1 = title1,
        _title2 = title2,
        _title3 = title3,
        super(option);

  //You can add a get method to retrieve the title based on the option selected with a switch
  String get title {
    switch (value) {
      case Option.one:
        return _title1;
      case Option.two:
        return _title2;
      case Option.three:
        return _title3;
      default:
        return _title1; //or a default String, but to be honest this will never be used
    }
  }

  Option get option => value;
  set option(Option newOption) => value = newOption;
}

三鍵碼

enum Option {
  one,
  two,
  three,
}

class TriButton extends StatefulWidget {
  TriButton(
      {this.title1, this.title2, this.title3, this.triWidth, this.myButton});

  final String title1;
  final String title2;
  final String title3;
  final Constraints triWidth;
  final MyButton myButton;

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

class _TriButtonState extends State<TriButton> {
  Option selectedOption;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        constraints: widget.triWidth,
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Expanded(
              child: RectButton(
                buttonChild: Text(
                  widget.title1,
                  style: TextStyle(color: Colors.white),
                ),
                onPress: () {
                  setState(() {
                    selectedOption = Option.one;
                  });
                },
                bgColor: selectedOption == Option.one
                    ? kActiveButtonColor
                    : kInactiveButtonColor,
              ),
            ),
            Expanded(
              child: RectButton(
                buttonChild: Text(
                  widget.title2,
                  style: TextStyle(color: Colors.white),
                ),
                onPress: () {
                  setState(() {
                    selectedOption = Option.two;
                  });
                },
                bgColor: selectedOption == Option.two
                    ? kActiveButtonColor
                    : kInactiveButtonColor,
              ),
            ),
            Expanded(
              child: RectButton(
                buttonChild: Text(
                  widget.title3,
                  style: TextStyle(color: Colors.white),
                ),
                onPress: () {
                  setState(() {
                    selectedOption = Option.three;
                  });
                },
                bgColor: selectedOption == Option.three
                    ? kActiveButtonColor
                    : kInactiveButtonColor,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

輸入畫面

class InputScreen extends StatefulWidget {
  static const String id = 'adjustments';

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

class _InputScreenState
    extends State<InputScreen> {
  final TextEditingController weightController = TextEditingController();
  final TextEditingController heightController = TextEditingController();
  final TextEditingController creatController = TextEditingController();
  final MyUnit heightUnit = MyUnit();
  final MyUnit weightUnit = MyUnit(imperial: 'lbs', metric: 'kg');
  final MyUnit creatUnit = MyUnit(imperial: 'mg/dL', metric: 'mg/dL');
  final MyButton selected = MyButton();

  @override
  void dispose() {
    super.dispose();
    weightController.dispose();
    heightController.dispose();
    creatController.dispose();
    heightUnit.dispose();
    weightUnit.dispose();
    selected.dispose();
  }

  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Color(0xff142651),
      body: Column(
        children: <Widget>[
          AppBar(
            leading: null,
            actions: <Widget>[
              IconButton(
                  icon: Icon(Icons.close),
                  onPressed: () {
                    Navigator.pop(context);
                  }),
            ],
            title: Text('Dose Adjustment'),
            backgroundColor: Colors.transparent,
            elevation: 0.0,
          ),
          ValueListenableBuilder<Option>(
            valueListenable: selectedAbx,
            builder: (context, option, _) => TriButton(
              title1: 'Button 1',
              title2: 'Button 2',
              title3: 'Button 3',
            ),
          ),
          InputRow(
            myUnit: heightUnit,
            inputParameter: 'height',
            textField: heightController,
            colour: kOrangePantone,
          ),
          InputRow(
            myUnit: weightUnit,
            inputParameter: 'weight',
            textField: weightController,
            colour: kRoyalPurple,
          ),
          InputRow(
            myUnit: creatUnit,
            inputParameter: 'SCr',
            textField: creatController,
            colour: kDogwoodRose,
          ),
          RoundedButton(
            title: 'Calculate',
            onPressed: () {
              print(selected.option);
              String inputHeight = heightController.text;
              String inputWeight = weightController.text;
              String inputCreat = creatController.text;

              double imperialHeight = double.parse(inputHeight) * 2.54;
              double metricHeight = double.parse(inputHeight);
              double imperialWeight = double.parse(inputWeight) / 2.2;
              double metricWeight = double.parse(inputWeight);

              double creat = double.parse(inputCreat);

              CalculatorTest calc;
              if (heightUnit.unitType == 'cm' && weightUnit.unitType == 'kg') {
                calc = CalculatorTest(
                    height: metricHeight,
                    weight: metricWeight,
                    creatinine: creat);
              } else if (heightUnit.unitType == 'inches' &&
                  weightUnit.unitType == 'lbs') {
                calc = CalculatorTest(
                    height: imperialHeight,
                    weight: imperialWeight,
                    creatinine: creat);
              } else if (heightUnit.unitType == 'cm' &&
                  weightUnit.unitType == 'lbs') {
                calc = CalculatorTest(
                    height: metricHeight,
                    weight: imperialWeight,
                    creatinine: creat);
              } else {
                heightUnit.unitType == 'inches' && weightUnit.unitType == 'kg';
                calc = CalculatorTest(
                    height: imperialHeight,
                    weight: metricWeight,
                    creatinine: creat);
              }
              ;

              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => ResultsScreen(
                    bmiResult: calc.calculate(),
                  ),
                ),
              );
            },
          ),
        ],
      ),
    );
  }
}

在您的自定義小部件上為您的 TextField 添加參數 TextEditingController

class InputRow extends StatefulWidget {
  InputRow({this.inputParameter, this.unit1, this.unit2, this.textField});
  final String inputParameter;
  final String unit1;
  final String unit2;
  final TextEditingController textField; //Add this controller and also to the parameters of the constructor

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



class _InputRowState extends State<InputRow> {
  String newTaskTitle;
  Units selectedUnit;
  String unit;

  @override
  void initState() {
    super.initState();
    setState(() {
      unit = widget.unit1;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      constraints: BoxConstraints(maxWidth: 375, maxHeight: 50),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Container(
            child: AutoSizeText(
              widget.inputParameter,
              textAlign: TextAlign.center,
              style: TextStyle(
                fontSize: 20.0,
              ),
            ),
          ),
          Expanded(
            child: Container(
              decoration: BoxDecoration(
                border: Border.all(
                  color: Colors.red,
                  width: 3,
                ),
                borderRadius: BorderRadius.only(
                  topLeft: Radius.circular(10),
                  bottomLeft: Radius.circular(10),
                ),
              ),
              child: TextField(
                controller: widget.textField, //  <-- The Controller
                autofocus: true,
                textAlign: TextAlign.center,
                onChanged: (newText) {
                  newTaskTitle = newText;
                },
              ),
            ),
          ),
          Container(
            decoration: BoxDecoration(
              color: Colors.red,
              border: Border.all(
                color: Colors.red,
                width: 3,
              ),
              borderRadius: BorderRadius.only(
                topRight: Radius.circular(10),
                bottomRight: Radius.circular(10),
              ),
            ),
            child: Row(
              children: <Widget>[
                Container(
                  padding: EdgeInsets.all(5),
                  child: Center(
                      child: AutoSizeText(
                    unit,
                    style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
                  )),
                ),
                Container(
                    constraints: BoxConstraints(maxHeight: 50, maxWidth: 60),
                    child: FlatButton(
                      highlightColor: Colors.transparent,
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.center,
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: <Widget>[
                          Icon(
                            Icons.loop,
                            size: 25,
                          ),
                        ],
                      ),
                      onPressed: () {
                        setState(() {
                          selectedUnit = selectedUnit == Units.unit2
                              ? Units.unit1
                              : Units.unit2;
                          if (selectedUnit == Units.unit1) {
                            unit = widget.unit1;
                          } else {
                            unit = widget.unit2;
                          }
                        });
                      },
                    )),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

在父小部件(調用自定義小部件的屏幕)上,為您想知道的每個 TextField 創建 TextEditingController,它們有一個參數 TextEditingController.text ,它為您提供寫在控制的 Textfield 上的值

class InputScreen extends StatefulWidget {
  static const String id = 'adjustments';
  
  @override
  AdjustmentInputScreenState createState() => AdjustmentInputScreenState();
}

class AdjustmentInputScreenState extends State<InputScreen> {
  final TextEditingController weightController = TextEditingController(); //create one for the height
  final TextEditingController heightController = TextEditingController(); //create one for the width
  

  //don't forget to dispose them
  @override
  void dispose(){
    super.dispose();
    weightController.dispose();
    heightController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: <Widget>[
          AppBar(
            leading: null,
            actions: <Widget>[
              IconButton(
                  icon: Icon(Icons.close),
                  onPressed: () {
                    Navigator.pop(context);
                  }),
            ],
            title: Text('Dose Adjustment'),
            backgroundColor: Colors.transparent,
            elevation: 0.0,
          ),
          InputRow(
            unit1: 'cm',
            unit2: 'inches',
            inputParameter: 'height',
            textField: heightController, // The textcontroller to check the height
          ),
          InputRow(unit1: 'lbs', unit2: 'kg', inputParameter: 'weight',
                   textField: weightController, // The textcontroller to check the weight
                  ),
          FlatButton(
            child: Text('Calculate'),
            onPressed: () {
              //int.tryparse if you want a number, check for null, empty strings or strings that aren't number
              String height = heightController.text;
              String weight = weightController.text;
              print('Height: $height');
              print('Weight: $weight');
              //Do your math here
            },
          ),
        ],
      ),
    );
  }
}

使用 heightController.text 或 weightController.text 您可以在父級中的任何位置看到該值,只要您有 TextEditingController 將其附加到您想要查看的 TextField 小部件

更新

試試看 TextEditingController 是如何工作的,你會看到它擴展了一個 class ValueNotifier,當值改變時重建它的監聽器,你可以像這樣創建你自己的

class MyUnit extends ValueNotifier<Units>{ //You want to check when the enum Units change, so that will be your ValueNotifier
  final String _label1;
  final String _label2;
  
  MyUnit({Units unit = Units.unit1, String label1 = 'cm', String label2 = 'inches'}) : _label1 = label1, _label2 = label2, super(unit);
  
  String get label => value == Units.unit1 ? _label1 : _label2; //The labels you define, just like unit1 and unit2 in InputRow 
  Units get unit => value; //the enum value 
  set unit(Units newUnit) => value = newUnit; //when this change, it will rebuild the listeners
}

現在就像 TextEditingController 你只需要創建它們並處理它們

final MyUnit heightUnit = MyUnit();
final MyUnit weightUnit = MyUnit(label1: 'lbs', label2: 'kg');


//don't forget to dispose them
@override
void dispose(){
  super.dispose();
  weightController.dispose();
  heightController.dispose();
  heightUnit.dispose();
  weightUnit.dispose();
}

...

InputRow(
   myUnit: heightUnit,
   inputParameter: 'height',
   textField: heightController,
),
InputRow(myUnit: weightUnit, inputParameter: 'weight',
   textField: weightController,
),
FlatButton(
   child: Text('Calculate'),
   onPressed: () {        
     //I change the names of the variables to avoid confusion
     String myHeight = heightController.text;
     String myWeight = weightController.text;
     String labelHeight = heightUnit.label;
     String labelWeight = weightUnit.label;
     print('Height: $myHeight $labelHeight');
     print('Weight: $myWeight $labelWeight');
          
     double weight = double.parse(myWeight); //this could throw an error if myWeight cannot be parsed
     if(weightUnit.unit == Units.unit1) weight = weight / 2.2;
     print(weight.toStringAsFixed(1));
     //Do your math here
   },
),

在 InputRow 中,您可以像 TextEditingController 一樣傳遞此 class,現在您不需要提供其他值 unit1、unit2、selectedUnit,因為該邏輯現在位於 class MyUnit

class InputRow extends StatefulWidget {
  InputRow({this.inputParameter, this.textField, this.myUnit});
  final String inputParameter;
  final MyUnit myUnit;
  final TextEditingController textField; //Add this controller and also to the parameters of the constructor

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



class _InputRowState extends State<InputRow> {

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      constraints: BoxConstraints(maxWidth: 375, maxHeight: 50),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Container(
            child: Text(
              widget.inputParameter,
              textAlign: TextAlign.center,
              style: TextStyle(
                fontSize: 20.0,
              ),
            ),
          ),
          Expanded(
            child: Container(
              decoration: BoxDecoration(
                border: Border.all(
                  color: Colors.red,
                  width: 3,
                ),
                borderRadius: BorderRadius.only(
                  topLeft: Radius.circular(10),
                  bottomLeft: Radius.circular(10),
                ),
              ),
              child: TextField(
                controller: widget.textField, //  <-- The Controller
                autofocus: true,
                textAlign: TextAlign.center,
              ),
            ),
          ),
          Container(
            decoration: BoxDecoration(
              color: Colors.red,
              border: Border.all(
                color: Colors.red,
                width: 3,
              ),
              borderRadius: BorderRadius.only(
                topRight: Radius.circular(10),
                bottomRight: Radius.circular(10),
              ),
            ),
            child: Row(
              children: <Widget>[
                Container(
                  padding: EdgeInsets.all(5),
                  child: Center(
                   child: ValueListenableBuilder<Units>( //This work as a listener
                      valueListenable: widget.myUnit, //the object to listen, it needs to extend a ValueNotifier
                      builder: (context, unit, _) =>
                        Text(widget.myUnit.label,style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500))
                /*
                 The builder gives me a value unit, that I can use when the ValueListenableBuilder rebuilds,
                 but that is the Units enum, which you don't want to display, so you ignore it and give widget.myUnit.label to the Text widget, it will rebuild only when Units change, but the label also getter also change with that value, so it's ok
                */
                  )
                  ),
                ),
                Container(
                    constraints: BoxConstraints(maxHeight: 50, maxWidth: 60),
                    child: FlatButton(
                      highlightColor: Colors.transparent,
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.center,
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: <Widget>[
                          Icon(
                            Icons.loop,
                            size: 25,
                          ),
                        ],
                      ),
                      onPressed: () {
                        Units unit = widget.myUnit.unit;
                        widget.myUnit.unit = unit == Units.unit1 ? Units.unit2 : Units.unit1; //this will call the setter in MyUnit and rebuild the listeners
                      },
                    )),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

TriButton 代碼如您所見,我嘗試使用值通知程序,但無法弄清楚如何獲取所選按鈕的標題。 我不知道如何將該信息拉到下一個屏幕。

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'rect_button.dart';
import 'package:pocketpk/constants.dart';

enum Option {
  one,
  two,
  three,
}

class TriButton extends StatefulWidget {
  TriButton(
      {this.title1, this.title2, this.title3, this.triWidth, this.onChanged});

  final String title1;
  final String title2;
  final String title3;
  final Constraints triWidth;
  ValueChanged<Option> onChanged;

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

class _TriButtonState extends State<TriButton> {
  Option selectedOption;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        constraints: widget.triWidth,
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Expanded(
              child: ValueListenableBuilder<Option>(
                valueListenable: widget.onChanged,
                builder: (context, option, _) => RectButton(
                  buttonChild: Text(
                    widget.title1,
                    style: TextStyle(color: Colors.white),
                  ),
                  onPress: () {
                    setState(() {
                      selectedOption = Option.one;
                    });
                  },
                  bgColor: selectedOption == Option.one
                      ? kActiveButtonColor
                      : kInactiveButtonColor,
                ),
              ),
            ),
            Expanded(
              child: ValueListenableBuilder<Option>(
                valueListenable: widget.onChanged,
                builder: (context, option, _) => RectButton(
                  buttonChild: Text(
                    widget.title2,
                    style: TextStyle(color: Colors.white),
                  ),
                  onPress: () {
                    setState(() {
                      selectedOption = Option.two;
                    });
                  },
                  bgColor: selectedOption == Option.two
                      ? kActiveButtonColor
                      : kInactiveButtonColor,
                ),
              ),
            ),
            Expanded(
              child: ValueListenableBuilder<Option>(
                valueListenable: widget.onChanged,
                builder: (context, option, _) => RectButton(
                  buttonChild: Text(
                    widget.title3,
                    style: TextStyle(color: Colors.white),
                  ),
                  onPress: () {
                    setState(() {
                      selectedOption = Option.three;
                    });
                  },
                  bgColor: selectedOption == Option.three
                      ? kActiveButtonColor
                      : kInactiveButtonColor,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

通知者

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

class MyButton extends ValueNotifier<Option> {
  final String _title1;
  final String _title2;
  final String _title3;

  MyButton(
      {Option option = Option.one,
      String title1 = 'A',
      String title2 = 'B',
      String title3 = 'C'})
      : _title1 = title1,
        _title2 = title2,
        _title3 = title3,
        super(option);

  //You can add a get method to retrieve the title based on the option selected with a switch
  String get title {
    switch (value) {
      case Option.one:
        return _title1;
      case Option.two:
        return _title2;
      case Option.three:
        return _title3;
      default:
        return _title1; //or a default String, but to be honest this will never be used
    }
  }

  Option get option => value;
  set option(Option newOption) => value = newOption;
}

更新

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'rect_button.dart';
import 'package:pocketpk/constants.dart';

enum Option {
  one,
  two,
  three,
}

class Parent extends StatelessWidget{
  ValueNotifier<Option> myButton = MyButton();

  @override
  Widget build(BuildContext context){
    return ValueListenableBuilder<Option>(
       valueListenable: myButton,
       builder: (context, button, _) => TriButton(
           title1: button.title1, //take the underscores of the names in the MyButton class to make them public
           title2: button.title2,
           title3: button.title3,
           triWidth: BoxConstraints(), //I don't know this value
           onChanged: (newOption) => button.option = newOption,
         )
     );
  }
}

class TriButton extends StatefulWidget {
  TriButton(
      {this.title1, this.title2, this.title3, this.triWidth, this.onChanged});

  final String title1;
  final String title2;
  final String title3;
  final Constraints triWidth;
  ValueChanged<Option> onChanged;

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

class _TriButtonState extends State<TriButton> {

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        constraints: widget.triWidth,
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Expanded(
              child: RectButton(
                buttonChild: Text(
                  widget.title1,
                  style: TextStyle(color: Colors.white),
                ),
                onPress: () {
                  widget.onChanged(Option.one);
                },
                bgColor: selectedOption == Option.one
                  ? kActiveButtonColor
                  : kInactiveButtonColor,
              ),
            ),
            Expanded(
              child: RectButton(
                buttonChild: Text(
                  widget.title2,
                  style: TextStyle(color: Colors.white),
                ),
                onPress: () {
                  widget.onChanged(Option.two);
                },
                bgColor: selectedOption == Option.two
                  ? kActiveButtonColor
                  : kInactiveButtonColor,
              ),
            ),
            Expanded(
              child: RectButton(
                buttonChild: Text(
                  widget.title3,
                  style: TextStyle(color: Colors.white),
                ),
                onPress: () {
                  widget.onChanged(Option.three);
                },
                bgColor: selectedOption == Option.three
                  ? kActiveButtonColor
                  : kInactiveButtonColor,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

第三輪

你幾乎明白了,問題就在這里

class _TriButtonState extends State<TriButton> {
  Option selectedOption; 
  // this value is not part of the notifier,
  // it's an independent variable

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        constraints: widget.triWidth,
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Expanded(
              child: RectButton(
                buttonChild: Text(
                  widget.title1,
                  style: TextStyle(color: Colors.white),
                ),
                onPress: () {
                  setState(() {
                    selectedOption = Option.one; 
                    //changing this value doesn't notify/change the ValueNotifier
                  });
                },
                bgColor: selectedOption == Option.one
                    ? kActiveButtonColor
                    : kInactiveButtonColor,
              ),
            ),

您擁有的Valuechanged最好更改通知程序

class TriButton extends StatefulWidget {
  TriButton(
      {this.title1, this.title2, this.title3, this.triWidth, this.selected, this.onChanged});

  final String title1;
  final String title2;
  final String title3;
  final BoxConstraints triWidth;
  final Option selected; //instead of passing the class, just pass the option from the class
  final ValueChanged<Option> onChanged; //create this to tell the notifier a value changed

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

現在在 TriButton

class _TriButtonState extends State<TriButton> {
  Option selectedOption; 
  // this value is not part of the notifier,
  // it's an independent variable

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        constraints: widget.triWidth,
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Expanded(
              child: RectButton(
                buttonChild: Text(
                  widget.title1,
                  style: TextStyle(color: Colors.white),
                ),
                onPress: () => widget.onChanged(Option.one), //tell to which value you want to change when this is pressed
                bgColor: widget.selected == Option.one
                    ? kActiveButtonColor
                    : kInactiveButtonColor,
              ),
            ),
            .... //Repeat for the others

並在父級( InputScreen )中更改 ValueNotifier 以添加這些參數

ValueListenableBuilder<Option>(
        valueListenable: selected,
        builder: (context, option, _) => TriButton(
          title1: 'Button 1',
          title2: 'Button 2',
          title3: 'Button 3',
          selected: option, //the value selected
          onChanged: (newOption) => selected.option = newOption //the value to change when one of the buttons is pressed
        ),
      ),

現在它會相應地改變,當你點擊“計算”時,它會打印正確的值


也只是為了幫助您了解一些邏輯,這是在沒有 ValueNotifier 的情況下如何做到的,有一個名為CupertinoSegmentedControl的 Cupertino 小部件(現在不要關注小部件的樣式,只關注邏輯)

它需要一個Map<T, Widget>來制作按鈕,其中每個小部件都是Text('Button 1,2...)和組值來決定哪個 T 到 select,在這種情況下,我只會將 T 設為 int和 select 根據索引。 在 _InputScreenState 中創建一個 Map ,其中包含小部件和一個保存所選值的 int ;

final Map<int,Widget> buttons = {
    1: Text('Button 1'),
    2: Text('Button 2'),
    3: Text('Button 3')
  };
  
  final Map<int,Widget> genereatedButtons = List<Widget>.generate(
    10, (index) => Text('Button $index')).asMap(); //This is the same as the above, just to generate as much as you want, in this case I just genereated 10
  int keySelected; //it holds the value selected, if null nothing is selected, but you could initilialize it at 0

現在在 InputRow 之前創建小部件

CupertinoSegmentedControl<int>(
    children: genereatedButtons, //or the other map buttons
    groupValue: keySelected,
    onValueChanged: (index) => setState(() => keySelected = index), 
),

並在按鈕計算print(keySelected)將為您提供選擇的索引

在此處輸入圖像描述

您應該在 InputRow 小部件中創建一個 function 從字段中獲取數據並返回它們。 然后,當您創建 InputRow 時,給它一個鍵。 最后,當您想從 InputRow 中獲取值時,只需調用 key.yourFunction() 並存儲結果。

class InputRow extends StatefulWidget {
  InputRow({this.inputParameter, this.unit1, this.unit2, Key key}) : super(key: key););
  final String inputParameter;
  final String unit1;
  final String unit2;

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

創建密鑰: GlobalKey<_InputRowState> key = new GlobalKey();

將密鑰傳遞給 InputRow:

InputRow(
  unit1: 'cm',
  unit2: 'inches',
  inputParameter: 'height',
  key: key,
),

從 InputRow 獲取參數: var data = key.yourFunction();

輸入畫面

class InputScreen extends StatefulWidget {
  static const String id = 'Input';
  @override
  _InputScreenState createState() =>
      _InputScreenState();
}

class _InputScreenState
    extends State<InputScreen> {
  final TextEditingController weightController = TextEditingController();
  final TextEditingController heightController = TextEditingController();
  final TextEditingController creatController = TextEditingController();
  final TextEditingController ageController = TextEditingController();
  final MyUnit heightUnit = MyUnit();
  final MyUnit weightUnit = MyUnit(imperial: 'lbs', metric: 'kg');
  final MyUnit creatUnit = MyUnit(imperial: 'mg/dL', metric: 'mg/dL');
  final MyUnit ageUnit = MyUnit(imperial: 'years', metric: 'years');
  final MyButton selected = MyButton(title3: 'Female', title4: 'Male');

  @override
  void dispose() {
    super.dispose();
    weightController.dispose();
    heightController.dispose();
    creatController.dispose();
    heightUnit.dispose();
    weightUnit.dispose();
    ageUnit.dispose();
    selected.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: Column(
        children: <Widget>[
          ClipPath(
            clipper: MyClipper(),
            child: Container(
              height: 250,
              width: double.infinity,
              decoration: BoxDecoration(
                gradient: kHeaderGradient,
                image: DecorationImage(
                  image: AssetImage('images/virus.png'),
                ),
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.center,
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  AppBar(
                    leading: null,
                    actions: <Widget>[
                      IconButton(
                          icon: Icon(Icons.close),
                          onPressed: () {
                            Navigator.pop(context);
                          }),
                    ],
                    title: Text(
                      'Creatinine Clearance',
                      style: kHeaderTextStyle,
                    ),
                    backgroundColor: Colors.transparent,
                    elevation: 0.0,
                  ),
                ],
              ),
            ),
          ),
          Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: <Widget>[
              ValueListenableBuilder<Option>(
                valueListenable: selected,
                builder: (context, option, _) => MakeButtons(
                  num0: 3,
                  num1: 5,
                  makeButtonWidth: MediaQuery.of(context).size.width * 0.45,
                  selected: option,
                  onChanged: (newOption) => selected.option = newOption,
                ),
              ),
              InputRow(
                myUnit: heightUnit,
                inputParameter: 'height',
                textField: heightController,
                colour: kOrangePantone,
              ),
              InputRow(
                myUnit: weightUnit,
                inputParameter: 'weight',
                textField: weightController,
                colour: kRoyalPurple,
              ),
              InputRow(
                myUnit: creatUnit,
                inputParameter: 'SCr',
                textField: creatController,
                colour: kDogwoodRose,
              ),
              InputRow(
                myUnit: ageUnit,
                inputParameter: 'Age',
                textField: ageController,
                colour: kDogwoodRose,
              ),
              RoundedButton(
                title: 'Calculate',
                onPressed: () {
                  String inputHeight = heightController.text;
                  String inputWeight = weightController.text;
                  String inputCreat = creatController.text;
                  String inputAge = ageController.text;

                  double imperialHeight = double.parse(inputHeight) * 2.54;
                  double metricHeight = double.parse(inputHeight);
                  double imperialWeight = double.parse(inputWeight) / 2.2;
                  double metricWeight = double.parse(inputWeight);

                  double creat = double.parse(inputCreat);
                  double age = double.parse(inputAge);


                  double multiplier = selected.title == 'Female' ? 0.85 : 1.0; //- code I am trying to have performed on my calculator model //
                  double height = heightUnit.unitType == 'cm'
                      ? metricHeight
                      : imperialHeight;
                  double weight = weightUnit.unitType == 'cm'
                      ? metricWeight
                      : imperialWeight;


                  double idealWeight = selected.title == 'Female'//- Code I am trying to perform on my calculator model
                      ? (45 +
                          2.3 *
                              (heightUnit.unitType == 'cm'
                                  ? ((double.parse(inputHeight) - 152.4) / 2.54)
                                  : (double.parse(inputHeight) - 60)))
                      : (50 +
                          2.3 *
                              (heightUnit.unitType == 'cm'
                                  ? ((double.parse(inputHeight) - 152.4) / 2.54)
                                  : (double.parse(inputHeight) - 60)));

                  double adjustWeight = (weightUnit.unitType == 'kg'
                      ? (double.parse(inputWeight) - idealWeight) * 0.4 +
                          idealWeight
                      : ((double.parse(inputWeight) / 2.2) - idealWeight) *
                              0.4 +
                          idealWeight);

                  print(weight);
                  print(idealWeight);
                  print(adjustWeight);

                  Calculator calc;
                  calc = Calculator(
                    height: height,
                    weight: weight,
                    creatinine: creat,
                    age: age,

//- right now I can only pass the data this way. If I try to do math in my calculator model, I keep getting the else result of my if statements because no value is passed before the code is run
                    genderMultiplier: multiplier,
                    ideal: idealWeight,
                    adjust: adjustWeight,
                  );

                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => ResultsScreen(
                        Result: calc.calculate(),
                        idealResult: calc.calculateIdeal(),
                        adjustResult: calc.calculateAdjust(),
                      ),
                    ),
                  );
                },
              ),
            ],
          ),
        ],
      ),
    );
  }
}

class MyClipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    var path = Path();
    path.lineTo(0, size.height - 80);
    path.quadraticBezierTo(
        size.width / 2, size.height, size.width, size.height - 80);
    path.lineTo(size.width, 0);
    path.close();
    return path;
  }

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) {
    return false;
  }
}

計算器

class Calculator {
  Calculator({
    height,
    weight,
    creatinine,
    age,
    genderMultiplier,
    ideal,
    adjust,
  });

  double _crcl;
  double _idealCrCL;
  double _adjustCrCL;
  
  
  factory Calculator.idealCalculate({
    double height,
    double weight,
    double creatinine,
    double age,
    bool isFemale = true,
    bool isMetricHeight = true,
    bool isMetricWeight = true,
  }) {
    double myHeight = isMetricHeight ? height : height * 2.54;
    double myWeight = isMetricWeight ? weight : weight / 2.2;
    double imperialHeight = isMetricHeight ? myHeight / 2.54 : height;
    double multiplier;
    double idealWeight;
    double adjustWeight;
    if (isFemale) {
      multiplier = 0.85;
      idealWeight = 45 + 2.3 * (imperialHeight - 60);
    } else {
      multiplier = 1.0;
      idealWeight = 50 + 2.3 * (imperialHeight - 60);
    }
    adjustWeight = (myWeight - idealWeight) * 0.4 + idealWeight;
    return Calculator(
      height: myHeight,
      weight: myWeight,
      creatinine: creatinine,
      age: age,
      genderMultiplier: multiplier,
      ideal: idealWeight,
      adjust: adjustWeight,
      
    );
  }

  String calculate() {
    _crcl = (((140 - age) * weight) / (72 * creatinine)) * genderMultiplier;
    return _crcl.toStringAsFixed(1);
  }

  String calculateIdeal() {
    _idealCrCL = (((140 - age) * ideal) / (72 * creatinine)) * genderMultiplier;
    return _idealCrCL.toStringAsFixed(1);
  }

  String calculateAdjust() {
    _adjustCrCL = weight / ideal >= 1.4
        ? (((140 - age) * adjust) / (72 * creatinine)) * genderMultiplier
        : _idealCrCL;
    return _adjustCrCL.toStringAsFixed(1);
  }
}

結果屏幕

class ResultsScreen extends StatelessWidget {
  static const String id = 'results';
  ResultsScreen({
    @required this.Result,
    this.idealResult,
    this.adjustResult,
  });

  final String Result;
  final String idealResult;
  final String adjustResult;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('BMI CALCULATOR'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          Container(
            padding: EdgeInsets.all(15),
            alignment: Alignment.bottomLeft,
            child: Text(
              'Your Result',
            ),
          ),
          ReuseableCard(
            bgColor: kGreyBackgroundColor,
            cardChild: Column(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: <Widget>[
                Text(
                  Result,
                ),
                Text(idealResult),
                Text(adjustResult),
              ],
            ),
          ),
          RoundedButton(
            title: 'Re-Calc',
            onPressed: () {
              Navigator.pop(context);
            },
          )
        ],
      ),
    );
  }
}

RoundedButton(
    title: 'Calculate',
    onPressed: () {
      String inputHeight = heightController.text;
      String inputWeight = weightController.text;
      String inputCreat = creatController.text;
      String inputAge = ageController.text;

      double creat = double.parse(inputCreat);
      double age = double.parse(inputAge);

      print(weight);
      print(idealWeight);
      print(adjustWeight);


      /// Create a factory constructor to help you do the math before creating the Calculator
      Calculator calc = Calculator.idealCalculate(
        height: double.parse(inputHeight),
        weight: double.parse(inputWeight),
        creatinine: double.parse(inputCreat),
        age: double.parse(inputAge),
        isFemale: selected.title == 'Female',
        isMetricHeight: heightUnit.unitType == 'cm',
        isMetricWeight: weightUnit.unitType == 'cm'
      );

      Navigator.push(
         context,
         MaterialPageRoute(
           builder: (context) => ResultsScreen(
             Result: calc.calculate(),
             idealResult: calc.calculateIdeal(),
             adjustResult: calc.calculateAdjust(),
           ),
         ),
      );
   },
),

然后在你的 Calculate model 中創建一個像這樣的工廠構造函數

factory Calculator.idealCalculate({
      double height,
      double weight,
      double creatinine,
      double age,
      bool isFemale = true,
      bool isMetricHeight = true,
      bool isMetricWeight = true,
    }){
    double myHeight = isMetricHeight ? height : height * 2.54;
    double myWeight = isMetricWeight ? weight : weight / 2.2;
    double imperialHeight = isMetricHeight ? myHeight / 2.54 : height;
    double multiplier;
    double idealWeight;
    double adjustWeight;
    if(isFemale){
      multiplier = 0.85;
      idealWeight = 45 + 2.3 * (imperialHeight - 60);
    }
    else{
      multiplier = 1.0;
      idealWeight = 50 + 2.3 * (imperialHeight - 60);
    }
    adjustWeight = (myWeight - idealWeight) * 0.4 + idealWeight;
    return Calculator(
      height: myHeight,
      weight: myWeight,
      creatinine: creatinine,
      age: age,
      genderMultiplier: multiplier,
      ideal: idealWeight,
      adjust: adjustWeight,
    );
  }

另外,如果您想在您的應用程序(Redux、Provider、Bloc 等)中保留一個計算器 model 的單個實例,並添加設置器或方法來動態更改您想要的值,我建議您檢查不同的 state 管理

例子

import 'package:flutter/material.dart';

class Calculator {
  Calculator({
    this.height,
    this.weight,
    this.creatinine,
    this.age,
    this.genderMultiplier,
    this.ideal,
    this.adjust,
  });

  double height;
  double weight;
  double creatinine;
  double age;
  double genderMultiplier;
  double ideal;
  double adjust;
  String heightUnit;

  double _crcl;
  double _idealCrCL;
  double _adjustCrCL;
  
  factory Calculator.idealCalculate({
      double height,
      double weight,
      double creatinine,
      double age,
      bool isFemale = true,
      bool isMetricHeight = true,
      bool isMetricWeight = true,
    }){
    double myHeight = isMetricHeight ? height : height * 2.54;
    double myWeight = isMetricWeight ? weight : weight / 2.2;
    double imperialHeight = isMetricHeight ? myHeight / 2.54 : height;
    double multiplier;
    double idealWeight;
    double adjustWeight;
    if(isFemale){
      multiplier = 0.85;
      idealWeight = 45 + 2.3 * (imperialHeight - 60);
    }
    else{
      multiplier = 1.0;
      idealWeight = 50 + 2.3 * (imperialHeight - 60);
    }
    adjustWeight = (myWeight - idealWeight) * 0.4 + idealWeight;
    return Calculator(
      height: myHeight,
      weight: myWeight,
      creatinine: creatinine,
      age: age,
      genderMultiplier: multiplier,
      ideal: idealWeight,
      adjust: adjustWeight,
    );
  }
  
  
  set idealWeight(String title) {
    bool isFemale = title == 'Female';
    double imperialHeight = heightUnit == 'cm' ? height / 2.54 : height;
    if(isFemale){
      genderMultiplier = 0.85;
      ideal = 45 + 2.3 * (imperialHeight - 60);
    }
    else{
      genderMultiplier = 1.0;
      ideal = 50 + 2.3 * (imperialHeight - 60);
    }
    adjust = (weight - ideal) * 0.4 + ideal;
  }


  String calculate() {
    _crcl = (((140 - age) * weight) / (72 * creatinine)) * genderMultiplier;
    return _crcl.toStringAsFixed(1);
  }

  String calculateIdeal() {
    _idealCrCL = (((140 - age) * ideal) / (72 * creatinine)) * genderMultiplier;
    return _idealCrCL.toStringAsFixed(1);
  }

  String calculateAdjust() {
    _adjustCrCL = weight / ideal >= 1.4
        ? (((140 - age) * adjust) / (72 * creatinine)) * genderMultiplier
        : _idealCrCL;
    return _adjustCrCL.toStringAsFixed(1);
  }
}

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: MyWidget(),
        ),
      ),
    );
  }
}

class MyWidget extends StatelessWidget {
  
  final Calculator calc = Calculator.idealCalculate(
    age: 24,
    creatinine: 152,
    height: 162,
    weight: 64
  );
  
  
  @override
  Widget build(BuildContext context) {
    return Text(calc.calculate(), style: Theme.of(context).textTheme.headline4);
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM