[英]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)。
目前,除了打印的內容外,一切正常。 我只能獲取有關 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.