Future.delayed 之后设置的表单在变量后续状态更改后不会重建

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

我有一个场景,仅在某个

Form
完成后才需要显示
Future
(实际上,这是在动画完成之后,但为了可重复性,我简化了下面代码片段中的内容)。

然后,当填写并提交表单并且表单中提供的答案正确时,我需要更改表单字体的颜色(除其他事项外,但我正在简化)。

我尝试通过更改 setState 中变量

_isAnswerCorrect
的值来做到这一点(更改表单字体颜色),该值被传递到无状态小部件
NumberInputField
,它是
TextFormField
的包装器。但是,
NumberInputField
不会重建。

我还尝试将

key
UniqueKey()
ValueKey(_isAnswerCorrect.toString())
)设置为
NumberInputField
,因为这个解决方案在另一个类似的场景中对我有用,但两者都不起作用。

只需将下面的代码复制并粘贴到 dartpad 中即可查看实际行为(此处不起作用):

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({
    super.key,
  });

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool _isAnswerCorrect = false;

  Widget _questionForm = Container();

  final _formKey = GlobalKey<FormState>();

  void _answerIsCorrect() {
    setState(() {
      _isAnswerCorrect = true;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              child: Text('Show form'),
              onPressed: () {
                Future.delayed(
                  const Duration(milliseconds: 100),
                  () => setState(
                    () {
                      _questionForm = SizedBox(
                        width: 100,
                        child: Column(
                          children: [
                            Form(
                              key: _formKey,
                              child: NumberInputField(
                                isAnswerCorrect: _isAnswerCorrect,
                              ),
                            ),
                            SizedBox(height: 20),
                            ElevatedButton(
                              child: Text('Change Color?'),
                              onPressed: _answerIsCorrect,
                            ),
                          ],
                        ),
                      );
                    },
                  ),
                );
              },
            ),
            SizedBox(height: 20),
            _questionForm,
          ],
        ),
      ),
    );
  }
}

class NumberInputField extends StatelessWidget {
  final bool isAnswerCorrect;

  const NumberInputField({
    super.key,
    required this.isAnswerCorrect,
  });

  @override
  Widget build(BuildContext context) {
    Color fontColor = Colors.blue;

    if (isAnswerCorrect) {
      fontColor = Colors.black;
    }

    return TextFormField(
      style: TextStyle(color: fontColor),
      textAlign: TextAlign.center,
      decoration: InputDecoration(
        hintText: '?',
      ),
    );
  }
}


但是,我已经成功地以另一种形式做到了这一点,这种形式不是仅在 Future 之后设置的(这里它有效)(只需将下面的代码复制并粘贴到 dartpad 中即可查看行为):

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({
    super.key,
  });

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool _isAnswerCorrect = false;

  final _formKey = GlobalKey<FormState>();

  void _answerIsCorrect() {
    setState(() {
      print('setting state');
      _isAnswerCorrect = true;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            SizedBox(
              width: 100,
              child: Column(
                children: [
                  Form(
                    key: _formKey,
                    child: NumberInputField(
                      isAnswerCorrect: _isAnswerCorrect,
                    ),
                  ),
                  SizedBox(height: 20),
                  ElevatedButton(
                    child: Text('Change Color?'),
                    onPressed: _answerIsCorrect,
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class NumberInputField extends StatelessWidget {
  final bool isAnswerCorrect;

  const NumberInputField({
    super.key,
    required this.isAnswerCorrect,
  });

  @override
  Widget build(BuildContext context) {
    Color fontColor = Colors.blue;

    if (isAnswerCorrect) {
      fontColor = Colors.black;
    }

    return TextFormField(
      style: TextStyle(color: fontColor),
      textAlign: TextAlign.center,
      decoration: InputDecoration(
        hintText: '?',
      ),
    );
  }
}


我一定在这里遗漏了一些非常微不足道的东西。你知道为什么会这样吗?

flutter dart
1个回答
0
投票

这是因为您的 form 与当前页面上下文处于不同的上下文中,因此

setState
没有重建
NumberInputField
无状态小部件。在代码中,您在
_questionForm
函数内更改
onPressed
小部件,在这种情况下,填充到
_questionForm
的小部件不处于当前上下文状态。 如果我错了请纠正我,对我来说解释它比实现它更难。

我修改了你的代码以使其正常工作。

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({
    super.key,
  });

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool _isAnswerCorrect = false;

  /// [_isFormShowed] Manage show/hide form, if it's true it will show form,
  /// otherwise the form will not shown.
  bool _isFormShowed = false;

  /* Removed [_questionForm] */
  // Widget _questionForm = Container();

  final _formKey = GlobalKey<FormState>();

  void _answerIsCorrect() {
    setState(() {
      _isAnswerCorrect = true;
    });
  }

  Widget _buildForm() {
    return SizedBox(
      width: 100,
      child: Column(
        children: [
          Form(
            key: _formKey,
            child: NumberInputField(
              isAnswerCorrect: _isAnswerCorrect,
            ),
          ),
          SizedBox(height: 20),
          ElevatedButton(
            child: Text('Change Color?'),
            onPressed: _answerIsCorrect,
          ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    // Create [form] variable for manage form widget.
    Widget form = Container();

    // if [_isFormShowed] is true then fill [form] with [_buildForm()]
    if (_isFormShowed) form = _buildForm();

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              child: Text('Show form'),
              onPressed: () {
                Future.delayed(
                  const Duration(milliseconds: 100),
                  // Change [_isFormShowed] value
                  () => setState(() => _isFormShowed = !_isFormShowed),
                );
                print('show');
              },
            ),
            SizedBox(height: 20),

            // Change [_questionForm] widget to [form].
            form,
          ],
        ),
      ),
    );
  }
}

class NumberInputField extends StatelessWidget {
  final bool isAnswerCorrect;

  const NumberInputField({
    super.key,
    required this.isAnswerCorrect,
  });

  @override
  Widget build(BuildContext context) {
    Color fontColor = Colors.blue;

    if (isAnswerCorrect) {
      fontColor = Colors.black;
    }

    return TextFormField(
      style: TextStyle(color: fontColor),
      textAlign: TextAlign.center,
      decoration: InputDecoration(
        hintText: '?',
      ),
    );
  }
}

变化

  1. 添加
    _isFormShowed
    用于管理显示/隐藏表单
  2. 删除
    _questionForm
    变量
  3. 添加
    _buildForm()
    返回表单小部件
  4. 添加局部变量
    form
    ,用于根据
    _isFormShowed
    值确定将显示哪个小部件。
  5. 更改“更改颜色”按钮
    onPressed
    功能,用于更改
    _isFormShowed
© www.soinside.com 2019 - 2024. All rights reserved.