首页 > 解决方案 > 输入长度和按钮点击问题

问题描述

我正在为我女儿开发一个简单的数学应用程序。以下是一些您可以根据需要使用的代码(它可能不是最漂亮的代码,但它工作正常,也许它可以帮助某人)。

我的问题是:

1) 限制用户可以输入的字符(数字)的数量。我只在涉及 textFields(例如 maxLength)时找到解决方案。

2)我的刷新按钮(“NEXT”)根本不起作用。这个想法是从之前的算术选择中给用户一个新的随机数学任务。

3)目前您可以输入错误的答案,单击确定,然后更正您的答案以获得“正确”。计划是,一旦您单击确定,您将无法更改您的答案。您将只能单击下一步。(我计划稍后实现一个计数器,这将在 x 个任务后返回正确和错误答案的数量)。

非常感谢任何帮助(代码或我将要查看的内容)。谢谢你。

import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
      .then((_) => runApp(HomePage()));
}

class HomePage extends StatelessWidget {
   @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'input',
      theme: ThemeData(primarySwatch: Colors.purple),
      home: FirstClass(),
    );
  }
}

 class FirstClass extends StatefulWidget {
  @override
  _FirstClassState createState() => _FirstClassState();
}

class _FirstClassState extends State<FirstClass> {
  final random = Random();
  int a, b, c, sum;
  String output;

  void changeData(String buttonName) {
    setState(() {
      a = random.nextInt(10);
      b = random.nextInt(10);

      if (buttonName == '+') {
         sum = a + b;
        output = '$a+$b= ';
      } else if (buttonName == '-') {
        if (a >= b) {
          sum = a - b;
          output = '$a-$b= ';
        } else if (b > a) {
          sum = b - a;
          output = '$b-$a= ';
        }
      }

      print(sum.toString());
      Navigator.of(context).popUntil(ModalRoute.withName('/'));
      Navigator.of(context).push(MaterialPageRoute(
          builder: (context) => SecondClass(
                sum: sum,
                refresh: changeData,
                output: output,
                buttonName: buttonName,
              )));
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(color: Colors.deepPurple),
      child: Scaffold(
        backgroundColor: Colors.transparent,
        appBar: AppBar(
          centerTitle: true,
          backgroundColor: Colors.transparent,
          elevation: 0.0,
          leading: IconButton(
            icon: Icon(Icons.menu),
            onPressed: () {},
          ),
          title: Text(
            'MATH',
            style: TextStyle(fontSize: 25.0),
          ),
        ),
        body: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              RaisedButton(
                  child: Text('+', style: TextStyle(fontSize: 24.0)),
                  onPressed: () => changeData('+')),
              RaisedButton(
                   child: Text('-', style: TextStyle(fontSize: 24.0)),
                  onPressed: () => changeData('-')),
            ],
          ),
        ),
      ),
    );
  }
}

class SecondClass extends StatefulWidget {
  final int sum;
  final String output;
  final String buttonName;
  final Function refresh;

  SecondClass({this.sum, this.refresh, this.buttonName, this.output});

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

class _SecondClassState extends State<SecondClass> {
  String output = "";
  String _output = "";
  String output2 = "";

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

  buttonPressed(String buttonText) {
    if (buttonText == "<-") {
      _output = "";
    } else if (buttonText == "OK") {
      if (output.isNotEmpty) {
        if (output == widget.sum.toString()) {
          setState(() {
            output2 = 'Correct';
          });
        } else {
          setState(() {
            output2 = 'False';
          });
        }
      } else if (buttonText == "NEXT") {
        widget.refresh(widget.buttonName);
      }
    } else {
      _output = _output + buttonText;
    }
    setState(() {
      output = _output;
    });
    print(buttonText);
  }

  Widget buildButton(String buttonText) {
    return Expanded(
      child: Padding(
        padding: const EdgeInsets.only(left: 8.0, right: 8.0, bottom: 8.0),
        child: OutlineButton(
            color: Colors.white,
            child: Text(
              buttonText,
              style: TextStyle(
                  color: Colors.white,
                  fontSize: 20.0,
                  fontWeight: FontWeight.bold),
            ),
            onPressed: () => buttonPressed(buttonText)),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(color: Colors.purple),
      child: Column(
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.only(top: 40.0, left: 20.0, right: 
20.0),
            child: Container(
                height: 60.0,
                width: double.infinity,
                decoration: BoxDecoration(
                    color: Colors.white,
                    borderRadius: BorderRadius.circular(15.0)),
                child: Center(
                  child: Text(
                    widget.output + output,
                    style: TextStyle(
                        color: Colors.black,
                        fontSize: 48.0,
                        fontWeight: FontWeight.bold),
                  ),
                )),
          ),
          Padding(
            padding: const EdgeInsets.only(top: 20.0, left: 20.0, right: 
20.0),
            child: Container(
               height: 60.0,
              width: double.infinity,
              decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(15.0)),
              child: Text(
                output2,
                style: TextStyle(
                    color: Colors.black,
                    fontSize: 48.0,
                    fontWeight: FontWeight.bold),
              ),
            ),
          ),
          Expanded(child: Divider()),
           Column(
            children: <Widget>[
              Row(
                children: <Widget>[
                  buildButton('1'),
                  buildButton('2'),
                  buildButton('3'),
                ],
              ),
              Row(
                children: <Widget>[
                  buildButton('4'),
                  buildButton('5'),
                  buildButton('6'),
                ],
              ),
              Row(
                children: <Widget>[
                  buildButton('7'),
                  buildButton('8'),
                  buildButton('9'),
                ],
              ),
              Row(
                children: <Widget>[
                  buildButton('<-'),
                  buildButton('0'),
                  buildButton('OK'),
                ],
              ),
              Row(
                children: <Widget>[
                  buildButton('NEXT'),
                ],
              ),
            ],
           ),
        ],
       ),
    );
  }
}

标签: functionvalidationbuttoninputflutter

解决方案


嗯,它的工作。这不是最好的方法,但我尝试保留您的旧代码库。我真的建议使用 BLoC 之类的模式来管理小部件的状态并避免 setState 调用。

需要将您的代码拆分为更多类。我在这里做了什么??

  • 创建一个名为MathOperation. 此枚举用于标识用户选择了哪种算术运算。

  • 旧的头等舱现在是OperationSelectorScreen

  • 旧的 SecondClass 现在是QuestionAndAnswerScreen

  • 我创建了CustomTextField一个类来实现一个具有最大字符数的简单“文本字段”。

  • 我已经实现了MathOperationTask类。此类是算术运算的抽象,例如求和和减法。该类保存运算所涉及的数字、符号“+”或“-”以及运算结果。

源代码中有一些注释可以指导您,如果您需要随时要求做的事情,我会尽可能回答。我希望它有所帮助。

import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

/// enum used to identify math operation types
enum MathOperation { SUM, SUBTRACTION }

void main() {
  SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
      .then((_) => runApp(HomePage()));
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'input',
      theme: ThemeData(primarySwatch: Colors.purple),
      home: OperationSelectorScreen(),
    );
  }
}

/// old FirstClass
class OperationSelectorScreen extends StatefulWidget {
  @override
  _OperationSelectorScreenState createState() => _OperationSelectorScreenState();
}

class _OperationSelectorScreenState extends State<OperationSelectorScreen> {

  MathOperation _userSelectedOperation;

  void changeData(String buttonName) {
    setState(() {
      if (buttonName == '+') {
        _userSelectedOperation = MathOperation.SUM;
      } else if (buttonName == '-') {
        _userSelectedOperation = MathOperation.SUBTRACTION;
      }

      Navigator.of(context).popUntil(ModalRoute.withName('/'));

      /// we only need pass to next screen what kind of operation
      /// was selected by user
      Navigator.of(context).push(MaterialPageRoute(
          builder: (context) => QuestionAndAnswerScreen(
            operationType: _userSelectedOperation,// operation selected by user  ( '+' or '-' )
          )));
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(color: Colors.deepPurple),
      child: Scaffold(
        backgroundColor: Colors.transparent,
        appBar: AppBar(
          centerTitle: true,
          backgroundColor: Colors.transparent,
          elevation: 0.0,
          leading: IconButton(
            icon: Icon(Icons.menu),
            onPressed: () {},
          ),
          title: Text(
            'MATH',
            style: TextStyle(fontSize: 25.0),
          ),
        ),
        body: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[

              RaisedButton(
                  child: Text('+', style: TextStyle(fontSize: 24.0)),
                  onPressed: () => changeData('+')),

              RaisedButton(
                  child: Text('-', style: TextStyle(fontSize: 24.0)),
                  onPressed: () => changeData('-')),
            ],
          ),
        ),
      ),
    );
  }
}

/// old SecondClass
class QuestionAndAnswerScreen extends StatefulWidget {
  final operationType; // if the operations will be (+) or (-)

  QuestionAndAnswerScreen({this.operationType});

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

class _QuestionAndAnswerScreenState extends State<QuestionAndAnswerScreen> {

  String _userTypedAnswer=""; // numbers that user had typed
  String _answerValidationOutput = ""; // will say if the user answer ir correct or wrong(false)
  MathOperationTask _currentTask; // current arithmetic operation

  // this member controls the back button "<-" activity
  // if user had responded the question, this value will be true and
  // the button "<-" will not work properly.
  bool _isQuestionResponded = false;

  @override
  void initState() {
    super.initState();
    //using math operation task generator method to create a new math operation
    _currentTask = MathOperationTask.generateMathTask( widget.operationType );
  }

  buttonPressed(String buttonText) {
    // this logic can be improved if we
    // transform the custom keyboard in a widget

    if (buttonText == "<-") {
      if (!_isQuestionResponded){
        _changeUserTypedText("");
        _changeAnswerValidationText("");
      }
    }

    else if (buttonText == "OK") {
      if (_userTypedAnswer.isNotEmpty) {
        _isQuestionResponded = true;
        if (_userTypedAnswer == _currentTask.results.toString()) {
          _changeAnswerValidationText('Correct');
        }

        else {
          _changeAnswerValidationText('False');
        }
      }
    }

    else if (buttonText == "NEXT") {
      print("new OP");
      _spawnArithmeticOperation();
    }

    else {
      ///This if statement solves the problem of put in member after question
     ///responded.If question is NOT responded, OK button not pressed then we update the text. 
     if (!_isQuestionResponded)
        _changeUserTypedText( (_userTypedAnswer + buttonText) );
    }

  }

  /// this mehtod creates a new arithmetic operation and update the screen with
  void _spawnArithmeticOperation(){
    _currentTask = MathOperationTask.generateMathTask(widget.operationType);
    _answerValidationOutput ="";
    _userTypedAnswer = "";
    _isQuestionResponded = false;
    setState(() {});
  }

  /// method to change and update UI after user type something.
  void _changeUserTypedText(String text){
    setState(() => _userTypedAnswer = text );
  }

  /// update the text if the answer is correct, wrong or clean the text.
  void _changeAnswerValidationText(String text){
    setState(() => _answerValidationOutput = text );
  }

  Widget buildButton(String buttonText) {
    return Expanded(
      child: Padding(
        padding: const EdgeInsets.only(left: 8.0, right: 8.0, bottom: 8.0),
        child: OutlineButton(
            color: Colors.white,
            child: Text(
              buttonText,
              style: TextStyle(
                  color: Colors.white,
                  fontSize: 20.0,
                  fontWeight: FontWeight.bold),
            ),
            onPressed: () => buttonPressed(buttonText)),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {

    final operationField = CustomTextField( maxLength: 7, // max text length
      text: "${_currentTask.firstMember}" // first member of operation
          "${_currentTask.operationSymbol}" // operation signal
          "${_currentTask.secondMember}= " // second member of math operation
          "$_userTypedAnswer",
    );

    final answerFinalResultsField = CustomTextField(
        maxLength: 7,
        text: _answerValidationOutput
    );

    return Container(
      decoration: BoxDecoration(color: Colors.purple),
      child: Column(
        children: <Widget>[
          operationField,
          answerFinalResultsField,
          Expanded(child: Divider()),
          _buildKeyboard(),
        ],
      ),
    );
  }

  // here i put your keyboard layout..
  Widget _buildKeyboard(){
    return Column(
      children: <Widget>[
        Row(
          children: <Widget>[
            buildButton('1'),
            buildButton('2'),
            buildButton('3'),
          ],
        ),
        Row(
          children: <Widget>[
            buildButton('4'),
            buildButton('5'),
            buildButton('6'),
          ],
        ),
        Row(
          children: <Widget>[
            buildButton('7'),
            buildButton('8'),
            buildButton('9'),
          ],
        ),
        Row(
          children: <Widget>[
            buildButton('<-'),
            buildButton('0'),
            buildButton('OK'),
          ],
        ),
        Row(
          children: <Widget>[
            buildButton('NEXT'),
          ],
        ),
      ],
    );
  }
}

/// this class represents an arithmetic operation
/// example 3 + 6 = 9
/// 3 is the firstMember, 6 the secondMember and results is 9.
class MathOperationTask {
  final firstMember;
  final secondMember;
  final results; //operation results
  final operationSymbol;

  // text math symbols constants only to show...
  static final String PLUS = "+";
  static final String LESS = "-";

  MathOperationTask( {this.firstMember, this.secondMember, this.results, this.operationSymbol} );

  /// this method is used to generate a specific math task from a specific type.
  static MathOperationTask generateMathTask( MathOperation type ){
    var random = Random();
    var firstMember = random.nextInt(10);// 0..9
    var secondMember = random.nextInt(10);

    switch(type){
      case MathOperation.SUM:
        return MathOperationTask(
          firstMember: firstMember,
          secondMember: secondMember,
          results: (firstMember + secondMember),
          operationSymbol: PLUS
        );

      case MathOperation.SUBTRACTION:
        var results;

        if (firstMember < secondMember) {
          // we exchange the values position in operation...
          var temp = firstMember;
          firstMember = secondMember;
          secondMember = temp;
        }

        results = firstMember - secondMember;

        return MathOperationTask(
          results: results,
          secondMember: secondMember,
          firstMember: firstMember,
          operationSymbol: LESS,
        );

      default:
        break;
    }

    //in case of invalid operation...
    return MathOperationTask(
      firstMember: 0,
      secondMember: 0,
      results: 0,
    );
  }
}

/// A simple custom text field that limits his text
/// with a specific characters number
///
class CustomTextField extends StatelessWidget {
  final maxLength;
  final _text;

  CustomTextField({this.maxLength = 7, String text}) :
      _text = (text.length > maxLength) ? text.substring(0, maxLength ) : text;
      /// this lines solves characters numbers problem
      ///if the text contains more characters that is allowed (maxLength) then we
      /// cut the string form character 0 until last position allowed (maxLength).

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(top: 40.0, left: 20.0, right: 20.0),
      child: Container(
        height: 60.0,
        width: double.infinity,
        decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.circular(15.0)),
        child: Center(
          child: Text(
            _text,
            style: TextStyle(
                color: Colors.black,
                fontSize: 48.0,
                fontWeight: FontWeight.bold),
          ),
        ),
      ),
    );

  }
}

推荐阅读