(flutter)从子部件调用方法但得到 null

问题描述 投票:0回答:1

我想在按下按钮时从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,
                    ))
              ],
            ),
          ),

      ],
    );
  }
}
flutter dart null form-fields global-key
1个回答
0
投票

GlobalKey 方法应该可行,但似乎您可能没有使用正确的 GlobalKey 实例初始化子小部件。

以下是一些确保 GlobalKey 正确使用的步骤和调整:

使用 GlobalKey 初始化子部件:

确保子小部件 (AlcoholTimerForm) 是使用父小部件 (AlcoholTimerPage) 中定义的 GlobalKey 实例创建的。 确保子部件可通过 GlobalKey 访问:

按下按钮时,使用 GlobalKey 调用子部件中的 calAlcohol 方法。

© www.soinside.com 2019 - 2024. All rights reserved.