我想在按下按钮时从AlcoholTimerForm stf widget(=child) 到AlcoholTimerPage stf widget(=parent) 调用名为“calAlcohol()”的方法。
所以我使用了全局密钥,但我得到了空。
我应该将按钮小部件移动到子小部件以实现“按下按钮时执行 calAlcohol() ”吗?或者有什么办法可以解决这个空问题?
下面是代码。抱歉我的英语不好。
alcohol_timer_page.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:ice_d/theme.dart';
import 'package:ice_d/ui/widgets/alcohol_timer_timer.dart';
import 'package:ice_d/ui/widgets/alcohol_timer_form.dart';
import 'package:ice_d/app_state.dart';
import 'package:ice_d/timer_provider.dart';
class AlcoholTimerPage extends StatefulWidget {
const AlcoholTimerPage({super.key});
@override
State<AlcoholTimerPage> createState() => _AlcoholTimerPageState();
}
class _AlcoholTimerPageState extends State<AlcoholTimerPage> {
final formKey = GlobalKey<FormState>();
final GlobalKey<AlcoholTimerFormState> alcoholTimerFormKey = GlobalKey<AlcoholTimerFormState>();
bool isBtnActivated = false;
double? alcohol = 0;
@override
Widget build(BuildContext context) {
final deviceWidth = MediaQuery.of(context).size.width;
final timerProvider = Provider.of<TimerProvider>(context);
return Scaffold(
backgroundColor: AppColors.white,
body: Center(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
const SizedBox(height: 20,),
SizedBox(
width: deviceWidth*0.8,
height: deviceWidth*0.8,
child: Stack(alignment: Alignment.center, children: [
Consumer<TimerProvider>(
builder: (context, timerProvider, child) {
final leftHour = timerProvider.leftHour;
final leftMin = timerProvider.leftMin;
final totalHour = timerProvider.hour;
final totalMin = timerProvider.min;
return AlcoholTimerWidget(
totalTime: (totalMin! == 0 && totalHour! == 0)
? 1
: 60 * totalHour! + totalMin,
leftTime: (totalMin == 0 && totalHour == 0)
? 1
: 60 * leftHour + leftMin, // default = 0
);
},
),
// Consumer<AppState>(
// builder: (context, appState, child) {
// return appState.isTimerRunning
// ? TimerTime(size, timerProvider)
// : TimerText(size);
// },
// )
])),
const SizedBox(height: 38,),
Form(
key: formKey,
child: AlcoholTimerForm(isBtnActivated: isBtnActivated),
onChanged: (){
setState(() {
isBtnActivated = formKey.currentState!.validate();
});
},
),
const SizedBox(height: 38,),
ElevatedButton(
onPressed: () async {
if (isBtnActivated){
print('???');
formKey.currentState!.save();
print('~~~~~~~~~~~~~~~~~~');
alcohol = alcoholTimerFormKey.currentState?.calAlcohol();
print('---------------------');
print(alcohol); //output; null
final timerProvider = Provider.of<TimerProvider>(context, listen: false);
final appState = Provider.of<AppState>(context, listen: false);
int leftHour = (alcohol! / 0.015).floor();
int leftMin = (((alcohol! / 0.015) - leftHour) * 60).floor();
timerProvider.setTimer(leftHour, leftMin);
timerProvider.startTimer(appState);
} else {
}
},
child: TextRegular(string: '타이머 시작', size: 20,),
style: ButtonStyle(
backgroundColor: WidgetStateProperty.resolveWith((state){
if (isBtnActivated) {
return AppColors.mainColor;
}
else{
return AppColors.grey;
}
},
),)
)
],
),
),
),
);
}
}
alcohol_timer_form.dart
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:ice_d/theme.dart';
import 'alcohol_timer_form_field.dart';
class AlcoholTimerForm extends StatefulWidget {
bool isBtnActivated = false;
AlcoholTimerForm({super.key, required isBtnActivated});
@override
State<AlcoholTimerForm> createState() => AlcoholTimerFormState();
}
class AlcoholTimerFormState extends State<AlcoholTimerForm> {
String _sex = '남성';
double _sexWeight = 0;
double _weight = 0;
String _drink = '소주';
double _alcohol = 0;
double _drinkAmount = 0;
String _time = '90분 내';
double _timeNum = 0;
final List<String> _drinks = ['소주', '맥주', '와인', '위스키'];
final List<String> _times = ['90분 내', '2시간 내','3시간 내','4시간 내','5시간 내','6시간 내','7시간 내','8시간 내','9시간 내','10시간 내','11시간 내','12시간 내'];
//final List<String> _defaultAlcohol = [];
//todo _drinks 선택에 따라 _defaultAlcohol이 initial value에 보이게
final List<String> _labels = ['성별', '몸무게', '주류 종류', '마신 양', '음주한 시간'];
final List<String> _errorMsgs = ['','','','',''];
double calAlcohol(){
if(_sex == '남성'){
_sexWeight = 0.86;
} else{
_sexWeight = 0.64;
}
if(_time == '90분 내'){
_timeNum = 1.5;
}else{
_timeNum = double.parse(_time.replaceAll('시간 내', ''));
}
double bac = ((_drinkAmount*_alcohol*0.7894*0.7)/(_weight*_sexWeight)-(0.015*_timeNum));
return double.parse(bac.toStringAsFixed(3));
}
@override
Widget build(BuildContext context) {
final deviceWidth = MediaQuery.of(context).size.width;
final List<Widget> renderForm = [
FormFieldToggle(sex: _sex, onErrorChanged: (String msg){setState((){_errorMsgs[0] = msg;});}),
Row(children: [
FormFieldText(value: _weight, label: '몸무게', onErrorChanged: (String msg){setState((){_errorMsgs[1] = msg;});}),
const SizedBox(width: 4.0,),
const TextMedium(string: 'kg', size: 16)],
),
Row(
children: [
FormFieldDropdown(
options: _drinks,
value: _drink,
onChanged: (String? newValue){
setState((){
_drink = newValue!;
})
;}),
SizedBox(width: deviceWidth*0.03,),
FormFieldText(value: _alcohol, label: '알코올 도수', onErrorChanged: (String msg){setState((){_errorMsgs[2] = msg;});}),
const SizedBox(width: 4.0,),
const TextMedium(string: '%', size: 16,)
],
),
Row(
children: [
FormFieldText(value: _drinkAmount, label: '마신 양', onErrorChanged: (String msg){setState((){_errorMsgs[3] = msg;});}),
SizedBox(width: deviceWidth*0.03,),
const TextMedium(string: 'ml', size: 16,)
],
),
FormFieldDropdown
(options: _times,
value: _time,
width: deviceWidth*0.5,
onChanged: (String? newValue){
setState((){
_time = newValue!;
});
}
)
];
return Column(
children: [
for(int i=0; i<5; i++)
SizedBox(
height: 70,
child: Stack(
clipBehavior: Clip.none,
children: [
Positioned(left: deviceWidth*0.1, child: TextMedium(string: _labels[i], size: 20,)),
Positioned(left: deviceWidth*0.4, child: renderForm[i]),
Positioned(left: deviceWidth*0.4, top: 46,
child: TextRegular(
string: _errorMsgs[i],
size: 12,
color: Colors.red,
))
],
),
),
],
);
}
}
GlobalKey 方法应该可行,但似乎您可能没有使用正确的 GlobalKey 实例初始化子小部件。
以下是一些确保 GlobalKey 正确使用的步骤和调整:
使用 GlobalKey 初始化子部件:
确保子小部件 (AlcoholTimerForm) 是使用父小部件 (AlcoholTimerPage) 中定义的 GlobalKey 实例创建的。 确保子部件可通过 GlobalKey 访问:
按下按钮时,使用 GlobalKey 调用子部件中的 calAlcohol 方法。